-
Notifications
You must be signed in to change notification settings - Fork 0
/
tictac.py
218 lines (179 loc) · 5.88 KB
/
tictac.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
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# -*- coding: utf-8 -*-
"""
Tic Tac Toe game for practicing TDD.
"""
class NotInTurnError(Exception):
pass
class InvalidMoveError(Exception):
pass
class TicTac(object):
"""
Game of Tic Tac Toe.
"""
def __init__(self):
self.board = Board()
self.player_one = Player(self, 'x')
self.player_two = Player(self, 'y')
self.last_played = None
def show_game_board(self):
"""
Show the current status of the game board.
"""
return self.board.body
def is_over(self):
"""
Check if the game is over. A game is over if all the
tiles in a board are completed.
"""
return self.who_won() or self.board.is_full()
def who_won(self):
"""
Return the winning player.
"""
if self.board.check_if_player_won(self.player_one):
return self.player_one
elif self.board.check_if_player_won(self.player_two):
return self.player_two
def is_a_draw(self):
"""
Return wheter the game is a draw or not.
"""
return not bool(self.who_won())
class Board(object):
"""
Game board.
"""
def __init__(self):
self.body = [
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
]
def is_empty(self):
"""
Verify that the board is empty. A board is empty if all the rows
are contain only 0.
"""
for row in self.body:
for column in row:
if bool(column):
return False
return True
def is_full(self):
"""
Verify that the board is full. A full board does not contain
a single negative value in its body.
"""
for row in self.body:
for column in row:
if not bool(column):
return False
return True
def _check_move_validity(self, *args):
"""
Check that the move to be made is a valid one.
:param coord_x: Integer, x position.
:param coord_y: Integer, y position.
"""
if bool(self.body[args[0]][args[1]]):
raise InvalidMoveError('Invalid move.')
def _check_move_is_inside_board(self, *args):
"""
Check that the move is inside the board.
:param coord_x: Integer, x position.
:param coord_y: Integer, y position.
"""
if args[0] < 0 or args[0] > 2 or args[1] < 0 or args[1] > 2:
raise InvalidMoveError('Invalid move.')
def place_move_in_board(self, mark, *args):
"""
Place a move into the board with the requested mark.
:param mark: String, unique mark.
:param coord_x: Integer, x position.
:param coord_y: Integer, y position.
"""
self._check_move_is_inside_board(*args)
self._check_move_validity(*args)
self.body[args[0]][args[1]] = mark
def _check_row(self, row, player_mark):
"""
Check a row of the board to check if all tiles match
a player mark.
:param row: List, sequence of 3 tiles.
:param player_mark: String, a mark to represent a player.
"""
for tile in row:
if tile != player_mark:
return False
return True
def _check_column(self, column, player_mark):
"""
Check if a player has won by placing its mark on the
columns.
:param column: Integer, column to check in board.
:param player_mark: String, mark of a player.
"""
for row in range(3):
if self.body[row][column] != player_mark:
return False
return True
def _check_first_diagonal(self, player_mark):
"""
Check the validity of the first diagonal of the
board.
:param player_mark: String, player identification.
"""
for row in range(3):
if self.body[row][row] != player_mark:
return False
return True
def _check_second_diagonal(self, player_mark):
"""
Check the validity of the second diagonal of the board.
:param player_mark: String, player identification.
"""
return self.body[0][2] == self.body[1][1] == \
self.body[2][0] == player_mark
def check_if_player_won(self, player):
"""
Check if the player passed to it has won the game.
:param player: Player object.
"""
for row in self.body:
if self._check_row(row, player.player_mark):
return True
elif self._check_column(self.body.index(row), player.player_mark):
return True
elif self._check_first_diagonal(player.player_mark):
return True
elif self._check_second_diagonal(player.player_mark):
return True
class Player(object):
"""
A tic tac player.
"""
def __init__(self, game, mark):
self.game = game
self.player_mark = mark
def _check_if_is_my_turn(self):
"""
Validate that the last player to make a move was not me. This
object is no cheater.
"""
if self.game.last_played == self:
raise NotInTurnError('Playing out of turn.')
def _mark_my_turn_as_done(self):
"""
Player finished making a move, so mark it as the last player
to play and pass the turn to the other player.
"""
self.game.last_played = self
def make_move(self, coord_x, coord_y):
"""
Make a move into a game board using a x,y coordinates point.
:param coord_x: Integer, must be between 0 and 2.
:param coord_y: Integer, must be between 0 and 2.
"""
self._check_if_is_my_turn()
self.game.board.place_move_in_board(self.player_mark, coord_x, coord_y)
self._mark_my_turn_as_done()