-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtictactoe.py
117 lines (95 loc) · 3.28 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
from typing import List, Set, Tuple, Optional
X = "X"
O = "O"
EMPTY = None
def initial_state() -> List[List[None]]:
return [[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY]]
def player(board: List[List]) -> str:
num_x = sum(row.count(X) for row in board)
num_o = sum(row.count(O) for row in board)
return X if num_x <= num_o else O
def actions(board: List[List]) -> Set[Tuple[int, int]]:
return {(i, j) for i in range(3) for j in range(3) if board[i][j] is EMPTY}
def result(board: List[List], action: Tuple[int, int]) -> List[List]:
i, j = action
if not (0 <= i < len(board) and 0 <= j < len(board[0])):
raise ValueError("Action is out of bounds.")
if board[i][j] is not EMPTY:
raise ValueError("Cell is already occupied.")
new_board = [row[:] for row in board]
new_board[i][j] = player(board)
return new_board
def winner(board: List[List]) -> Optional[str]:
# Check rows
for row in board:
if all(cell == X for cell in row):
return X
elif all(cell == O for cell in row):
return O
# Check columns
for col in zip(*board):
if all(cell == X for cell in col):
return X
elif all(cell == O for cell in col):
return O
# Check diagonals
if board[0][0] == board[1][1] == board[2][2] != EMPTY:
return board[0][0]
if board[0][2] == board[1][1] == board[2][0] != EMPTY:
return board[0][2]
return None
def terminal(board: List[List]) -> bool:
return not actions(board) or winner(board) is not None
def utility(board: List[List]) -> int:
if winner(board) == X:
return 1
elif winner(board) == O:
return -1
else:
return 0
def minimax(board: List[List]) -> Tuple[int, int]:
if winner(board) is not None:
return None
if player(board) == X:
best_value = float("-inf")
alpha = float("-inf")
beta = float("inf")
for action in actions(board):
value = min_value(result(board, action), alpha, beta)
if value > best_value:
best_value = value
best_action = action
alpha = max(alpha, best_value)
else:
best_value = float("inf")
alpha = float("-inf")
beta = float("inf")
for action in actions(board):
value = max_value(result(board, action), alpha, beta)
if value < best_value:
best_value = value
best_action = action
beta = min(beta, best_value)
return best_action
def max_value(board: List[List], alpha: int, beta: int) -> int:
if terminal(board):
return utility(board)
value = float("-inf")
for action in actions(board):
value = max(value, min_value(result(board, action), alpha, beta))
if value >= beta:
return value
alpha = max(alpha, value)
return value
def min_value(board: List[List], alpha: int, beta: int) -> int:
if terminal(board):
return utility(board)
value = float("inf")
for action in actions(board):
value = min(value, max_value(result(board, action), alpha, beta))
if value <= alpha:
return value
beta = min(beta, value)
return value