wwwRong

ox_game2

Dec 6th, 2019
398
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. --[[
  2.   แปลงมาจาก OX.py ในหนังสือ Artificial Intelligence
  3.   with Machine Learning ของ รศ.ดร.ปริญญา สงวนสัตย์
  4. ]]
  5. --version 2 ปรับปรุงใหม่ให้สั้นขึ้นนิดนึง แก้ส่วน ai
  6. --[[
  7.   เปลี่ยน table O กับ X ให้เก็บ boolean แทนเลขช่อง
  8.   แล้วเรียกใช้ index แทนเพื่อความสะดวกการใช้งาน
  9.   ลดการใช้ลูปลง
  10. ]]
  11. --แก้เพิ่มเติม v1
  12. local O = {false,false,false,false,false,false,false,false,false}
  13. local X = {false,false,false,false,false,false,false,false,false}
  14. local win = {{1,2,3},
  15.             {4,5,6},
  16.             {7,8,9},
  17.             {1,4,7},
  18.             {2,5,8},
  19.             {3,6,9},
  20.             {1,5,9},
  21.             {3,5,7}}
  22. --[[
  23. เอาออก
  24. local map = {"[_]","[_]","[_]",
  25.             "[_]","[_]","[_]",
  26.             "[_]","[_]","[_]"}
  27. ]]
  28. local rnd = math.random
  29. local max = math.max
  30.  
  31. --เพิ่ม function การปัดเศษ
  32. local floor = math.floor
  33.  
  34. --ทำเรียกใช้ table function สะดวกขึ้น
  35. local add = table.insert
  36. local unpak = table.unpack
  37. local concat = table.concat
  38.  
  39. --แก้ bug ของรุ่น 1 ปรับให้สั้นลงไม่ต้องใช้ลูป
  40. function all(t1, t2)
  41.   --ให้ t1 เป็น O หรือ X
  42.   return t1[t2[1]] and t1[t2[2]] and t1[t2[3]]
  43. end
  44.  
  45. --แก้ให้ code สั้นลงไม่ต้องใช้ลูป
  46. function any(t1, t2)
  47.   --ให้ t1 เป็น O หรือ X
  48.   local t = 0
  49.   --(เงื่อนไข and จริง or เท็จ) เหมือน (เงื่อนไข?จริง:เท็จ) ใน C
  50.   t = t + (t1[t2[1]] and 1 or 0)
  51.   t = t + (t1[t2[2]] and 1 or 0)
  52.   t = t + (t1[t2[3]] and 1 or 0)
  53.   return t  --คืนค่าเป็นจำนวนของต่าที่เป็นจริงของ t1 ที่มี index เป็นสมาชิกของ t2
  54. end
  55.  
  56. --เอาส่วนของการสุ่มเลือกใน string ออกเพราะไม่ได้ใช้
  57. function randomChoice(args)
  58.   --if type(args) == "table" then
  59.     return args[rnd(#args)]
  60.   --elseif type(args) == "string" then
  61.     --local p = rnd(#args)
  62.     --return args:sub(p,p)
  63.   --end
  64. end
  65.  
  66. function checkWin(P)
  67.   for _, w in pairs(win) do
  68.     if all(P, w) then
  69.       return true
  70.     end
  71.   end
  72.   return false
  73. end
  74.  
  75. --ลดเหลือลูปเดียวสร้าง map สด
  76. function displayOX()
  77.   local newMap = {}
  78.   for i = 1, 9, 3 do
  79.     newMap[i] = O[i] and "[O]" or (X[i] and "[X]" or "[_]")
  80.     newMap[i+1] = O[i+1] and "[O]" or (X[i+1] and "[X]" or "[_]")
  81.     newMap[i+2] = O[i+2] and "[O]" or (X[i+2] and "[X]" or "[_]")
  82.     print(newMap[i]..newMap[i+1]..newMap[i+2])
  83.   end
  84. end
  85.  
  86. function ai()
  87.   --เปลี่ยน validMove เป็นแบบเดียวกับ O กับ X แต่เก็บค่าเริ่มต้นเป็น true
  88.   local validMove = {true,true,true,true,true,true,true,true,true}
  89.   for i = 1, 9 do
  90.     validMove[i] = not (O[i] or X[i]) and validMove[i]
  91.   end
  92. --เพิ่ม v1 หาแถวที่มี X สองตำแหน่งและไม่มี O คืนค่าเป็นตำแหน่งที่ว่างในแถวนั้น
  93.   for _, w in pairs(win) do
  94.     local cX = any(X,w)
  95.     for _, v in pairs(w) do
  96.       if cX == 2 and validMove[v] then
  97.         return v
  98.       end
  99.     end
  100.   end
  101. --##########
  102.   local V = {-100,-100,-100,-100,-100,-100,-100,-100,-100}
  103.   for i, v in ipairs(validMove) do
  104.     --validMove มีสมาชิก 9 ตัวเลยเพิ่มเงื่อนไขให้ทำเฉพาะช่องที่มีค่าเป็น true
  105.     if v then
  106.       local tempX, criticalMove = {unpak(X)}, {}  --ให้ tempX เป็น clone ของ X
  107.       tempX[i] = v                                --เพิ่มตำแหน่งเดินของ X โดยเปลี่ยน tempX[i] ด้วยค่าใน validMove[i]
  108.       V[i], criticalMove = evalOX(O,tempX)        --หา critiMove และคำนวณทางเลือกในตาเดินต่างๆ (V[i])
  109.       if #criticalMove > 0 then                   --ถ้า O ใกล้ชนะ (มีสองตัวในชุดใดชุดหนึ่งของ table win)
  110.         --แก้ v1 เอาออกไปใช้ส่วนข้างบน
  111.         --ส่วนที่เพิ่มให้ ai ถึง O จะใกล้ชนะแต่เป็นตาของ X ถ้าเลือกแล้วชนะเกมก็ให้ใช้ค่านี้ไม่ต้องไปเลือกใน criticalMove อีก
  112.         --if checkWin(tempX) then
  113.           --return i
  114.         --end
  115.         --จบส่วนที่เพิ่ม
  116.         --##########
  117.         --ส่วนข้างล่างนี้น่าจะเผื่อสำหรับตารางเกิน 3x3 หรือเปล่าเพราะ
  118.         --criticalMove จะมี O อยู่สองตำแหน่งว่างหนึ่งไม่ต้องจับใส่ list
  119.         --แล้วสุ่มเลือกก็ได้มั๊งแต่ช่างเถอะเอาตามตัวอย่าง
  120.         local move = {}
  121.         for _, c in pairs(criticalMove) do
  122.           --เปลี่ยนการตรวจสอบ validMove
  123.           if validMove[c] then                    --ถ้าตำแหน่งใน criticalMove อยู่ในตาที่สามารถเดินได้ให้เพิ่มตำแหน่งนั้นใน move
  124.             --แก้ v1 เอาออก
  125.             --add(move,c)
  126.             --แก้ v1 เปลี่ยนเป็นคืนค่าตำแหน่งที่ว่างในแถวของ criticalMove เพราะมีตัวเดียว
  127.             return c
  128.             --##########
  129.           end
  130.         end
  131.         --แก้ v1 เอาออก
  132.         --return randomChoice(move)
  133.         --##########
  134.       end
  135.     end
  136.   end
  137.   --นี่เป็นส่วนคำนวณ โดยสุ่มจากตัวเลือกที่ดีที่สุด (ตำแหน่งที่มีค่ามากที่สุดใน V)
  138.   local maxV = max(unpak(V))
  139.   local imaxV = {}
  140.   for i, v in pairs(V) do
  141.     if v == maxV then
  142.       add(imaxV,i)
  143.     end
  144.   end
  145.   return randomChoice(imaxV)
  146. end
  147.  
  148. function evalOX(o,x)
  149.   local SO, SX, criticalMove =calSOX(o,x)
  150.   --คำนวณโอกาสในตำแหน่งเดินนั้น
  151.   --(1 + (จำนวนรวมของ X ในทุกแถวที่ X มีโอกาสชนะ) - (จำนวนรวมของ O ในทุกแถวที่ O มีโอกาสชนะ))
  152.   return (1 + SX - SO), criticalMove
  153. end
  154.  
  155. function calSOX(o,x)
  156.   local SO, SX = 0, 0
  157.   local criticalMove = {}
  158.   for _, w in pairs(win) do
  159.     local cO = any(o,w)          --หาจำนวนของ O ที่อยู่ใน w (table ย่อยใน Win)
  160.     local cX = any(x,w)          --หาจำนวนของ X ที่อยู่ใน w (table ย่อยใน Win)
  161.     if cX == 0 then              --ถ้าไม่มี X ในแถวนั้น (แปลว่ามีโอกาสที่ O จะชนะในแถวนั้น)
  162.       SO = SO + cO               --เก็บค่าจำนวนรวมของ O ในแต่ละแถว
  163.       if cO == 2 then            --ถ้าจำนวนของ O ในแถวนั้นเป็น 2
  164.         print("critical", "{".. concat(w,",").."}")
  165.         criticalMove = w         --แสดงว่าแถวนั้นเป็น critical ที่ X ต้องกันเพื่อไม่ให้ O ชนะ
  166.       end
  167.     end
  168.     if cO == 0 then              --ถ้าไม่มี O ในแถวนั้น (แปลว่ามีโอกาสที่ X จะชนะในแถวนั้น)
  169.       SX = SX + cX               --เก็บค่าจำนวนรวมของ X ในแต่ละแถว
  170.     end
  171.   end
  172.   return SO, SX, criticalMove
  173. end
  174.  
  175. while true do
  176.   io.write("Choose position [1-9]: ")
  177.   local move = tonumber(io.read())
  178.   print("")
  179.   local validMove = true
  180.   goto checkMove
  181.   ::chooseAgain::
  182.   io.write("Bad move: choose position [1-9]: ")
  183.   move = tonumber(io.read())
  184.   print("")
  185.   ::checkMove::
  186.   if not move then
  187.     goto chooseAgain
  188.   --elseif move > 9 or move < 1 then
  189.     --goto chooseAgain
  190.   --else
  191.     --เพิ่มการปัดเศษทศนิยมกรณีป้อนค่าเป็นเลขทศนิยม
  192.     --move = floor(move)
  193.   --end
  194.   --แก้ v1 ปัดเศษก่อนแล้วค่อยเทียบค่า
  195.   else
  196.     move == floor(move)
  197.     if move > 9 or move < 1 then
  198.       goto chooseAgain
  199.     end
  200.   end
  201.   --##########
  202.   --แก้ให้เทียบค่า O[move] แทน
  203.   --for _, v in pairs(O) do
  204.     --if move == v then
  205.     --if O[move] then
  206.       --goto chooseAgain
  207.     --end
  208.   --end
  209.   --แก้ให้เทียบค่า X[move] แทน
  210.   --for _, v in pairs(X) do
  211.     --if move == v then
  212.     --if X[move] then
  213.       --goto chooseAgain
  214.     --end
  215.   --end
  216.   --แก้ v1 เอาลูปออกแล้วปรับเป็นเงื่อนไขเดียว
  217.   if O[move] or X[move] then
  218.     goto chooseAgain
  219.   end
  220.   --##########
  221.   --เปลี่ยนจาก insert table O เป็นเลือกตำแหน่งที่ O[move] ให้เป็น true แทน
  222.   O[move] = true
  223.   displayOX()
  224.   if checkWin(O) then
  225.     print("O win")
  226.     break
  227.   end
  228.   --เปลี่ยนจากนับจำนวนสมาชิกใน O กับ X เป้นการตรวจ true/false แทนตามการเปลี่ยน O กับ X
  229.   local d = true
  230.   for i = 1, 9 do
  231.     d = (O[i] or X[i]) and d
  232.   end
  233.   if d then
  234.     print("Draw")
  235.     break
  236.   end
  237.   --จบส่วนที่เปลี่ยนกรณีเสมอ
  238.   --เปลี่ยนจาก insert table X เป็นเลือกตำแหน่งที่ X[ai()] ให้เป็น true แทน
  239.   X[ai()] = true
  240.   displayOX()
  241.   if checkWin(X) then
  242.     print("X win")
  243.     break
  244.   end
  245.   --ตัดส่วนนี้ออกเพราะ O เริ่มก่อน O ลงสุดท้ายถึงจะเสมอได้ไม่ต้องตรวจหลังตาเดินของ X
  246.   --if #O + #X == 9 then
  247.     --print("Draw")
  248.     --break
  249.   --end
  250. end
Add Comment
Please, Sign In to add comment