@@ -192,19 +192,14 @@ enum Piece: String {
192
192
}
193
193
}
194
194
195
- // a move is an integer 0-9 indicating a place to put a piece
195
+ // a move is an integer 0-8 indicating a place to put a piece
196
196
typealias Move = Int
197
197
198
198
struct Board {
199
199
let position : [ Piece ]
200
200
let turn : Piece
201
201
let lastMove : Move
202
202
203
- // the legal moves in a position are all of the empty squares
204
- var legalMoves : [ Move ] {
205
- return position. indices. filter { position [ 0ドル] == . E }
206
- }
207
-
208
203
// by default the board is empty and X goes first
209
204
// lastMove being -1 is a marker of a start position
210
205
init ( position: [ Piece ] = [ . E, . E, . E, . E, . E, . E, . E, . E, . E] , turn: Piece = . X, lastMove: Int = - 1 ) {
@@ -221,16 +216,21 @@ struct Board {
221
216
return Board ( position: tempPosition, turn: turn. opposite, lastMove: location)
222
217
}
223
218
219
+ // the legal moves in a position are all of the empty squares
220
+ var legalMoves : [ Move ] {
221
+ return position. indices. filter { position [ 0ドル] == . E }
222
+ }
223
+
224
224
var isWin : Bool {
225
225
return
226
- position [ 0 ] == position [ 1 ] && position [ 0 ] == position [ 2 ] && position [ 0 ] != . E || // row 0
227
- position [ 3 ] == position [ 4 ] && position [ 3 ] == position [ 5 ] && position [ 3 ] != . E || // row 1
228
- position [ 6 ] == position [ 7 ] && position [ 6 ] == position [ 8 ] && position [ 6 ] != . E || // row 2
229
- position [ 0 ] == position [ 3 ] && position [ 0 ] == position [ 6 ] && position [ 0 ] != . E || // col 0
230
- position [ 1 ] == position [ 4 ] && position [ 1 ] == position [ 7 ] && position [ 1 ] != . E || // col 1
231
- position [ 2 ] == position [ 5 ] && position [ 2 ] == position [ 8 ] && position [ 2 ] != . E || // col 2
232
- position [ 0 ] == position [ 4 ] && position [ 0 ] == position [ 8 ] && position [ 0 ] != . E || // diag 0
233
- position [ 2 ] == position [ 4 ] && position [ 2 ] == position [ 6 ] && position [ 2 ] != . E // diag 1
226
+ position [ 0 ] == position [ 1 ] && position [ 0 ] == position [ 2 ] && position [ 0 ] != . E || // row 0
227
+ position [ 3 ] == position [ 4 ] && position [ 3 ] == position [ 5 ] && position [ 3 ] != . E || // row 1
228
+ position [ 6 ] == position [ 7 ] && position [ 6 ] == position [ 8 ] && position [ 6 ] != . E || // row 2
229
+ position [ 0 ] == position [ 3 ] && position [ 0 ] == position [ 6 ] && position [ 0 ] != . E || // col 0
230
+ position [ 1 ] == position [ 4 ] && position [ 1 ] == position [ 7 ] && position [ 1 ] != . E || // col 1
231
+ position [ 2 ] == position [ 5 ] && position [ 2 ] == position [ 8 ] && position [ 2 ] != . E || // col 2
232
+ position [ 0 ] == position [ 4 ] && position [ 0 ] == position [ 8 ] && position [ 0 ] != . E || // diag 0
233
+ position [ 2 ] == position [ 4 ] && position [ 2 ] == position [ 6 ] && position [ 2 ] != . E // diag 1
234
234
235
235
}
236
236
@@ -239,49 +239,67 @@ struct Board {
239
239
}
240
240
}
241
241
242
- func minimax( _ board: Board , maximizing: Bool ) -> ( eval: Int , bestMove: Move ) {
243
- if board. isWin { return ( 1 , board. lastMove) }
244
- else if board. isDraw { return ( 0 , board. lastMove) }
242
+ // Find the best possible outcome for originalPlayer
243
+ func minimax( _ board: Board , maximizing: Bool , originalPlayer: Piece ) -> Int {
244
+ // Base case — evaluate the position if it is a win or a draw
245
+ if board. isWin && originalPlayer == board. turn. opposite { return 1 } // win
246
+ else if board. isWin && originalPlayer != board. turn. opposite { return - 1 } // loss
247
+ else if board. isDraw { return 0 } // draw
245
248
249
+ // Recursive case — maximize your gains or minimize the opponent's gains
246
250
if maximizing {
247
- var bestEval : ( eval : Int , bestMove : Move ) = ( Int . min, - 1 )
248
- for move in board. legalMoves {
249
- let result = minimax ( board. move ( move) , maximizing: false )
250
- if result . eval > bestEval. eval { bestEval = result}
251
+ var bestEval = Int . min
252
+ for move in board. legalMoves { // find the move with the highest evaluation
253
+ let result = minimax ( board. move ( move) , maximizing: false , originalPlayer : originalPlayer )
254
+ bestEval= max ( result, bestEval )
251
255
}
252
256
return bestEval
253
257
} else { // minimizing
254
- var worstEval : ( eval : Int , bestMove : Move ) = ( Int . max, - 1 )
258
+ var worstEval = Int . max
255
259
for move in board. legalMoves {
256
- let result = minimax ( board. move ( move) , maximizing: true )
257
- if result . eval < worstEval. eval { worstEval = result}
260
+ let result = minimax ( board. move ( move) , maximizing: true , originalPlayer : originalPlayer )
261
+ worstEval= min ( result, worstEval )
258
262
}
259
263
return worstEval
260
264
}
261
265
}
262
266
267
+ // Run minimax on every possible move to find the best one
268
+ func findBestMove( _ board: Board ) -> Move {
269
+ var bestEval = Int . min
270
+ var bestMove = - 1
271
+ for move in board. legalMoves {
272
+ let result = minimax ( board. move ( move) , maximizing: false , originalPlayer: board. turn)
273
+ if result > bestEval {
274
+ bestEval = result
275
+ bestMove = move
276
+ }
277
+ }
278
+ return bestMove
279
+ }
280
+
263
281
// win in 1 move
264
282
let toWinEasyPosition : [ Piece ] = [ . X, . O, . X,
265
283
. X, . E, . O,
266
284
. E, . E, . O]
267
285
let testBoard1 : Board = Board ( position: toWinEasyPosition, turn: . X, lastMove: 8 )
268
- let answer1 = minimax ( testBoard1, maximizing : true )
269
- print ( answer1. bestMove )
286
+ let answer1 = findBestMove ( testBoard1)
287
+ print ( answer1)
270
288
271
289
// must block O's win
272
290
let toBlockPosition : [ Piece ] = [ . X, . E, . E,
273
291
. E, . E, . O,
274
292
. E, . X, . O]
275
293
let testBoard2 : Board = Board ( position: toBlockPosition, turn: . X, lastMove: 8 )
276
- let answer2 = minimax ( testBoard2, maximizing : true )
277
- print ( answer2. bestMove )
294
+ let answer2 = findBestMove ( testBoard2)
295
+ print ( answer2)
278
296
279
297
// find the best move to win in 2 moves
280
298
let toWinHardPosition : [ Piece ] = [ . X, . E, . E,
281
299
. E, . E, . O,
282
300
. O, . X, . E]
283
301
let testBoard3 : Board = Board ( position: toWinHardPosition, turn: . X, lastMove: 6 )
284
- let answer3 = minimax ( testBoard3, maximizing : true )
285
- print ( answer3. bestMove )
302
+ let answer3 = findBestMove ( testBoard3)
303
+ print ( answer3)
286
304
//: [Next](@next)
287
305
0 commit comments