-
Notifications
You must be signed in to change notification settings - Fork 1
/
tic_tac_toe.py
323 lines (265 loc) · 13.8 KB
/
tic_tac_toe.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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
'''tic-tac-toe Game using PySimpleGUI'''
import os
import numpy as np
import PySimpleGUI as sg
INIT_WINDOW = None
CURRENT_WORKING_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
X_IMAGE = os.path.join(CURRENT_WORKING_DIRECTORY, 'X.png')
X_RED = os.path.join(CURRENT_WORKING_DIRECTORY,'X_Red.png')
O_IMAGE = os.path.join(CURRENT_WORKING_DIRECTORY, 'O.png')
O_RED = os.path.join(CURRENT_WORKING_DIRECTORY, 'O_Red.png')
GAME_ICON = os.path.join(CURRENT_WORKING_DIRECTORY, 'tictactoe.ico')
START_GAME: bool = False
CHECK_FOR_WINNER: bool = False
MAIN_DIAGONAL_IS_WINNER: bool = False
CURENT_BOARD_WON: bool = False
ROWS, COLS = (3, 3)
GAME_PROGRESS_ARRAY = [['' for i in range(COLS)] for j in range(ROWS)]
GAME_PROGRESS_ARRAY = np.array(GAME_PROGRESS_ARRAY, dtype=str)
WINNING_PATTERNS: list = [['00', '10', '20'], ['01', '11', '21'], ['02', '12', '22'], # rows
['00', '01', '02'], ['10', '11', '12'], ['20', '21', '22'], # columns
['00', '11', '22'], ['00', '11', '02']] # diagonals
def split(word):
'''splits a string to constituents chars.'''
return [int(char) for char in word]
def progress_game(key: str, player_marker: str):
'''populated the 'GAME_PROGRESS_ARRAY' and
checks for is winning condition.'''
global GAME_PROGRESS_ARRAY
global CURENT_BOARD_WON
row, column = split(key)
GAME_PROGRESS_ARRAY[row][column] = player_marker
if CHECK_FOR_WINNER:
if is_winning():
CURENT_BOARD_WON = True
def is_row_column_diagonal_complete(row_col_num: int = -1, is_row: bool = True, is_diagonal: bool = False):
'''checks if the given row or column is complete
to proceed with a winner.'''
if is_diagonal is False and row_col_num != -1:
if is_row:
row = row_col_num
if GAME_PROGRESS_ARRAY[row][0] != '' and \
GAME_PROGRESS_ARRAY[row][1] != '' and \
GAME_PROGRESS_ARRAY[row][2] != '':
return True
else:
return False
else:
col = row_col_num
if GAME_PROGRESS_ARRAY[0][col] != '' and \
GAME_PROGRESS_ARRAY[1][col] != '' and \
GAME_PROGRESS_ARRAY[2][col] != '':
return True
else:
return False
else:
if GAME_PROGRESS_ARRAY[0][0] != '' and \
GAME_PROGRESS_ARRAY[1][1] != '' and \
GAME_PROGRESS_ARRAY[2][2] != '':
return True
elif GAME_PROGRESS_ARRAY[2][0] != '' and \
GAME_PROGRESS_ARRAY[1][1] != '' and \
GAME_PROGRESS_ARRAY[0][2] != '':
return True
def mark_the_winner(row_is_winner: bool, row_column_index: int = -1, diagonal_is_winner: bool = False):
'''marks the winner row/column by updating
the button row/column.'''
if not diagonal_is_winner and row_column_index != -1:
if row_is_winner:
row = row_column_index
if GAME_PROGRESS_ARRAY[row][0] == 'X':
GAME_BOARD[str(row)+str(0)].update(image_filename=X_RED)
GAME_BOARD[str(row)+str(1)].update(image_filename=X_RED)
GAME_BOARD[str(row)+str(2)].update(image_filename=X_RED)
else:
GAME_BOARD[str(row)+str(0)].update(image_filename=O_RED)
GAME_BOARD[str(row)+str(1)].update(image_filename=O_RED)
GAME_BOARD[str(row)+str(2)].update(image_filename=O_RED)
else:
col = row_column_index
if GAME_PROGRESS_ARRAY[0][col] == 'X':
GAME_BOARD[str(0)+str(col)].update(image_filename=X_RED)
GAME_BOARD[str(1)+str(col)].update(image_filename=X_RED)
GAME_BOARD[str(2)+str(col)].update(image_filename=X_RED)
else:
GAME_BOARD[str(0)+str(col)].update(image_filename=O_RED)
GAME_BOARD[str(1)+str(col)].update(image_filename=O_RED)
GAME_BOARD[str(2)+str(col)].update(image_filename=O_RED)
else:
if MAIN_DIAGONAL_IS_WINNER:
if GAME_PROGRESS_ARRAY[1][1] == 'X':
GAME_BOARD[str(0)+str(0)].update(image_filename=X_RED)
GAME_BOARD[str(1)+str(1)].update(image_filename=X_RED)
GAME_BOARD[str(2)+str(2)].update(image_filename=X_RED)
else:
GAME_BOARD[str(0)+str(0)].update(image_filename=O_RED)
GAME_BOARD[str(1)+str(1)].update(image_filename=O_RED)
GAME_BOARD[str(2)+str(2)].update(image_filename=O_RED)
else:
if GAME_PROGRESS_ARRAY[1][1] == 'X':
GAME_BOARD[str(0)+str(2)].update(image_filename=X_RED)
GAME_BOARD[str(1)+str(1)].update(image_filename=X_RED)
GAME_BOARD[str(2)+str(0)].update(image_filename=X_RED)
else:
GAME_BOARD[str(0)+str(2)].update(image_filename=O_RED)
GAME_BOARD[str(1)+str(1)].update(image_filename=O_RED)
GAME_BOARD[str(2)+str(0)].update(image_filename=O_RED)
def is_winning():
'''evaluated the current state of the gameboard
and checks if there is a winner currently.'''
global GAME_PROGRESS_ARRAY
global CHECK_FOR_WINNER
global MAIN_DIAGONAL_IS_WINNER
# check for the row wise sequence.
for row in range(ROWS):
if is_row_column_diagonal_complete(row_col_num=row, is_row=True):
if GAME_PROGRESS_ARRAY[row][0] == GAME_PROGRESS_ARRAY[row][1] == GAME_PROGRESS_ARRAY[row][2]:
mark_the_winner(row_is_winner=True, row_column_index=row)
display_winner_and_continue(winning_marker=GAME_PROGRESS_ARRAY[row][0])
CHECK_FOR_WINNER = False
return True
# check for the column wise sequence.
for col in range(COLS):
if is_row_column_diagonal_complete(row_col_num=col, is_row=False):
if GAME_PROGRESS_ARRAY[0][col] == GAME_PROGRESS_ARRAY[1][col] == GAME_PROGRESS_ARRAY[2][col]:
mark_the_winner(row_is_winner=False, row_column_index=col)
display_winner_and_continue(winning_marker=GAME_PROGRESS_ARRAY[0][col])
CHECK_FOR_WINNER = False
return True
# check for the 2 diagonals for a winning sequence.
if is_row_column_diagonal_complete(is_diagonal=True):
if GAME_PROGRESS_ARRAY[0][0] == GAME_PROGRESS_ARRAY[1][1] == GAME_PROGRESS_ARRAY[2][2]:
MAIN_DIAGONAL_IS_WINNER = True
mark_the_winner(row_column_index=-1, row_is_winner=False, diagonal_is_winner=True)
display_winner_and_continue(winning_marker=GAME_PROGRESS_ARRAY[1][1])
CHECK_FOR_WINNER = False
return True
elif GAME_PROGRESS_ARRAY[2][0] == GAME_PROGRESS_ARRAY[1][1] == GAME_PROGRESS_ARRAY[0][2]:
mark_the_winner(row_column_index=-1, row_is_winner=False, diagonal_is_winner=True)
display_winner_and_continue(winning_marker=GAME_PROGRESS_ARRAY[1][1])
CHECK_FOR_WINNER = False
return True
def display_winner_and_continue(winning_marker: str):
'''display the winner of the current board.'''
global INIT_WINDOW
if winning_marker == PLAYER1_MARKER:
continue_with_same_player = sg.PopupYesNo('The Winner is ' + PLAYER1_NAME + '.\nDo you want to play another game with the current players?',
title='Board Winner!', text_color='darkblue', icon=GAME_ICON,
grab_anywhere=True, font=('Blackadder ITC', 20))
elif winning_marker == PLAYER2_MARKER:
continue_with_same_player = sg.PopupYesNo('The Winner is ' + PLAYER2_NAME + '.\nDo you want to play another game with the current players?',
title='Board Winner!', text_color='darkblue', icon=GAME_ICON,
grab_anywhere=True, font=('Blackadder ITC', 20))
if continue_with_same_player == 'Yes':
GAME_BOARD.close()
initialize_game_board()
elif continue_with_same_player == 'No' and not INIT_WINDOW:
GAME_BOARD.hide()
INIT_WINDOW = init_game_window()
def init_game_window():
'''Initializes and creates the game options window.'''
init_game_layout = [[sg.Text('Player 1 Name: ', size=(12, 1)),
sg.InputText('', key='-P1_NAME-')],
[sg.Text('Player 2 Name: ', size=(12, 1)),
sg.InputText('', key='-P2_NAME-')],
[sg.Frame(layout=[[sg.Radio('X', group_id="P1_PREF", key='-P1_MARK-',
default=True, size=(10, 1)),
sg.Radio('O', group_id="P1_PREF", key='-P2_MARK-')]],
title='Player 1 Preference', relief=sg.RELIEF_GROOVE,
tooltip='Set Player 1 Preference')],
[sg.Button("Start Game", key='-START-'), sg.Button('Exit', key='-EXIT-')]]
return sg.Window('Tic Tac Toe Options', icon=GAME_ICON, finalize=True).Layout(init_game_layout)
INIT_WINDOW = init_game_window()
while True:
EVENT, VALUES = INIT_WINDOW.Read()
if EVENT in (sg.WINDOW_CLOSED, '-EXIT-'):
break
if EVENT not in (None, '-EXIT-'):
if EVENT == '-START-':
if VALUES['-P1_NAME-'] == '' and VALUES['-P2_NAME-'] == '':
sg.popup_ok("Error initializing players name. Enter both the players name before proceeding.",
title='Tic Tac Toe', icon=GAME_ICON)
else:
PLAYER1_NAME, PLAYER2_NAME, PLAYER1_X, PLAYER2_X = VALUES['-P1_NAME-'], VALUES['-P2_NAME-'], VALUES['-P1_MARK-'], VALUES['-P2_MARK-']
# Get the PLayer Markers to start with.
if PLAYER1_X:
PLAYER1_MARKER, PLAYER2_MARKER = ("X", "O")
else:
PLAYER1_MARKER, PLAYER2_MARKER = ("O", "X")
# set the flag to start the game as once the
# window is closed the event loop will be destroyed.
if EVENT == '-START-':
if VALUES['-P1_NAME-'] is not None and VALUES['-P2_NAME-'] is not None:
START_GAME = True
# Close the options window and start the game.
INIT_WINDOW.close()
INIT_WINDOW.close()
INIT_WINDOW = None
STEP_COUNTER: int = 0
PLAYER_SWITCH = True
PLAYER1_MARKED_CELLS: list = []
PLAYER2_MARKED_CELLS: list = []
if START_GAME:
def initialize_game_board():
'''initialize the game board.'''
GAME_BOARD_LAYOUT = [[sg.Text('Player 1: ' + PLAYER1_NAME, key='-P1-', text_color='darkblue')],
[sg.Text('Player 2: ' + PLAYER2_NAME, key='-P2-', text_color='white')],
[sg.Text(PLAYER1_NAME + "'s Marker: " + PLAYER1_MARKER)],
[sg.Text(PLAYER2_NAME + "'s Marker: " + PLAYER2_MARKER)],
[sg.Text('')]]
GAME_BOARD_LAYOUT += [[sg.Button(' ', size=(8, 4), key=str(j)+str(i))
for i in range(3)] for j in range(3)]
BOARD = sg.Window('Tic Tac Toe', icon=GAME_ICON).Layout(GAME_BOARD_LAYOUT)
return BOARD
GAME_BOARD = initialize_game_board()
while True:
GAME_EVENT, GAME_VALUES = GAME_BOARD.Read()
if GAME_EVENT in (sg.WINDOW_CLOSED, 'Exit'):
break
CURRENT_MARKER = GAME_BOARD[GAME_EVENT].get_text()
GAME_BOARD[GAME_EVENT].update(PLAYER1_MARKER if CURRENT_MARKER == ' ' and\
PLAYER_SWITCH is True else PLAYER2_MARKER if CURRENT_MARKER == ' ' and\
PLAYER_SWITCH is False else PLAYER1_MARKER if CURRENT_MARKER == PLAYER1_MARKER
else PLAYER2_MARKER if CURRENT_MARKER == PLAYER2_MARKER else ' ')
# Change the color of the player text to mark
# the next player's turn. 'DarkBlue indicates
# the player who should make the next move.'
# Additionally, Once the player has made a move,
# disable the button.
if GAME_BOARD[GAME_EVENT].get_text() == PLAYER1_MARKER:
# increase the step counter.
# The minimum number of steps required to win the game is 5
STEP_COUNTER += 1
PLAYER_SWITCH = False
PLAYER1_MARKED_CELLS.append(GAME_EVENT)
GAME_BOARD['-P1-'].update(text_color='white')
GAME_BOARD['-P2-'].update(text_color='darkblue')
if PLAYER1_MARKER == 'X':
GAME_BOARD[GAME_EVENT].update(image_filename=X_IMAGE)
else:
GAME_BOARD[GAME_EVENT].update(image_filename=O_IMAGE)
GAME_BOARD[GAME_EVENT].update(disabled=True)
progress_game(GAME_EVENT, PLAYER1_MARKER)
if CURENT_BOARD_WON:
break
elif GAME_BOARD[GAME_EVENT].get_text() == PLAYER2_MARKER:
# increase the step counter.
# The minimum number of steps required to win the game is 5
STEP_COUNTER += 1
PLAYER_SWITCH = True
PLAYER2_MARKED_CELLS.append(GAME_EVENT)
GAME_BOARD['-P1-'].update(text_color='darkblue')
GAME_BOARD['-P2-'].update(text_color='white')
if PLAYER2_MARKER == 'X':
GAME_BOARD[GAME_EVENT].update(image_filename=X_IMAGE)
else:
GAME_BOARD[GAME_EVENT].update(image_filename=O_IMAGE)
GAME_BOARD[GAME_EVENT].update(disabled=True)
progress_game(GAME_EVENT, PLAYER2_MARKER)
if CURENT_BOARD_WON:
break
# The minimum number of steps required
# to win the game board is 5.
if STEP_COUNTER == 4:
CHECK_FOR_WINNER = True
# GAME_BOARD.close()