-
Notifications
You must be signed in to change notification settings - Fork 0
/
TicTacToe.py
140 lines (116 loc) · 4.92 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
from collections import OrderedDict, deque
from SeriesGame import SeriesGame
class TicTacToeEngine(SeriesGame):
def __init__(self):
SeriesGame.__init__(self, height=3, width=3, win=3)
def b_diag_iter(self, x, y):
return (f"{z}, {z}" for z in range(3)) if x == y else ()
def f_diag_iter(self, x, y):
return ((f"{z}, {2 - z}" for z in range(3))
if int(x) == 2 - int(y) else ())
class TicTacToe_Intelligence:
def __init__(self):
self.engine = TicTacToeEngine()
self.priority_moves = deque()
def draw(self, values):
print("""
{} | {} | {}
--+---+--
{} | {} | {}
--+---+--
{} | {} | {}
""".format(*values))
def count_along_vec(self, Vector):
return sum(self.engine.players_vector(Vector))
def get_empty(self, Vector):
for Index in Vector:
if self.engine[Index] == " ":
return Index
def get_next_vec_win(self, Vector):
repeatVector = tuple(Vector)
if self.count_along_vec(repeatVector) == 2:
return self.get_empty(repeatVector)
def get_winning_moves(self, move):
x, y = move[0], move[-1]
winAlongVecs = (self.get_next_vec_win(Iter(x, y))
for Iter in self.engine.vector_iters)
return [move for move in winAlongVecs if move is not None]
def count_focus(self, Vector, Counter):
nextPlayer, repeatVector = self.engine.get_next_player(), tuple(Vector)
isNextPlayerPresent = any(self.engine[Index] == nextPlayer
for Index in repeatVector)
if not isNextPlayerPresent:
Focus = self.count_along_vec(repeatVector)
for Index in repeatVector:
Counter[Index] += Focus
def apply_to_board_vectors(self, Func, *args):
for z in range(3):
Func(self.engine.row_iter(None, z), *args)
Func(self.engine.col_iter(z, None), *args)
Func(self.engine.b_diag_iter(0, 0), *args)
Func(self.engine.f_diag_iter(0, 2), *args)
def count_enemy_focus(self):
focusCount = OrderedDict((Index, 0) for Index in iter(self.engine))
self.apply_to_board_vectors(self.count_focus, focusCount)
# self.draw(focusCount.values())
return focusCount
def get_death_moves(self):
focusCount = self.count_enemy_focus()
enemyFocus = (Idx for Idx, Cnt in focusCount.items() if Cnt > 1)
moves = []
for Index in enemyFocus:
backup = (self.engine.player, self.engine.game_board.copy())
self.engine.player = self.engine.get_next_player()
self.engine[Index] = self.engine.player
moves.extend(self.get_winning_moves(Index))
self.engine.player, self.engine.game_board = backup
return moves
def count_wins(self, Vector, Counter):
repeatVector = tuple(Vector)
if self.engine.is_vector_win(repeatVector):
for Index in repeatVector:
Counter[Index] += 1
def fill_game_board(self, Player):
for Index in self.engine.game_board:
if self.engine[Index] == " ":
self.engine[Index] = Player
def count_board_wins(self, winCount, Player):
backup = (self.engine.player, self.engine.game_board.copy())
self.engine.player = Player
self.fill_game_board(Player)
self.apply_to_board_vectors(self.count_wins, winCount)
self.engine.player, self.engine.game_board = backup
# self.draw(winCount.values())
return winCount
def invert_count(self, countDict):
invDict = {}
for Index, Count in countDict.items():
invDict.setdefault(Count, []).append(Index)
return invDict
def sort_count(self, countDict):
nestedSort = sorted(self.invert_count(countDict).items(), reverse=True)
return [v for _, l in nestedSort for v in l]
def make_move(self):
# print(self.priority_moves)
while self.priority_moves:
move = self.priority_moves.popleft()
if self.engine.game_board.get(move, "") == " ": break
else:
deathMoves = self.get_death_moves()
# print(deathMoves)
winCount = OrderedDict((Index, 0) for Index in iter(self.engine))
self.count_board_wins(winCount, self.engine.get_player())
self.count_board_wins(winCount, self.engine.get_next_player())
for move in self.sort_count(winCount):
if (self.engine.game_board.get(move, "") == " " and
move not in deathMoves):
break
self.engine.make_move(move)
self.priority_moves.extend(self.get_winning_moves(move))
return move
def oppenent_move(self, move):
self.engine.make_move(move);
self.priority_moves.extend(self.get_winning_moves(move))
def reset(self):
self.engine.reset()
self.priority_moves.clear()