-
Notifications
You must be signed in to change notification settings - Fork 131
Expand file tree
/
Copy pathTic-Tac-Toe using Minimax.py
More file actions
183 lines (154 loc) · 5.63 KB
/
Copy pathTic-Tac-Toe using Minimax.py
File metadata and controls
183 lines (154 loc) · 5.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
GAME_INCOMPLETE = 0
GAME_DRAW = 1
GAME_X = 2
GAME_O = 3
X = 1
O = -1
EMPTY = 0
# This function tests if a specific player wins. Possibilities:
# Three rows [X X X] or [O O O]
# Three cols [X X X] or [O O O]
# Two diagonals [X X X] or [O O O]
# param board: the state of the current board
# return GAME_INCOMPLETE, GAME_DRAW, GAME_X, or GAME_O
def evaluate_game(board):
win_states = [[board[0][0], board[0][1], board[0][2]],
[board[1][0], board[1][1], board[1][2]],
[board[2][0], board[2][1], board[2][2]],
[board[0][0], board[1][0], board[2][0]],
[board[0][1], board[1][1], board[2][1]],
[board[0][2], board[1][2], board[2][2]],
[board[0][0], board[1][1], board[2][2]],
[board[2][0], board[1][1], board[0][2]]]
if [X, X, X] in win_states:
return GAME_X
if [O, O, O] in win_states:
return GAME_O
for row in board:
for i in row:
if i == EMPTY:
return GAME_INCOMPLETE
return GAME_DRAW;
# Outputs the current game state to the console
# param board: the state of the current board
def print_board(board):
for row in range(len(board)):
line = ""
for col in range(len(board[row])):
if board[row][col] == X:
line = line + ' X '
elif board[row][col] == O:
line = line + ' O '
else:
line = line + " "
if col < 2:
line = line + "|"
print(line)
if row < 2:
print("-----------")
# This function plays the O player (The opponent)
# Presently I have made O simply return the first valid move I find
# If you like, you can make this function match your X function to watch two minimax agents duke it out
# But really, this can be defined to anything you want it to do for testing. I will only be testing "X_move"
# param board: the state of the current board
# return a tuple (i,j) with the row, col of O's chosen move
def O_move(board):
for row in range(len(board)):
for col in range(len(board[row])):
if board[row][col] == EMPTY:
return (row, col)
print("ERROR! No Valid Move!")
def minimax(board, depth,move):
game_winner = evaluate_game(board)
if (game_winner == GAME_X):
return 10
elif (game_winner == GAME_O):
return -10
elif (game_winner == GAME_DRAW):
return 0
if move == "O":
best = 1000
i, j = O_move(board)
board[i][j] = O
best = min(best,minimax(board, depth+1, "x"))
board[i][j] = EMPTY
return best-depth
best = -1000
for row in range(len(board)):
for col in range(len(board[row])):
if (board[row][col] == EMPTY):
board[row][col] = X
best = max(best, minimax(board,depth+1,"o"))
board[row][col] = EMPTY
return best-depth
# This function plays the X player (The player)
# This is the main functionality you will implement with the minimax algoithm
# param board: the state of the current board
# return a tuple (i,j) with the row, col of X's chosen move
def X_move(board):
#TODO: Implement the Minimax Algorithm
# Given an input game state, find the best move for X with the minimax algorithm
# For scores, you can use +10 for an X win, -10 for a O win, and 0 for a Draw
# In addition, in order to motivate the agent to win or lose as soon as possible,
# subtract the depth of completed game state from the score. For Example:
#
# If the input state is: X | | X
# | | O
# | O |
#
# Some potential completed game states might have the scores:
#
# X | O | X X Win = 10
# | X | O -> -> Score = 7
# | O | X Depth = 3
#
# X | X | X X Win = 10
# | | O -> -> Score = 9
# | O | Depth = 1
#
# X | O | X Draw = 0
# O | X | O -> -> Score = -5
# O | O | X Depth = 5
#
# X | O | X O Win = -10
# X | O | O -> -> Score = -15 -> This state is actually not possible, because X always goes first
# O | O | X Depth = 5 However, in the input state I used, its actually impossible for O to win, as far as I can tell...
#
#code begins !
bestVal = -1000
bestMove = (-1,-1)
for row in range(len(board)):
for col in range(len(board[row])):
if board[row][col] == EMPTY:
board[row][col] = X
moveVal = minimax(board,1, "o")
board[row][col] = EMPTY
if (moveVal > bestVal):
bestVal= moveVal
bestMove = (row,col)
return (bestMove)
print("ERROR! No Valid Move!")
#END FILLER CODE
board = [[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY]]
game_winner = evaluate_game(board)
#Game Loop
while game_winner == GAME_INCOMPLETE:
i, j = X_move(board)
board[i][j] = X
print_board(board)
game_winner = evaluate_game(board)
if game_winner != GAME_INCOMPLETE:
break;
i, j = O_move(board)
board[i][j] = O
print_board(board)
game_winner = evaluate_game(board)
#Game is complete, announce winner
if game_winner == GAME_DRAW:
print("Game was a Draw!")
elif game_winner == GAME_X:
print("X Wins!")
else:
print("O Wins!")