SHOW:
|
|
- or go back to the newest paste.
1 | --[[ | |
2 | แปลงมาจาก OX.py ในหนังสือ Artificial Intelligence | |
3 | with Machine Learning ของ รศ.ดร.ปริญญา สงวนสัตย์ | |
4 | ]] | |
5 | - | --1) แก้ครั้งที่ 1 ลืมตรวจโค้ด function all() ปัญหา checkWin() แล้วได้ false ตลอด 5-12-18 23:12 |
5 | + | --version 2 ปรับปรุงใหม่ให้สั้นขึ้นนิดนึง แก้ส่วน ai |
6 | - | --2) แก้ครั้งที่ 2 |
6 | + | |
7 | - | -- 2.1) แก้ function all() เพราะจำนวนสมาชิกใน t1 กับ t2 ไม่เท่ากัน |
7 | + | เปลี่ยน table O กับ X ให้เก็บ boolean แทนเลขช่อง |
8 | - | -- 2.2) เพิ่มส่วนตรวจ X ชนะกรณีที่มี criticalMove |
8 | + | แล้วเรียกใช้ index แทนเพื่อความสะดวกการใช้งาน |
9 | - | --3) แก้ครั้งที่ 3 |
9 | + | ลดการใช้ลูปลง |
10 | - | -- 3.1) เอา 2.2 ออกเพิ่มการตรวจแถวที่มี X สองตำแหน่งและมีตำแหน่งว่างในแถวแทน |
10 | + | |
11 | - | -- 3.2) เปลี่ยนจากสุ่มเลือกตำแหน่งว่างใน criticalMove เป็นเอาตำแหน่งที่ยังว่างมาใช้เลยเพราะใน criticalMove มีตำแหน่งว่างค่าเดียว |
11 | + | --แก้เพิ่มเติม v1 |
12 | - | -- 3.3) เพิ่มการแปลงค่าที่ผู้เล่นป้อนให้เป็นจำนวนเต็มด้วยการปัดเศษ |
12 | + | local O = {false,false,false,false,false,false,false,false,false} |
13 | - | local O, X = {}, {} |
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 | - | --3) เพิ่มตัวย่อฟังก์ชั่นปัดเศษ |
27 | + | |
28 | local rnd = math.random | |
29 | local max = math.max | |
30 | ||
31 | - | --table.sort(t1) --1) เพิ่มบรรทัดนี้ 2) แก้ออก |
31 | + | --เพิ่ม function การปัดเศษ |
32 | - | local t = 0 --2 เพิ่มบรรทัดนี้ เก็บผลรวม |
32 | + | |
33 | - | --for i = 1, #t1 do --1) แก้ออก |
33 | + | |
34 | - | for j = 1, #t2 do |
34 | + | --ทำเรียกใช้ table function สะดวกขึ้น |
35 | - | for i = 1, #t1 do --2) เพิ่มบรรทัดนี้ |
35 | + | local add = table.insert |
36 | - | --if t1[j] ~= t2[j] then --1) เปลี่ยน t1[i] เป็น t1[j] 2) แก้ออก |
36 | + | local unpak = table.unpack |
37 | - | if t1[i] == t2[j] then --2) เปลี่ยน t1[j] เป็น t1[i] เปลี่ยนเป็นตรวจเท่ากัน |
37 | + | local concat = table.concat |
38 | - | t = t + 1 --2) เพิ่มบรรทัดนี้ เก็บจำนวนสมาชิกที่ตรงกัน |
38 | + | |
39 | - | --return false --2) แก้ออก |
39 | + | --แก้ bug ของรุ่น 1 ปรับให้สั้นลงไม่ต้องใช้ลูป |
40 | - | if t == 3 then return true end --2) เพิ่มบรรทัดนี้ ถ้าสมาชิกใน t2 (table ของแถวที่สามารถชนะ) ทุกตัว(3)เป็นสมาชิกใน t1 |
40 | + | |
41 | --ให้ t1 เป็น O หรือ X | |
42 | - | end --2) เพิ่ม |
42 | + | return t1[t2[1]] and t1[t2[2]] and t1[t2[3]] |
43 | end | |
44 | - | --end --1) แก้ออก |
44 | + | |
45 | - | return false --2) เปลี่ยน true เป็น false |
45 | + | --แก้ให้ code สั้นลงไม่ต้องใช้ลูป |
46 | function any(t1, t2) | |
47 | --ให้ t1 เป็น O หรือ X | |
48 | local t = 0 | |
49 | --(เงื่อนไข and จริง or เท็จ) เหมือน (เงื่อนไข?จริง:เท็จ) ใน C | |
50 | - | for i = 1, #t1 do |
50 | + | t = t + (t1[t2[1]] and 1 or 0) |
51 | - | for j = 1, #t2 do |
51 | + | t = t + (t1[t2[2]] and 1 or 0) |
52 | - | if t1[i] == t2[j] then |
52 | + | t = t + (t1[t2[3]] and 1 or 0) |
53 | - | t = t + 1 |
53 | + | return t --คืนค่าเป็นจำนวนของต่าที่เป็นจริงของ t1 ที่มี index เป็นสมาชิกของ t2 |
54 | end | |
55 | ||
56 | --เอาส่วนของการสุ่มเลือกใน string ออกเพราะไม่ได้ใช้ | |
57 | - | return t |
57 | + | |
58 | --if type(args) == "table" then | |
59 | return args[rnd(#args)] | |
60 | --elseif type(args) == "string" then | |
61 | - | if type(args) == "table" then |
61 | + | --local p = rnd(#args) |
62 | --return args:sub(p,p) | |
63 | - | elseif type(args) == "string" then |
63 | + | |
64 | - | local p = rnd(#args) |
64 | + | |
65 | - | return args:sub(p,p) |
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 | - | local newMap = {table.unpack(map)} |
79 | + | newMap[i] = O[i] and "[O]" or (X[i] and "[X]" or "[_]") |
80 | - | if #O > 0 then |
80 | + | newMap[i+1] = O[i+1] and "[O]" or (X[i+1] and "[X]" or "[_]") |
81 | - | for o = 1, #O do |
81 | + | newMap[i+2] = O[i+2] and "[O]" or (X[i+2] and "[X]" or "[_]") |
82 | - | newMap[O[o]] = "[O]" |
82 | + | |
83 | end | |
84 | end | |
85 | - | if #X > 0 then |
85 | + | |
86 | - | for x = 1, #X do |
86 | + | |
87 | - | newMap[X[x]] = "[X]" |
87 | + | --เปลี่ยน validMove เป็นแบบเดียวกับ O กับ X แต่เก็บค่าเริ่มต้นเป็น true |
88 | local validMove = {true,true,true,true,true,true,true,true,true} | |
89 | for i = 1, 9 do | |
90 | - | for i = 1, #newMap, 3 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 | - | local validMove = {1,2,3,4,5,6,7,8,9} |
96 | + | if cX == 2 and validMove[v] then |
97 | - | --ลบตาเดินใน O กับ X ออกจาก validMove |
97 | + | return v |
98 | - | --0) เดิมใช้เปลี่ยนค่าใน validMove ที่ตรงกับ O และ X เป็น 0 แล้วค่อยมาลบออก |
98 | + | |
99 | - | --for o = 1, #O do |
99 | + | |
100 | - | --validMove[O[o]] = 0 |
100 | + | |
101 | --########## | |
102 | - | --for x = 1, #X do |
102 | + | |
103 | - | --validMove[X[x]] = 0 |
103 | + | for i, v in ipairs(validMove) do |
104 | --validMove มีสมาชิก 9 ตัวเลยเพิ่มเงื่อนไขให้ทำเฉพาะช่องที่มีค่าเป็น true | |
105 | - | --for i = #validMove, 1, -1 do |
105 | + | if v then |
106 | - | --if validMove[i] == 0 then |
106 | + | local tempX, criticalMove = {unpak(X)}, {} --ให้ tempX เป็น clone ของ X |
107 | - | --table.remove(validMove,i) |
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 | - | --0.5) ปรับเป็นรวม O กับ X เป็น moved ก่อนแล้วลบออกจาก validMove |
110 | + | --แก้ v1 เอาออกไปใช้ส่วนข้างบน |
111 | - | --เร็วขึ้นกว่าเดิมหน่อยโค้ดสั้นลงลดไปหนึ่งลูป |
111 | + | --ส่วนที่เพิ่มให้ ai ถึง O จะใกล้ชนะแต่เป็นตาของ X ถ้าเลือกแล้วชนะเกมก็ให้ใช้ค่านี้ไม่ต้องไปเลือกใน criticalMove อีก |
112 | - | local moved = {table.unpack(O)} |
112 | + | --if checkWin(tempX) then |
113 | - | for _, v in pairs(X) do |
113 | + | --return i |
114 | - | table.insert(moved,v) |
114 | + | --end |
115 | --จบส่วนที่เพิ่ม | |
116 | - | table.sort(moved, function(a,b) return a>b end) |
116 | + | --########## |
117 | - | for _, v in pairs(moved) do |
117 | + | --ส่วนข้างล่างนี้น่าจะเผื่อสำหรับตารางเกิน 3x3 หรือเปล่าเพราะ |
118 | - | table.remove(validMove,v) |
118 | + | --criticalMove จะมี O อยู่สองตำแหน่งว่างหนึ่งไม่ต้องจับใส่ list |
119 | --แล้วสุ่มเลือกก็ได้มั๊งแต่ช่างเถอะเอาตามตัวอย่าง | |
120 | - | --3) เพิ่มส่วน ai เลือกตำแหน่งว่างในแถวที่มี X อยู่สองตำแหน่ง |
120 | + | local move = {} |
121 | for _, c in pairs(criticalMove) do | |
122 | --เปลี่ยนการตรวจสอบ validMove | |
123 | - | for _, i in pairs(w) do |
123 | + | if validMove[c] then --ถ้าตำแหน่งใน criticalMove อยู่ในตาที่สามารถเดินได้ให้เพิ่มตำแหน่งนั้นใน move |
124 | - | for _, j in pairs(validMove) do |
124 | + | --แก้ v1 เอาออก |
125 | - | if cX == 2 and i == j then |
125 | + | --add(move,c) |
126 | - | return i |
126 | + | --แก้ v1 เปลี่ยนเป็นคืนค่าตำแหน่งที่ว่างในแถวของ criticalMove เพราะมีตัวเดียว |
127 | return c | |
128 | --########## | |
129 | end | |
130 | end | |
131 | --แก้ v1 เอาออก | |
132 | - | for _, v in pairs(validMove) do |
132 | + | --return randomChoice(move) |
133 | - | local tempX, criticalMove = {table.unpack(X)}, {} |
133 | + | --########## |
134 | - | table.insert(tempX,v) |
134 | + | |
135 | - | V[v], criticalMove = evalOX(O,tempX) |
135 | + | |
136 | - | if #criticalMove > 0 then |
136 | + | |
137 | - | --3) เอาส่วนนี้ออก |
137 | + | --นี่เป็นส่วนคำนวณ โดยสุ่มจากตัวเลือกที่ดีที่สุด (ตำแหน่งที่มีค่ามากที่สุดใน V) |
138 | - | --2) เพิ่มส่วน ai ถึง O จะใกล้ชนะแต่เป็นตาของ X ถ้าเลือกแล้วชนะเกมก็ให้ใช้ค่านี้ไม่ต้องไปเลือกใน criticalMove อีก |
138 | + | local maxV = max(unpak(V)) |
139 | - | --if checkWin(tempX) then |
139 | + | |
140 | - | --return v |
140 | + | |
141 | - | --end |
141 | + | |
142 | - | --3) เอา move ออก |
142 | + | add(imaxV,i) |
143 | - | local move = {} |
143 | + | |
144 | - | for _, i in pairs(validMove) do |
144 | + | |
145 | - | for _, j in pairs(criticalMove) do |
145 | + | |
146 | - | if j == i then |
146 | + | |
147 | - | --3) เปลี่ยนจากเอาค่าใส่ table เป็นคืนค่านั้นกลับ |
147 | + | |
148 | - | table.insert(move,j) |
148 | + | |
149 | - | return j |
149 | + | |
150 | --คำนวณโอกาสในตำแหน่งเดินนั้น | |
151 | --(1 + (จำนวนรวมของ X ในทุกแถวที่ X มีโอกาสชนะ) - (จำนวนรวมของ O ในทุกแถวที่ O มีโอกาสชนะ)) | |
152 | return (1 + SX - SO), criticalMove | |
153 | - | --3) เอาออก |
153 | + | |
154 | - | return randomChoice(move) |
154 | + | |
155 | function calSOX(o,x) | |
156 | local SO, SX = 0, 0 | |
157 | - | local maxV = max(table.unpack(V)) |
157 | + | |
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 | - | table.insert(imaxV,i) |
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 | - | local cO = any(o,w) |
176 | + | |
177 | - | local cX = any(x,w) |
177 | + | |
178 | - | if cX == 0 then |
178 | + | |
179 | - | SO = SO + cO |
179 | + | local validMove = true |
180 | - | if cO == 2 then |
180 | + | |
181 | - | print("critical", "{".. table.concat(w,",").."}") |
181 | + | |
182 | - | criticalMove = w |
182 | + | |
183 | move = tonumber(io.read()) | |
184 | print("") | |
185 | - | if cO == 0 then |
185 | + | |
186 | - | SX = SX + cX |
186 | + | |
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 | - | --3) เปลี่ยนให้แปลง move แป็นจำนวนเต็มด้วยการปัดเศษก่อนตรวจหาช่วงของ move |
204 | + | --if move == v then |
205 | --if O[move] then | |
206 | --goto chooseAgain | |
207 | --end | |
208 | - | move = floor(move) |
208 | + | |
209 | --แก้ให้เทียบค่า X[move] แทน | |
210 | --for _, v in pairs(X) do | |
211 | --if move == v then | |
212 | --if X[move] then | |
213 | - | for _, v in pairs(O) do |
213 | + | --goto chooseAgain |
214 | - | if move == v then |
214 | + | |
215 | --end | |
216 | --แก้ v1 เอาลูปออกแล้วปรับเป็นเงื่อนไขเดียว | |
217 | if O[move] or X[move] then | |
218 | - | for _, v in pairs(X) do |
218 | + | |
219 | - | if move == v then |
219 | + | |
220 | --########## | |
221 | --เปลี่ยนจาก insert table O เป็นเลือกตำแหน่งที่ O[move] ให้เป็น true แทน | |
222 | O[move] = true | |
223 | - | table.insert(O,move) |
223 | + | |
224 | if checkWin(O) then | |
225 | print("O win") | |
226 | break | |
227 | end | |
228 | --เปลี่ยนจากนับจำนวนสมาชิกใน O กับ X เป้นการตรวจ true/false แทนตามการเปลี่ยน O กับ X | |
229 | - | if #O + #X == 9 then |
229 | + | local d = true |
230 | for i = 1, 9 do | |
231 | d = (O[i] or X[i]) and d | |
232 | end | |
233 | - | table.insert(X,ai()) |
233 | + | if d then |
234 | print("Draw") | |
235 | break | |
236 | end | |
237 | --จบส่วนที่เปลี่ยนกรณีเสมอ | |
238 | --เปลี่ยนจาก insert table X เป็นเลือกตำแหน่งที่ X[ai()] ให้เป็น true แทน | |
239 | - | --2) เอาออกไม่ต้องใช้ตรวจเฉพาะในตาของ O ก็พอ |
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 |