1- import numpy as np
2-
3-
4- grid = np .full ((3 ,3 ), '-' )
5- cells = {'0' : (0 ,0 ), '1' : (0 ,1 ), '2' : (0 ,2 ),
6- '3' : (1 ,0 ), '4' : (1 ,1 ), '5' : (1 ,2 ),
7- '6' : (2 ,0 ), '7' : (2 ,1 ), '8' : (2 ,2 )}
8-
9- diagonal_1 = [0 ,1 ,2 ], [0 ,1 ,2 ] # main diagonal
10- diagonal_2 = [0 ,1 ,2 ], [2 ,1 ,0 ] # reverse diagonal
11- ver_hor_lines = {'v1' : grid [:,0 ], 'v2' : grid [:,1 ], 'v3' : grid [:,2 ], # verticals
12- 'h1' : grid [0 ,:], 'h2' : grid [1 ,:], 'h3' : grid [2 ,:]} # horizontals
13-
14- player = ''
15- turn = 1
16- free_spots = ['0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' ]
17- spot = ''
18-
19- while True :
20- # printing the grid
21- for el in grid :
22- print (' ' .join (el .astype (str )))
23-
24- # check if player won
25- if np .all (grid [diagonal_1 ] == player ) or np .all (grid [diagonal_2 ] == player ):
26- print (f"player { player .upper ()} , you won !" )
27- quit ()
28- for line in ver_hor_lines :
29- if np .all (ver_hor_lines [line ] == player ):
30- print (f"player { player .upper ()} , you won !" )
31- quit ()
32- print ('available positions: {}' .format (' ' .join (free_spots )))
33-
34- # check if game ended as a tie
35- if not free_spots :
36- print ('END GAME: TIE' )
37- break
38-
39- # update the player
40- if turn % 2 == 0 :
41- player = 'o'
42- else :
43- player = 'x'
44-
45- # ask the input
46- spot = input ('player {}, enter a position: ' .format (player .upper ()))
47- # entering 'out' will end the game at anytime
48- if spot == 'out' :
49- quit ()
50- # check if input is valid
51- if spot in free_spots :
52- # update the grid
53- position = cells [spot ]
54- grid [position ] = player
55- free_spots .remove (spot )
56- turn += 1
57- else :
58- print ('not valid. Enter again.' )
59-
60- print ()
1+ import os
2+ import sys
3+ import ttkbootstrap as ttk
4+
5+ from tkinter import IntVar
6+ from widgets import BoardGame , BoardScore
7+ from configuration import (
8+ # layout
9+ MAIN_SIZE , BOARD_GAME , BOARD_SCORE , RESET_BUTTON ,
10+ # style
11+ FRAME_STYLE_SCORE , FRAME_STYLE_GAME , BUTTON_BOARD_STYLE , BUTTON_RESET_STYLE , LABEL_SCORE_STYLE ,
12+ )
13+
14+ # import the modules for windows (it works only on windows)
15+
16+ try :
17+ from ctypes import windll , byref , sizeof , c_int
18+ except Exception :
19+ pass
20+
21+
22+ def path_resource (relative_path : str ) -> str :
23+ """
24+ it take the relative path and return the absolute path of the file from your system, is used for making the
25+ app into a exe file for window
26+
27+ """
28+ try :
29+ base_path : str = sys ._MEIPASS
30+ except Exception :
31+ base_path = os .path .abspath ('.' )
32+ return os .path .join (base_path , relative_path )
33+
34+
35+ class TicTacToe (ttk .Window ):
36+
37+ player_1 : IntVar
38+ player_2 : IntVar
39+ tie_score : IntVar
40+
41+ def __init__ (self ):
42+ super ().__init__ ()
43+
44+ self .bind ('<Alt-s>' , lambda event : self .destroy ())
45+ self .title ('' )
46+ self .set_emtpy_icon ()
47+ self .set_title_bar_color ()
48+ self .set_window_size (width = MAIN_SIZE [0 ], height = MAIN_SIZE [1 ])
49+
50+ # set up the style
51+ self .Style = ttk .Style (theme = 'darkly' )
52+
53+ # style for the score/ board_score
54+ self .Style .configure (
55+
56+ background = BOARD_SCORE ['BACKGROUND' ],
57+ style = FRAME_STYLE_SCORE ,
58+
59+ )
60+
61+ self .Style .configure (
62+
63+ background = BOARD_GAME ['BACKGROUND_FRAME' ],
64+ style = FRAME_STYLE_GAME ,
65+
66+ )
67+
68+ self .Style .configure (
69+
70+ background = BOARD_GAME ['BACKGROUND' ],
71+ bordercolor = BOARD_GAME ['BORDER_COLOR' ],
72+ borderthickness = BOARD_GAME ['BORDER_THICKNESS' ],
73+ borderwidth = BOARD_GAME ['BORDER_WIDTH' ],
74+ font = (BOARD_GAME ['FONT' ], BOARD_GAME ['FONT_SIZE' ]),
75+ justify = BOARD_GAME ['JUSTIFY' ],
76+ relief = BOARD_GAME ['RELIEF' ],
77+ style = BUTTON_BOARD_STYLE ,
78+
79+ )
80+
81+ self .Style .map (
82+
83+ style = BUTTON_BOARD_STYLE ,
84+ foreground = [
85+ ('active' , BOARD_GAME ['TEXT_COLOR_ACTIVE' ]),
86+ ('disabled' , BOARD_GAME ['TEXT_COLOR_DISABLED' ])
87+ ],
88+ background = [
89+ ('active' , BOARD_GAME ['HOVER_COLOR_ACTIVE' ]),
90+ ('disabled' , BOARD_GAME ['HOVER_COLOR_DISABLED' ])
91+ ]
92+ )
93+
94+ self .Style .configure (
95+
96+ background = RESET_BUTTON ['BACKGROUND' ],
97+ bordercolor = RESET_BUTTON ['BORDER_COLOR' ],
98+ borderthickness = RESET_BUTTON ['BORDER_THICKNESS' ],
99+ borderwidth = RESET_BUTTON ['BORDER_WIDTH' ],
100+ font = (RESET_BUTTON ['FONT' ], RESET_BUTTON ['SIZE' ]),
101+ justify = RESET_BUTTON ['JUSTIFY' ],
102+ relief = RESET_BUTTON ['RELIEF' ],
103+ style = BUTTON_RESET_STYLE ,
104+
105+ )
106+ self .Style .map (
107+
108+ style = BUTTON_RESET_STYLE ,
109+ foreground = [
110+ ('active' , RESET_BUTTON ['TEXT_COLOR_ACTIVE' ]),
111+ ('disabled' , RESET_BUTTON ['TEXT_COLOR_DISABLED' ])
112+ ],
113+ background = [
114+ ('active' , RESET_BUTTON ['HOVER_COLOR_ACTIVE' ]),
115+ ('disabled' , RESET_BUTTON ['HOVER_COLOR_DISABLED' ])]
116+
117+ )
118+
119+ self .Style .configure (
120+
121+ background = BOARD_SCORE ['BACKGROUND' ],
122+ font = (BOARD_SCORE ['FONT' ], BOARD_SCORE ['FONT_SIZE' ]),
123+ foreground = BOARD_SCORE ['TEXT_COLOR' ],
124+ style = LABEL_SCORE_STYLE ,
125+
126+ )
127+
128+ # set player data
129+ self .player_1 = ttk .IntVar (value = 0 )
130+ self .player_2 = ttk .IntVar (value = 0 )
131+ self .tie_score = ttk .IntVar (value = 0 )
132+
133+ # set widgets
134+ self .board_game = BoardGame (
135+
136+ parent = self ,
137+ style_cells = BUTTON_BOARD_STYLE ,
138+ style_frame = FRAME_STYLE_GAME ,
139+ player_1 = self .player_1 ,
140+ tie = self .tie_score ,
141+ player_2 = self .player_2 ,
142+
143+ )
144+
145+ self .board_score = BoardScore (
146+
147+ parent = self ,
148+ style_labels = LABEL_SCORE_STYLE ,
149+ style_frame = FRAME_STYLE_SCORE ,
150+ style_button = BUTTON_RESET_STYLE ,
151+ player_1 = self .player_1 ,
152+ tie = self .tie_score ,
153+ player_2 = self .player_2 ,
154+ function = self .clean_board
155+
156+ )
157+
158+ # run
159+ self .mainloop ()
160+
161+ def clean_board (self ):
162+ """
163+ It clean the board and reset the score
164+ """
165+ self .board_game .clean_board ()
166+ self .player_1 .set (0 )
167+ self .player_2 .set (0 )
168+ self .tie_score .set (0 )
169+
170+ def set_emtpy_icon (self ) -> None :
171+ """
172+ It sets the icon to one empty from the title bar
173+
174+ """
175+ try :
176+ path_image : str = path_resource ('media/empty.ico' )
177+ self .iconbitmap (path_image )
178+ except Exception :
179+ pass
180+
181+ def set_window_size (self , width : int , height : int ) -> None :
182+ """
183+ It adjust the window size to be in the center of the screen
184+
185+ """
186+ left = int (self .winfo_screenwidth () / 2 - width / 2 )
187+ top = int (self .winfo_screenheight () / 2 - height / 2 )
188+ self .geometry (f'{ width } x{ height } +{ left } +{ top } ' )
189+
190+ def set_title_bar_color (self ) -> None :
191+ """
192+ It works only on Windows, not on GNU/Linux and macOS.
193+ """
194+ try :
195+ HWND = windll .user32 .GetParent (self .winfo_id ())
196+ DWMWA_ATTRIBUTE : int = 35 # target the title bar
197+ color_tile : int = 0x00030303
198+ windll .dwmapi .DwmSetWindowAttribute (HWND , DWMWA_ATTRIBUTE , byref (c_int (color_tile )), sizeof (c_int ))
199+ except Exception :
200+ pass
201+
202+
203+ if __name__ == '__main__' :
204+
205+ # starts the game
206+ TicTacToe ()
0 commit comments