Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 35c05d9

Browse files
Update to the initial version.
tic-tac-toe Game using PySimpleGUI An update to the initial version where once the current board is Won/Complete, a Yes/No popup would let the players decide if they would want to continue playing. YES would let them continue playing the game. NO would take the control back to the game initialization window to start a fresh session with new players. Design pattern used for implementing multiple windows Using read_all_windows() @ https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Design_Pattern_Multiple_Windows1.py Only 1 window at a time is visible/active on the screen. 1. INIT_WINDOW opens the GAME_BOARD screen. 2. Closing the GAME_BOARD exits the game and returns to the INIT_WINDOW with the details from the previous session intact. 3. Exiting the INIT_WINDOW would exit the game.
1 parent 9525f2d commit 35c05d9

File tree

1 file changed

+386
-0
lines changed

1 file changed

+386
-0
lines changed
Lines changed: 386 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,386 @@
1+
"""
2+
tic-tac-toe Game using PySimpleGUI
3+
4+
An update to the initial version where once the current board is
5+
Won/Complete, a Yes/No popup would let the players decide
6+
if they would want to continue playing.
7+
8+
YES would let them continue playing the game.
9+
NO would take the control back to the game
10+
initialization window to start a fresh session with
11+
new players.
12+
13+
Design pattern used for implementing multiple windows
14+
Using read_all_windows() @
15+
https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Design_Pattern_Multiple_Windows1.py
16+
17+
Only 1 window at a time is visible/active on the screen.
18+
19+
1. INIT_WINDOW opens the GAME_BOARD screen.
20+
21+
2. Closing the GAME_BOARD exits the game and
22+
returns to the INIT_WINDOW with the details
23+
from the previous session intact.
24+
25+
3. Exiting the INIT_WINDOW would exit the game.
26+
27+
"""
28+
29+
import os
30+
import numpy as np
31+
import PySimpleGUI as sg
32+
33+
INIT_WINDOW = None
34+
GAME_BOARD = None
35+
36+
CURRENT_WORKING_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
37+
X_IMAGE = CURRENT_WORKING_DIRECTORY + '\\X.png'
38+
X_RED = CURRENT_WORKING_DIRECTORY + '\\X_Red.png'
39+
O_IMAGE = CURRENT_WORKING_DIRECTORY + '\\O.png'
40+
O_RED = CURRENT_WORKING_DIRECTORY + '\\O_Red.png'
41+
GAME_ICON = CURRENT_WORKING_DIRECTORY + '\\tictactoe.ico'
42+
43+
START_GAME: bool = False
44+
CHECK_FOR_WINNER: bool = False
45+
MAIN_DIAGONAL_IS_WINNER: bool = False
46+
CURENT_BOARD_WON: bool = False
47+
CONTINUE_WITH_NEXT_GAME: str = ''
48+
STEP_COUNTER: int = 0
49+
PLAYER_SWITCH = True
50+
51+
PLAYER1_NAME: str = ''
52+
PLAYER1_MARKER: str = ''
53+
PLAYER2_NAME: str = ''
54+
PLAYER2_MARKER: str = ''
55+
56+
ROWS, COLS = (3, 3)
57+
GAME_PROGRESS_ARRAY = [['' for i in range(COLS)] for j in range(ROWS)]
58+
GAME_PROGRESS_ARRAY = np.array(GAME_PROGRESS_ARRAY, dtype=str)
59+
# WINNING_PATTERNS: list = [['00', '10', '20'], ['01', '11', '21'], ['02', '12', '22'], # rows
60+
# ['00', '01', '02'], ['10', '11', '12'], ['20', '21', '22'], # columns
61+
# ['00', '11', '22'], ['00', '11', '02']] # diagonals
62+
63+
def split(word):
64+
'''splits a string to constituents chars.'''
65+
return [int(char) for char in word]
66+
67+
def progress_game(key: str, player_marker: str):
68+
'''populated the 'GAME_PROGRESS_ARRAY' and
69+
checks for is winning condition.'''
70+
71+
global GAME_PROGRESS_ARRAY
72+
global CURENT_BOARD_WON
73+
74+
continue_with_next_game: str = ''
75+
76+
row, column = split(key)
77+
GAME_PROGRESS_ARRAY[row][column] = player_marker
78+
79+
if CHECK_FOR_WINNER:
80+
game_won, winning_marker = is_winning()
81+
if game_won:
82+
CURENT_BOARD_WON = True
83+
continue_with_next_game = display_winner_and_continue(winning_marker=winning_marker)
84+
85+
return continue_with_next_game
86+
87+
def is_row_column_diagonal_complete(row_col_num: int = -1, is_row: bool = True, is_diagonal: bool = False):
88+
'''checks if the given row or column is complete
89+
to proceed with a winner.'''
90+
91+
if is_diagonal is False and row_col_num != -1:
92+
if is_row:
93+
row = row_col_num
94+
if GAME_PROGRESS_ARRAY[row][0] != '' and \
95+
GAME_PROGRESS_ARRAY[row][1] != '' and \
96+
GAME_PROGRESS_ARRAY[row][2] != '':
97+
return True
98+
else:
99+
return False
100+
else:
101+
col = row_col_num
102+
if GAME_PROGRESS_ARRAY[0][col] != '' and \
103+
GAME_PROGRESS_ARRAY[1][col] != '' and \
104+
GAME_PROGRESS_ARRAY[2][col] != '':
105+
return True
106+
else:
107+
return False
108+
else:
109+
if GAME_PROGRESS_ARRAY[0][0] != '' and \
110+
GAME_PROGRESS_ARRAY[1][1] != '' and \
111+
GAME_PROGRESS_ARRAY[2][2] != '':
112+
return True
113+
elif GAME_PROGRESS_ARRAY[2][0] != '' and \
114+
GAME_PROGRESS_ARRAY[1][1] != '' and \
115+
GAME_PROGRESS_ARRAY[0][2] != '':
116+
return True
117+
118+
119+
def mark_the_winner(row_is_winner: bool, row_column_index: int = -1, diagonal_is_winner: bool = False):
120+
'''marks the winner row/column by updating
121+
the button row/column.'''
122+
123+
if not diagonal_is_winner and row_column_index != -1:
124+
if row_is_winner:
125+
row = row_column_index
126+
if GAME_PROGRESS_ARRAY[row][0] == 'X':
127+
GAME_BOARD.Element(str(row)+str(0)).update(image_filename=X_RED)
128+
GAME_BOARD.Element(str(row)+str(1)).update(image_filename=X_RED)
129+
GAME_BOARD.Element(str(row)+str(2)).update(image_filename=X_RED)
130+
else:
131+
GAME_BOARD.Element(str(row)+str(0)).update(image_filename=O_RED)
132+
GAME_BOARD.Element(str(row)+str(1)).update(image_filename=O_RED)
133+
GAME_BOARD.Element(str(row)+str(2)).update(image_filename=O_RED)
134+
else:
135+
col = row_column_index
136+
if GAME_PROGRESS_ARRAY[0][col] == 'X':
137+
GAME_BOARD.Element(str(0)+str(col)).update(image_filename=X_RED)
138+
GAME_BOARD.Element(str(1)+str(col)).update(image_filename=X_RED)
139+
GAME_BOARD.Element(str(2)+str(col)).update(image_filename=X_RED)
140+
else:
141+
GAME_BOARD.Element(str(0)+str(col)).update(image_filename=O_RED)
142+
GAME_BOARD.Element(str(1)+str(col)).update(image_filename=O_RED)
143+
GAME_BOARD.Element(str(2)+str(col)).update(image_filename=O_RED)
144+
else:
145+
if MAIN_DIAGONAL_IS_WINNER:
146+
if GAME_PROGRESS_ARRAY[1][1] == 'X':
147+
GAME_BOARD.Element(str(0)+str(0)).update(image_filename=X_RED)
148+
GAME_BOARD.Element(str(1)+str(1)).update(image_filename=X_RED)
149+
GAME_BOARD.Element(str(2)+str(2)).update(image_filename=X_RED)
150+
else:
151+
GAME_BOARD.Element(str(0)+str(0)).update(image_filename=O_RED)
152+
GAME_BOARD.Element(str(1)+str(1)).update(image_filename=O_RED)
153+
GAME_BOARD.Element(str(2)+str(2)).update(image_filename=O_RED)
154+
else:
155+
if GAME_PROGRESS_ARRAY[1][1] == 'X':
156+
GAME_BOARD.Element(str(0)+str(2)).update(image_filename=X_RED)
157+
GAME_BOARD.Element(str(1)+str(1)).update(image_filename=X_RED)
158+
GAME_BOARD.Element(str(2)+str(0)).update(image_filename=X_RED)
159+
else:
160+
GAME_BOARD.Element(str(0)+str(2)).update(image_filename=O_RED)
161+
GAME_BOARD.Element(str(1)+str(1)).update(image_filename=O_RED)
162+
GAME_BOARD.Element(str(2)+str(0)).update(image_filename=O_RED)
163+
164+
def is_winning():
165+
'''evaluated the current state of the gameboard
166+
and checks if there is a winner currently.'''
167+
168+
global GAME_PROGRESS_ARRAY
169+
global CHECK_FOR_WINNER
170+
global MAIN_DIAGONAL_IS_WINNER
171+
172+
# check for the row wise sequence.
173+
for row in range(ROWS):
174+
if is_row_column_diagonal_complete(row_col_num=row, is_row=True):
175+
if GAME_PROGRESS_ARRAY[row][0] == GAME_PROGRESS_ARRAY[row][1] == GAME_PROGRESS_ARRAY[row][2]:
176+
mark_the_winner(row_is_winner=True, row_column_index=row)
177+
# continue_game = display_winner_and_continue(winning_marker=GAME_PROGRESS_ARRAY[row][0])
178+
CHECK_FOR_WINNER = False
179+
return True, GAME_PROGRESS_ARRAY[row][0]
180+
181+
# check for the column wise sequence.
182+
for col in range(COLS):
183+
if is_row_column_diagonal_complete(row_col_num=col, is_row=False):
184+
if GAME_PROGRESS_ARRAY[0][col] == GAME_PROGRESS_ARRAY[1][col] == GAME_PROGRESS_ARRAY[2][col]:
185+
mark_the_winner(row_is_winner=False, row_column_index=col)
186+
# display_winner_and_continue(winning_marker=GAME_PROGRESS_ARRAY[0][col])
187+
CHECK_FOR_WINNER = False
188+
return True, GAME_PROGRESS_ARRAY[0][col]
189+
190+
# check for the 2 diagonals for a winning sequence.
191+
if is_row_column_diagonal_complete(is_diagonal=True):
192+
if GAME_PROGRESS_ARRAY[0][0] == GAME_PROGRESS_ARRAY[1][1] == GAME_PROGRESS_ARRAY[2][2]:
193+
MAIN_DIAGONAL_IS_WINNER = True
194+
mark_the_winner(row_column_index=-1, row_is_winner=False, diagonal_is_winner=True)
195+
# display_winner_and_continue(winning_marker=GAME_PROGRESS_ARRAY[1][1])
196+
CHECK_FOR_WINNER = False
197+
return True, GAME_PROGRESS_ARRAY[1][1]
198+
199+
elif GAME_PROGRESS_ARRAY[2][0] == GAME_PROGRESS_ARRAY[1][1] == GAME_PROGRESS_ARRAY[0][2]:
200+
mark_the_winner(row_column_index=-1, row_is_winner=False, diagonal_is_winner=True)
201+
# display_winner_and_continue(winning_marker=GAME_PROGRESS_ARRAY[1][1])
202+
CHECK_FOR_WINNER = False
203+
return True, GAME_PROGRESS_ARRAY[1][1]
204+
205+
return False, ''
206+
207+
def display_winner_and_continue(winning_marker: str):
208+
'''display the winner of the current board.'''
209+
210+
if winning_marker == PLAYER1_MARKER:
211+
popup_result = sg.PopupYesNo('The Winner is ' + PLAYER1_NAME + '.\nDo you want to play another game with the current players?',
212+
title='Board Winner!', text_color='darkblue', icon=GAME_ICON,
213+
grab_anywhere=True, font=('Blackadder ITC', 20))
214+
elif winning_marker == PLAYER2_MARKER:
215+
popup_result = sg.PopupYesNo('The Winner is ' + PLAYER2_NAME + '.\nDo you want to play another game with the current players?',
216+
title='Board Winner!', text_color='darkblue', icon=GAME_ICON,
217+
grab_anywhere=True, font=('Blackadder ITC', 20))
218+
219+
return popup_result
220+
221+
def init_game_window():
222+
'''Initializes and creates the game options window.'''
223+
init_game_layout = [[sg.Text('Player 1 Name: ', size=(12, 1)),
224+
sg.InputText('', key='-P1_NAME-')],
225+
[sg.Text('Player 2 Name: ', size=(12, 1)),
226+
sg.InputText('', key='-P2_NAME-')],
227+
[sg.Frame(layout=[[sg.Radio('X', group_id="P1_PREF", key='-P1_MARK-',
228+
default=True, size=(10, 1)),
229+
sg.Radio('O', group_id="P1_PREF", key='-P2_MARK-')]],
230+
title='Player 1 Preference', relief=sg.RELIEF_GROOVE,
231+
tooltip='Set Player 1 Preference')],
232+
[sg.Button("Start Game", key='-START-'), sg.Button('Exit', key='-EXIT-')]]
233+
234+
return sg.Window('Tic Tac Toe Options', init_game_layout, icon=GAME_ICON, finalize=True)
235+
236+
def reset_game_board():
237+
'''Resets the current game board and re-initializes all the
238+
game parameters to continue playing the game with the same players.'''
239+
240+
global GAME_PROGRESS_ARRAY
241+
global STEP_COUNTER
242+
global CONTINUE_WITH_NEXT_GAME
243+
global CHECK_FOR_WINNER
244+
global CURENT_BOARD_WON
245+
global GAME_BOARD
246+
global PLAYER_SWITCH
247+
248+
GAME_BOARD = initialize_game_board()
249+
GAME_PROGRESS_ARRAY = [['' for i in range(COLS)] for j in range(ROWS)]
250+
GAME_PROGRESS_ARRAY = np.array(GAME_PROGRESS_ARRAY, dtype=str)
251+
STEP_COUNTER = 0
252+
CHECK_FOR_WINNER = False
253+
CONTINUE_WITH_NEXT_GAME = ''
254+
CURENT_BOARD_WON = False
255+
PLAYER_SWITCH = True
256+
257+
def initialize_game_board():
258+
'''initialize the game board.'''
259+
260+
global PLAYER1_NAME
261+
global PLAYER1_NAME
262+
global PLAYER1_MARKER
263+
global PLAYER2_MARKER
264+
265+
GAME_BOARD_LAYOUT = [[sg.Text('Player 1: ' + PLAYER1_NAME, key='-P1-', text_color='darkblue')],
266+
[sg.Text('Player 2: ' + PLAYER2_NAME, key='-P2-', text_color='white')],
267+
[sg.Text(PLAYER1_NAME + "'s Marker: " + PLAYER1_MARKER)],
268+
[sg.Text(PLAYER2_NAME + "'s Marker: " + PLAYER2_MARKER)],
269+
[sg.Text('')]]
270+
271+
GAME_BOARD_LAYOUT += [[sg.Button(' ', size=(8, 4), key=str(j)+str(i))
272+
for i in range(3)] for j in range(3)]
273+
274+
BOARD = sg.Window('Tic Tac Toe', GAME_BOARD_LAYOUT, icon=GAME_ICON, finalize=True)
275+
276+
BOARD.TKroot.protocol("WM_DESTROY_WINDOW", lambda: BOARD.write_event_value("WIN_CLOSE", ()))
277+
BOARD.TKroot.protocol("WM_DELETE_WINDOW", lambda: BOARD.write_event_value("WIN_CLOSE", ()))
278+
279+
return BOARD
280+
281+
# Design pattern 1 - First window does not remain active
282+
GAME_BOARD = None
283+
INIT_WINDOW = init_game_window()
284+
285+
while True:
286+
287+
WINDOW, EVENT, VALUES = sg.read_all_windows()
288+
289+
if EVENT in (sg.WIN_CLOSED, '-EXIT-') and WINDOW == INIT_WINDOW:
290+
break
291+
292+
if EVENT == '-START-' and not GAME_BOARD:
293+
294+
if VALUES['-P1_NAME-'] == '' and VALUES['-P2_NAME-'] == '':
295+
sg.popup_ok("Error initializing players name. Enter both the players name before proceeding.",
296+
title='Tic Tac Toe', icon=GAME_ICON)
297+
298+
else:
299+
PLAYER1_NAME, PLAYER2_NAME, PLAYER1_X, PLAYER2_X = VALUES['-P1_NAME-'], VALUES['-P2_NAME-'], VALUES['-P1_MARK-'], VALUES['-P2_MARK-']
300+
301+
# Get the PLayer Markers to start with.
302+
if PLAYER1_X:
303+
PLAYER1_MARKER, PLAYER2_MARKER = ("X", "O")
304+
else:
305+
PLAYER1_MARKER, PLAYER2_MARKER = ("O", "X")
306+
307+
# set the flag to start the game as once the
308+
# window is closed the event loop will be destroyed.
309+
if EVENT == '-START-':
310+
if VALUES['-P1_NAME-'] is not None and VALUES['-P2_NAME-'] is not None:
311+
START_GAME = True
312+
# Close the options window and start the game.
313+
INIT_WINDOW.close()
314+
GAME_BOARD = initialize_game_board()
315+
316+
if WINDOW == GAME_BOARD and (EVENT in ('WIN_CLOSE', 'Exit')):
317+
GAME_BOARD.close()
318+
GAME_BOARD = None
319+
INIT_WINDOW = init_game_window()
320+
321+
if START_GAME:
322+
323+
if EVENT not in ('-START-', 'WIN_CLOSE'):
324+
CURRENT_MARKER = GAME_BOARD.Element(EVENT).get_text()
325+
GAME_BOARD.Element(EVENT).update(PLAYER1_MARKER if CURRENT_MARKER == ' ' and\
326+
PLAYER_SWITCH is True else PLAYER2_MARKER if CURRENT_MARKER == ' ' and\
327+
PLAYER_SWITCH is False else PLAYER1_MARKER if CURRENT_MARKER == PLAYER1_MARKER
328+
else PLAYER2_MARKER if CURRENT_MARKER == PLAYER2_MARKER else ' ')
329+
330+
# Change the color of the player text to mark
331+
# the next player's turn. 'DarkBlue indicates
332+
# the player who should make the next move.'
333+
# Additionally, Once the player has made a move,
334+
# disable the button.
335+
if GAME_BOARD.Element(EVENT).get_text() == PLAYER1_MARKER:
336+
# increase the step counter.
337+
# The minimum number of steps required to win the game is 5
338+
STEP_COUNTER += 1
339+
PLAYER_SWITCH = False
340+
341+
GAME_BOARD.Element('-P1-').update(text_color='white')
342+
GAME_BOARD.Element('-P2-').update(text_color='darkblue')
343+
344+
if PLAYER1_MARKER == 'X':
345+
GAME_BOARD.Element(EVENT).update(image_filename=X_IMAGE)
346+
else:
347+
GAME_BOARD.Element(EVENT).update(image_filename=O_IMAGE)
348+
349+
GAME_BOARD.Element(EVENT).update(disabled=True)
350+
351+
CONTINUE_WITH_NEXT_GAME = progress_game(EVENT, PLAYER1_MARKER)
352+
if CONTINUE_WITH_NEXT_GAME == 'Yes':
353+
GAME_BOARD.Close()
354+
reset_game_board()
355+
elif CONTINUE_WITH_NEXT_GAME == 'No':
356+
GAME_BOARD.Close()
357+
INIT_WINDOW = init_game_window()
358+
359+
elif GAME_BOARD.Element(EVENT).get_text() == PLAYER2_MARKER:
360+
# increase the step counter.
361+
# The minimum number of steps required to win the game is 5
362+
STEP_COUNTER += 1
363+
PLAYER_SWITCH = True
364+
365+
GAME_BOARD.Element('-P1-').update(text_color='darkblue')
366+
GAME_BOARD.Element('-P2-').update(text_color='white')
367+
368+
if PLAYER2_MARKER == 'X':
369+
GAME_BOARD.Element(EVENT).update(image_filename=X_IMAGE)
370+
else:
371+
GAME_BOARD.Element(EVENT).update(image_filename=O_IMAGE)
372+
373+
GAME_BOARD.Element(EVENT).update(disabled=True)
374+
375+
CONTINUE_WITH_NEXT_GAME = progress_game(EVENT, PLAYER2_MARKER)
376+
if CONTINUE_WITH_NEXT_GAME == 'Yes':
377+
GAME_BOARD.Close()
378+
reset_game_board()
379+
elif CONTINUE_WITH_NEXT_GAME == 'No':
380+
GAME_BOARD.Close()
381+
INIT_WINDOW = init_game_window()
382+
383+
# The minimum number of steps required
384+
# to win the game board is 5.
385+
if STEP_COUNTER == 4:
386+
CHECK_FOR_WINNER = True

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /