-
Notifications
You must be signed in to change notification settings - Fork 0
/
tictactoe.py
150 lines (125 loc) · 3.9 KB
/
tictactoe.py
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
import math
# define Python user-defined exceptions
class invalidAction(Exception):
"Raised when the action is invalid"
pass
X = "X"
O = "O"
EMPTY = None
def initial_state():
"""
Returns starting state of the board.
"""
return [[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY]]
def player(board):
"""
Returns which player's turn it is.
"""
x_count = sum(row.count(X) for row in board)
o_count = sum(row.count(O) for row in board)
# If X has made the same or more moves than O, it's O's turn
if x_count > o_count:
return O
else:
return X
def actions(board):
"""
Returns set of all possible actions (i, j) available on the board.
"""
return [(x, y) for x in range(3) for y in range(3) if board[x][y] == EMPTY]
def result(board, action):
"""
Returns the board that results from making move (i, j) on the board.
"""
# Rozpakowanie krotki action (x, y)
x, y = action
if board[x][y] != EMPTY:
raise invalidAction("This action is not possible!")
# Tworzenie kopii planszy
new_board = [row.copy() for row in board]
new_board[x][y] = player(board) # Umieszczanie ruchu na nowej planszy
return new_board
def winner(board):
"""
Returns the winner of the game, if there is one.
"""
# Check rows
for row in board:
if row[0] == row[1] == row[2] and row[0] is not None:
return row[0]
# Check columns
for col in range(3):
if board[0][col] == board[1][col] == board[2][col] and board[0][col] is not None:
return board[0][col]
# Check diagonals
if board[0][0] == board[1][1] == board[2][2] and board[0][0] is not None:
return board[0][0]
if board[0][2] == board[1][1] == board[2][0] and board[0][2] is not None:
return board[0][2]
# No winner yet
return None
def terminal(board):
"""
Returns True if game is over, False otherwise.
"""
return winner(board) is not None or actions(board) == []
def utility(board):
"""
Returns 1 if X has won the game, -1 if O has won, 0 otherwise.
"""
if winner(board) == X:
return 1
elif winner(board) == O:
return -1
else:
return 0
def minimax(board):
"""
Returns the optimal action for the current player on the board.
"""
if terminal(board):
return None
current_player = player(board)
if current_player == X: # Maximizing player
best_value = -math.inf
best_action = None
for action in actions(board):
simulated_board = result(board, action)
value = minimax_value(simulated_board)
if value > best_value:
best_value = value
best_action = action
return best_action
else: # Minimizing player
best_value = math.inf
best_action = None
for action in actions(board):
simulated_board = result(board, action)
value = minimax_value(simulated_board)
if value < best_value:
best_value = value
best_action = action
return best_action
def minimax_value(board):
"""
Returns the minimax value for the board.
"""
if terminal(board):
return utility(board)
current_player = player(board)
if current_player == X: # Maximizing player
best_value = -math.inf
for action in actions(board):
simulated_board = result(board, action)
value = minimax_value(simulated_board)
best_value = max(best_value, value)
return best_value
else: # Minimizing player
best_value = math.inf
for action in actions(board):
simulated_board = result(board, action)
value = minimax_value(simulated_board)
best_value = min(best_value, value)
return best_value