View difference between Paste ID: yqVpijYW and QeQkquta
SHOW: | | - or go back to the newest paste.
1
--[[
2
  แปลงมาจาก OX.py ในหนังสือ Artificial Intelligence
3
  with Machine Learning ของ รศ.ดร.ปริญญา สงวนสัตย์
4
]]
5-
--version 2 ปรับปรุงใหม่ให้สั้นขึ้นนิดนึง แก้ส่วน ai 
5+
--version 3 ใช้ penlight module ช่วยจัดการเรื่อง list และ list comprehension
6
--ทำให้เขียนโค้ดสั้นลงใกล้เคียง python ต้้นฉบับมากขึ้น แต่ประสิทธิภาพยังไม่ได้ตรวจ
7-
  เปลี่ยน table O กับ X ให้เก็บ boolean แทนเลขช่อง
7+
-- #1.1 แก้ไขเปลี่ยนส่วนของ ai() และการตรวจสอบตอนผู้เล่นพิมพ์ตำแหน่ง
8-
  แล้วเรียกใช้ index แทนเพื่อความสะดวกการใช้งาน
8+
local comp = require("pl.comprehension").new()
9-
  ลดการใช้ลูปลง
9+
local ls = require("pl.List")
10
local O, X = ls{}, ls{}
11-
--แก้เพิ่มเติม v1
11+
12-
local O = {false,false,false,false,false,false,false,false,false}
12+
13-
local X = {false,false,false,false,false,false,false,false,false}
13+
14
            {1,4,7},
15
            {2,5,8},
16
            {3,6,9},
17
            {1,5,9},
18
            {3,5,7}}
19
local rnd = math.random
20
local max = math.max
21
local floor = math.floor
22
local concat = table.concat
23-
เอาออก
23+
local unpack = table.unpack
24-
local map = {"[_]","[_]","[_]",
24+
25-
            "[_]","[_]","[_]",
25+
--function all(t1, t2)
26-
            "[_]","[_]","[_]"}
26+
  --local t = true
27
    --for _, v in pairs(t2) do
28
      --t = t and t1:contains(v)
29
    --end
30
  --return t
31-
--เพิ่ม function การปัดเศษ
31+
--end
32
33
--function any(t1, t2)
34-
--ทำเรียกใช้ table function สะดวกขึ้น
34+
  --local t = 0
35-
local add = table.insert
35+
  --for _, v in pairs(t2) do
36-
local unpak = table.unpack
36+
    --if t1:contains(v) then
37
      --t = t + 1
38
    --end
39-
--แก้ bug ของรุ่น 1 ปรับให้สั้นลงไม่ต้องใช้ลูป
39+
40-
function all(t1, t2)
40+
  --return t
41-
  --ให้ t1 เป็น O หรือ X
41+
--end
42-
  return t1[t2[1]] and t1[t2[2]] and t1[t2[3]]
