Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- แปลงมาจาก OX.py ในหนังสือ Artificial Intelligence
- with Machine Learning ของ รศ.ดร.ปริญญา สงวนสัตย์
- ]]
- --version 2 ปรับปรุงใหม่ให้สั้นขึ้นนิดนึง แก้ส่วน ai
- --[[
- เปลี่ยน table O กับ X ให้เก็บ boolean แทนเลขช่อง
- แล้วเรียกใช้ index แทนเพื่อความสะดวกการใช้งาน
- ลดการใช้ลูปลง
- ]]
- --แก้เพิ่มเติม v1
- local O = {false,false,false,false,false,false,false,false,false}
- local X = {false,false,false,false,false,false,false,false,false}
- local win = {{1,2,3},
- {4,5,6},
- {7,8,9},
- {1,4,7},
- {2,5,8},
- {3,6,9},
- {1,5,9},
- {3,5,7}}
- --[[
- เอาออก
- local map = {"[_]","[_]","[_]",
- "[_]","[_]","[_]",
- "[_]","[_]","[_]"}
- ]]
- local rnd = math.random
- local max = math.max
- --เพิ่ม function การปัดเศษ
- local floor = math.floor
- --ทำเรียกใช้ table function สะดวกขึ้น
- local add = table.insert
- local unpak = table.unpack
- local concat = table.concat
- --แก้ bug ของรุ่น 1 ปรับให้สั้นลงไม่ต้องใช้ลูป
- function all(t1, t2)
- --ให้ t1 เป็น O หรือ X
- return t1[t2[1]] and t1[t2[2]] and t1[t2[3]]
- end
- --แก้ให้ code สั้นลงไม่ต้องใช้ลูป
- function any(t1, t2)
- --ให้ t1 เป็น O หรือ X
- local t = 0
- --(เงื่อนไข and จริง or เท็จ) เหมือน (เงื่อนไข?จริง:เท็จ) ใน C
- t = t + (t1[t2[1]] and 1 or 0)
- t = t + (t1[t2[2]] and 1 or 0)
- t = t + (t1[t2[3]] and 1 or 0)
- return t --คืนค่าเป็นจำนวนของต่าที่เป็นจริงของ t1 ที่มี index เป็นสมาชิกของ t2
- end
- --เอาส่วนของการสุ่มเลือกใน string ออกเพราะไม่ได้ใช้
- function randomChoice(args)
- --if type(args) == "table" then
- return args[rnd(#args)]
- --elseif type(args) == "string" then
- --local p = rnd(#args)
- --return args:sub(p,p)
- --end
- end
- function checkWin(P)
- for _, w in pairs(win) do
- if all(P, w) then
- return true
- end
- end
- return false
- end
- --ลดเหลือลูปเดียวสร้าง map สด
- function displayOX()
- local newMap = {}
- for i = 1, 9, 3 do
- newMap[i] = O[i] and "[O]" or (X[i] and "[X]" or "[_]")
- newMap[i+1] = O[i+1] and "[O]" or (X[i+1] and "[X]" or "[_]")
- newMap[i+2] = O[i+2] and "[O]" or (X[i+2] and "[X]" or "[_]")
- print(newMap[i]..newMap[i+1]..newMap[i+2])
- end
- end
- function ai()
- --เปลี่ยน validMove เป็นแบบเดียวกับ O กับ X แต่เก็บค่าเริ่มต้นเป็น true
- local validMove = {true,true,true,true,true,true,true,true,true}
- for i = 1, 9 do
- validMove[i] = not (O[i] or X[i]) and validMove[i]
- end
- --เพิ่ม v1 หาแถวที่มี X สองตำแหน่งและไม่มี O คืนค่าเป็นตำแหน่งที่ว่างในแถวนั้น
- for _, w in pairs(win) do
- local cX = any(X,w)
- for _, v in pairs(w) do
- if cX == 2 and validMove[v] then
- return v
- end
- end
- end
- --##########
- local V = {-100,-100,-100,-100,-100,-100,-100,-100,-100}
- for i, v in ipairs(validMove) do
- --validMove มีสมาชิก 9 ตัวเลยเพิ่มเงื่อนไขให้ทำเฉพาะช่องที่มีค่าเป็น true
- if v then
- local tempX, criticalMove = {unpak(X)}, {} --ให้ tempX เป็น clone ของ X
- tempX[i] = v --เพิ่มตำแหน่งเดินของ X โดยเปลี่ยน tempX[i] ด้วยค่าใน validMove[i]
- V[i], criticalMove = evalOX(O,tempX) --หา critiMove และคำนวณทางเลือกในตาเดินต่างๆ (V[i])
- if #criticalMove > 0 then --ถ้า O ใกล้ชนะ (มีสองตัวในชุดใดชุดหนึ่งของ table win)
- --แก้ v1 เอาออกไปใช้ส่วนข้างบน
- --ส่วนที่เพิ่มให้ ai ถึง O จะใกล้ชนะแต่เป็นตาของ X ถ้าเลือกแล้วชนะเกมก็ให้ใช้ค่านี้ไม่ต้องไปเลือกใน criticalMove อีก
- --if checkWin(tempX) then
- --return i
- --end
- --จบส่วนที่เพิ่ม
- --##########
- --ส่วนข้างล่างนี้น่าจะเผื่อสำหรับตารางเกิน 3x3 หรือเปล่าเพราะ
- --criticalMove จะมี O อยู่สองตำแหน่งว่างหนึ่งไม่ต้องจับใส่ list
- --แล้วสุ่มเลือกก็ได้มั๊งแต่ช่างเถอะเอาตามตัวอย่าง
- local move = {}
- for _, c in pairs(criticalMove) do
- --เปลี่ยนการตรวจสอบ validMove
- if validMove[c] then --ถ้าตำแหน่งใน criticalMove อยู่ในตาที่สามารถเดินได้ให้เพิ่มตำแหน่งนั้นใน move
- --แก้ v1 เอาออก
- --add(move,c)
- --แก้ v1 เปลี่ยนเป็นคืนค่าตำแหน่งที่ว่างในแถวของ criticalMove เพราะมีตัวเดียว
- return c
- --##########
- end
- end
- --แก้ v1 เอาออก
- --return randomChoice(move)
- --##########
- end
- end
- end
- --นี่เป็นส่วนคำนวณ โดยสุ่มจากตัวเลือกที่ดีที่สุด (ตำแหน่งที่มีค่ามากที่สุดใน V)
- local maxV = max(unpak(V))
- local imaxV = {}
- for i, v in pairs(V) do
- if v == maxV then
- add(imaxV,i)
- end
- end
- return randomChoice(imaxV)
- end
- function evalOX(o,x)
- local SO, SX, criticalMove =calSOX(o,x)
- --คำนวณโอกาสในตำแหน่งเดินนั้น
- --(1 + (จำนวนรวมของ X ในทุกแถวที่ X มีโอกาสชนะ) - (จำนวนรวมของ O ในทุกแถวที่ O มีโอกาสชนะ))
- return (1 + SX - SO), criticalMove
- end
- function calSOX(o,x)
- local SO, SX = 0, 0
- local criticalMove = {}
- for _, w in pairs(win) do
- local cO = any(o,w) --หาจำนวนของ O ที่อยู่ใน w (table ย่อยใน Win)
- local cX = any(x,w) --หาจำนวนของ X ที่อยู่ใน w (table ย่อยใน Win)
- if cX == 0 then --ถ้าไม่มี X ในแถวนั้น (แปลว่ามีโอกาสที่ O จะชนะในแถวนั้น)
- SO = SO + cO --เก็บค่าจำนวนรวมของ O ในแต่ละแถว
- if cO == 2 then --ถ้าจำนวนของ O ในแถวนั้นเป็น 2
- print("critical", "{".. concat(w,",").."}")
- criticalMove = w --แสดงว่าแถวนั้นเป็น critical ที่ X ต้องกันเพื่อไม่ให้ O ชนะ
- end
- end
- if cO == 0 then --ถ้าไม่มี O ในแถวนั้น (แปลว่ามีโอกาสที่ X จะชนะในแถวนั้น)
- SX = SX + cX --เก็บค่าจำนวนรวมของ X ในแต่ละแถว
- end
- end
- return SO, SX, criticalMove
- end
- while true do
- io.write("Choose position [1-9]: ")
- local move = tonumber(io.read())
- print("")
- local validMove = true
- goto checkMove
- ::chooseAgain::
- io.write("Bad move: choose position [1-9]: ")
- move = tonumber(io.read())
- print("")
- ::checkMove::
- if not move then
- goto chooseAgain
- --elseif move > 9 or move < 1 then
- --goto chooseAgain
- --else
- --เพิ่มการปัดเศษทศนิยมกรณีป้อนค่าเป็นเลขทศนิยม
- --move = floor(move)
- --end
- --แก้ v1 ปัดเศษก่อนแล้วค่อยเทียบค่า
- else
- move == floor(move)
- if move > 9 or move < 1 then
- goto chooseAgain
- end
- end
- --##########
- --แก้ให้เทียบค่า O[move] แทน
- --for _, v in pairs(O) do
- --if move == v then
- --if O[move] then
- --goto chooseAgain
- --end
- --end
- --แก้ให้เทียบค่า X[move] แทน
- --for _, v in pairs(X) do
- --if move == v then
- --if X[move] then
- --goto chooseAgain
- --end
- --end
- --แก้ v1 เอาลูปออกแล้วปรับเป็นเงื่อนไขเดียว
- if O[move] or X[move] then
- goto chooseAgain
- end
- --##########
- --เปลี่ยนจาก insert table O เป็นเลือกตำแหน่งที่ O[move] ให้เป็น true แทน
- O[move] = true
- displayOX()
- if checkWin(O) then
- print("O win")
- break
- end
- --เปลี่ยนจากนับจำนวนสมาชิกใน O กับ X เป้นการตรวจ true/false แทนตามการเปลี่ยน O กับ X
- local d = true
- for i = 1, 9 do
- d = (O[i] or X[i]) and d
- end
- if d then
- print("Draw")
- break
- end
- --จบส่วนที่เปลี่ยนกรณีเสมอ
- --เปลี่ยนจาก insert table X เป็นเลือกตำแหน่งที่ X[ai()] ให้เป็น true แทน
- X[ai()] = true
- displayOX()
- if checkWin(X) then
- print("X win")
- break
- end
- --ตัดส่วนนี้ออกเพราะ O เริ่มก่อน O ลงสุดท้ายถึงจะเสมอได้ไม่ต้องตรวจหลังตาเดินของ X
- --if #O + #X == 9 then
- --print("Draw")
- --break
- --end
- end
Add Comment
Please, Sign In to add comment