-
Notifications
You must be signed in to change notification settings - Fork 0
/
tictactoe.py
134 lines (118 loc) · 4.23 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
import numpy as np
SIZE = 3
class State(object):
def __init__(self):
# self.data is a SIZE * SIZE array where
# 0 represents an empty position
# 1 represents a cross (symbol for player 1)
# 2 represents a circle (symbol for player 2)
self.data = np.zeros((SIZE, SIZE))
# player: whose turn it is to play from this state
self.player = 1
self.hash = 0
# outcome can be
# 1 if Player 1 wins
# -1 if Player 2 wins
# 0 if it's a tie
# -2 if the game is not over
# 2 if the outcome has never been computed
self.outcome = 2
# Checks whether the game is over from this state and who won
def compute_outcome(self):
if self.outcome != 2:
return self.outcome
else:
# Checks rows
for i in range(0, SIZE):
if all(x == 1 for x in self.data[i, :]):
self.outcome = 1
return 1
if all(x == 2 for x in self.data[i, :]):
self.outcome = -1
return -1
# Checks columns
for j in range(0, SIZE):
if all(x == 1 for x in self.data[:, j]):
self.outcome = 1
return 1
if all(x == 2 for x in self.data[:, j]):
self.outcome = -1
return -1
# Checks diagonals
diag = [self.data[i,i] for i in range(0, SIZE)]
if all(x == 1 for x in diag):
self.outcome = 1
return 1
if all(x == 2 for x in diag):
self.outcome = -1
return -1
anti_diag = [self.data[i,SIZE - 1 - i] for i in range(0, SIZE)]
if all(x == 1 for x in anti_diag):
self.outcome = 1
return 1
if all(x == 2 for x in anti_diag):
self.outcome = -1
return -1
# Checks whether it's a tie
data_all = [self.data[i,j] for i in range(0, SIZE) for j in range(0, SIZE)]
if all(x != 0 for x in data_all):
self.outcome = 0
return 0
# If we reached this point the game is still going on
self.outcome = -2
return -2
# Prints the board
def __repr__(self):
out = "\n"
for i in range(0, SIZE):
out += '-'
for _ in range(0, SIZE):
out += '----'
out += "\n"
out += '| '
for j in range(0, SIZE):
if self.data[i, j] == 1:
token = 'x'
elif self.data[i, j] == 2:
token = 'o'
else:
token = ' '
out += token + ' | '
out += "\n"
for _ in range(0, SIZE):
out += '----'
return out
# Takes a state and returns the list of legal moves
def legal_plays(self):
legal = []
for i in range(0, SIZE):
for j in range(0, SIZE):
if self.data[i, j] == 0:
legal.append((i,j))
return legal
# Hashes can also be computed recursively so we never use that function
# def compute_hash(self):
# self.hash = 0
# for i in self.data.reshape(SIZE * SIZE):
# self.hash = self.hash * 3 + i
# return self.hash
# Compute the hash of the state obtained by playing action (i,j)
def compute_new_hash(self, action):
(i, j) = action
return self.hash + 3 ** (SIZE * i + j) * self.player
# Returns a new state obtained by playing action (i,j)
def next_state(self, action):
(i, j) = action
new_state = State()
new_state.data = np.copy(self.data)
new_state.data[i, j] = self.player
new_state.hash = self.compute_new_hash((i,j))
new_state.player = 3 - self.player
return new_state
# Updates the state by playing action (i,j)
def update_state(self, action):
(i, j) = action
self.data[i, j] = self.player
self.hash = self.compute_new_hash((i,j))
self.player = 3 - self.player
self.outcome = 2