42+
43
function randomChoice(args)
44
  return args[rnd(#args)]
45-
--แก้ให้ code สั้นลงไม่ต้องใช้ลูป
45+
46-
function any(t1, t2)
46+
47-
  --ให้ t1 เป็น O หรือ X
47+
48-
  local t = 0
48+
49-
  --(เงื่อนไข and จริง or เท็จ) เหมือน (เงื่อนไข?จริง:เท็จ) ใน C
49+
    --if all(P, w) then
50-
  t = t + (t1[t2[1]] and 1 or 0)
50+
      --return true
51-
  t = t + (t1[t2[2]] and 1 or 0)
51+
52-
  t = t + (t1[t2[3]] and 1 or 0)
52+
    local all = ls(comp "(_1):contains(v) for _,v in pairs(_2)" (P,w)):reduce(function(x,y) return x and y end)
53-
  return t  --คืนค่าเป็นจำนวนของต่าที่เป็นจริงของ t1 ที่มี index เป็นสมาชิกของ t2 
53+
    if all then
54
      return true
55
    end
56-
--เอาส่วนของการสุ่มเลือกใน string ออกเพราะไม่ได้ใช้
56+
57
  return false
58-
  --if type(args) == "table" then
58+
59-
    return args[rnd(#args)]
59+
60-
  --elseif type(args) == "string" then
60+
61-
    --local p = rnd(#args)
61+
62-
    --return args:sub(p,p)
62+
63
    newMap[i] = O:contains(i) and "[O]" or (X:contains(i) and "[X]" or "[_]")
64
    newMap[i+1] = O:contains(i+1) and "[O]" or (X:contains(i+1) and "[X]" or "[_]")
65
    newMap[i+2] = O:contains(i+2) and "[O]" or (X:contains(i+2) and "[X]" or "[_]")
66
    print(newMap[i]..newMap[i+1]..newMap[i+2])
67
  end
68-
    if all(P, w) then
68+
69
70
function ai()
71
  local validMove = ls.range(1,9)
72
  local moved = O:clone():extend(X)
73
  for _, v in pairs(moved) do
74
    validMove:remove_value(v)
75-
--ลดเหลือลูปเดียวสร้าง map สด
75+
76
  -- #1.1 แก้ไขตรวจหาแถวที่มี X สองตำแหน่งและไม่มี O ให้เลือกตำแหน่งที่ว่างในแถวนั้น
77
  for _, w in pairs(win) do
78
    local xl = ls(comp "(_1):contains(v) for _,v in pairs(_2)" (X,w))
79-
    newMap[i] = O[i] and "[O]" or (X[i] and "[X]" or "[_]")
79+
    local vl = ls(comp "(_1):contains(v) for _,v in pairs(_2)" (validMove,w))
80-
    newMap[i+1] = O[i+1] and "[O]" or (X[i+1] and "[X]" or "[_]")
80+
    if xl:count(true)==2 and vl:reduce(function(x,y) return x or y end) then
81-
    newMap[i+2] = O[i+2] and "[O]" or (X[i+2] and "[X]" or "[_]")
81+
      return comp "v for _,v in pairs(_1) if (_2):contains(v)"(w,validMove)[1]
82
    end
83
  end
84
  --##########
85
  local V = {-100,-100,-100,-100,-100,-100,-100,-100,-100}
86
  for _, v in pairs(validMove) do
87-
  --เปลี่ยน validMove เป็นแบบเดียวกับ O กับ X แต่เก็บค่าเริ่มต้นเป็น true
87+
    local tempX, criticalMove = X:clone(), {}
88-
  local validMove = {true,true,true,true,true,true,true,true,true}
88+
    tempX:append(v)
89-
  for i = 1, 9 do
89+
    V[v], criticalMove = evalOX(O,tempX)
90-
    validMove[i] = not (O[i] or X[i]) and validMove[i]
90+
    if #criticalMove > 0 then
91
      -- #1.1 เอาออก
92-
--เพิ่ม v1 หาแถวที่มี X สองตำแหน่งและไม่มี O คืนค่าเป็นตำแหน่งที่ว่างในแถวนั้น
92+
      --if checkWin(tempX) then
93
        --return v
94-
    local cX = any(X,w)
94+
      --end
95-
    for _, v in pairs(w) do
95+
      --##########
96-
      if cX == 2 and validMove[v] then
96+
      -- #1.1 เปลี่ยนจากใช้ลูปเป็น list comprehention แทนและ return ค่าเดียวไม่ต้องใส่ table แล้วสุ่มเลือก
97-
        return v
97+
      --local move = ls{}
98
      --for _, i in pairs(criticalMove) do
99
        --if validMove:contains(i) then
100
          --move:append(i)
101-
--##########
101+
102
      --end
103-
  for i, v in ipairs(validMove) do
103+
      --return randomChoice(move)
104-
    --validMove มีสมาชิก 9 ตัวเลยเพิ่มเงื่อนไขให้ทำเฉพาะช่องที่มีค่าเป็น true
104+
      --##########
105-
    if v then
105+
      return comp "v for _,v in pairs(_1) if (_2):contains(v)" (criticalMove,validMove)[1]
106-
      local tempX, criticalMove = {unpak(X)}, {}  --ให้ tempX เป็น clone ของ X
106+
107-
      tempX[i] = v                                --เพิ่มตำแหน่งเดินของ X โดยเปลี่ยน tempX[i] ด้วยค่าใน validMove[i]
107+
108-
      V[i], criticalMove = evalOX(O,tempX)        --หา critiMove และคำนวณทางเลือกในตาเดินต่างๆ (V[i])
108+
  local maxV = max(unpack(V))
109-
      if #criticalMove > 0 then                   --ถ้า O ใกล้ชนะ (มีสองตัวในชุดใดชุดหนึ่งของ table win)
109+
  --local imaxV = ls{}
110-
        --แก้ v1 เอาออกไปใช้ส่วนข้างบน
110+
  --for i, v in pairs(V) do
111-
        --ส่วนที่เพิ่มให้ ai ถึง O จะใกล้ชนะแต่เป็นตาของ X ถ้าเลือกแล้วชนะเกมก็ให้ใช้ค่านี้ไม่ต้องไปเลือกใน criticalMove อีก
111+
    --if v == maxV then
112-
        --if checkWin(tempX) then
112+
      --imaxV:append(i)
113-
          --return i
113+
114
  --end
115-
        --จบส่วนที่เพิ่ม
115+
  local imaxV = comp "i for i,v in pairs(_1) if v == (_2)" (V,maxV)
116-
        --##########
116+
117-
        --ส่วนข้างล่างนี้น่าจะเผื่อสำหรับตารางเกิน 3x3 หรือเปล่าเพราะ
117+
118-
        --criticalMove จะมี O อยู่สองตำแหน่งว่างหนึ่งไม่ต้องจับใส่ list 
118+
119-
        --แล้วสุ่มเลือกก็ได้มั๊งแต่ช่างเถอะเอาตามตัวอย่าง
119+
120-
        local move = {}
120+
  local SO, SX, criticalMove = calSOX(o,x)
121-
        for _, c in pairs(criticalMove) do
121+
122-
          --เปลี่ยนการตรวจสอบ validMove
122+
123-
          if validMove[c] then                    --ถ้าตำแหน่งใน criticalMove อยู่ในตาที่สามารถเดินได้ให้เพิ่มตำแหน่งนั้นใน move
123+
124-
            --แก้ v1 เอาออก
124+
125-
            --add(move,c)
125+
126-
            --แก้ v1 เปลี่ยนเป็นคืนค่าตำแหน่งที่ว่างในแถวของ criticalMove เพราะมีตัวเดียว
126+
127-
            return c
127+
128-
            --##########
128+
    --local cO = any(o,w)
129-
          end
129+
    --local cX = any(x,w)
130-
        end
130+
    local cO = ls(comp "(_1):contains(v) for _,v in pairs(_2)" (o,w))
131-
        --แก้ v1 เอาออก
131+
    local cX = ls(comp "(_1):contains(v) for _,v in pairs(_2)" (x,w))
132-
        --return randomChoice(move)
132+
    if not cX:reduce(function(x,y) return x or y end) then
133-
        --##########
133+
      local nO = cO:count(true)
134
      SO = SO + nO
135
      if nO == 2 then
136
        print("critical", "{".. concat(w,",").."}")
137-
  --นี่เป็นส่วนคำนวณ โดยสุ่มจากตัวเลือกที่ดีที่สุด (ตำแหน่งที่มีค่ามากที่สุดใน V)
137+
        criticalMove = w
138-
  local maxV = max(unpak(V))
138+
139-
  local imaxV = {}
139+
140-
  for i, v in pairs(V) do
140+
    if not cO:reduce(function(x,y) return x or y end) then
141-
    if v == maxV then
141+
      SX = SX + cX:count(true)
142-
      add(imaxV,i)
142+
143
  end
144
  return SO, SX, criticalMove
145
end
146
147
while true do
148
  io.write("Choose position [1-9]: ")
149-
  local SO, SX, criticalMove =calSOX(o,x)
149+
150-
  --คำนวณโอกาสในตำแหน่งเดินนั้น
150+
151-
  --(1 + (จำนวนรวมของ X ในทุกแถวที่ X มีโอกาสชนะ) - (จำนวนรวมของ O ในทุกแถวที่ O มีโอกาสชนะ))
151+
152
  ::chooseAgain::
153
  io.write("Bad move: choose position [1-9]: ")
154
  move = tonumber(io.read())
155
  print("")
156
  ::checkMove::
157
  if not move then
158
    goto chooseAgain
159-
    local cO = any(o,w)          --หาจำนวนของ O ที่อยู่ใน w (table ย่อยใน Win)
159+
  -- #1.1 เอาออก
160-
    local cX = any(x,w)          --หาจำนวนของ X ที่อยู่ใน w (table ย่อยใน Win)
160+
161-
    if cX == 0 then              --ถ้าไม่มี X ในแถวนั้น (แปลว่ามีโอกาสที่ O จะชนะในแถวนั้น)
161+
162-
      SO = SO + cO               --เก็บค่าจำนวนรวมของ O ในแต่ละแถว
162+
163-
      if cO == 2 then            --ถ้าจำนวนของ O ในแถวนั้นเป็น 2
163+
164
    move = floor(move)
165-
        criticalMove = w         --แสดงว่าแถวนั้นเป็น critical ที่ X ต้องกันเพื่อไม่ให้ O ชนะ
165+
    -- #1.1 ย้ายมาตรวจหลังจากปัดเศษแล้ว
166
    if move > 9 or move < 1 then
167
      goto chooseAgain
168-
    if cO == 0 then              --ถ้าไม่มี O ในแถวนั้น (แปลว่ามีโอกาสที่ X จะชนะในแถวนั้น)
168+
169-
      SX = SX + cX               --เก็บค่าจำนวนรวมของ X ในแต่ละแถว
169+
    --##########
170
  end
171
  if O:contains(move) or X:contains(move) then
172
    goto chooseAgain
173
  end
174
  O:append(move)
175
  displayOX()
176
  if checkWin(O) then
177
    print("O win")
178
    break
179-
  local validMove = true
179+
180
  if #O + #X == 9 then
181
    print("Draw")
182
    break
183
  end
184
  X:append(ai())
185
  displayOX()
186
  if checkWin(X) then
187
    print("X win")
188
    break
189
  end
190-
  --else
190+