11import pygame
22import thorpy
33from os import path
4- from tkinter import messagebox , Tk , simpledialog
54from threading import Thread
5+ from tkinter import messagebox , Tk
66from multiprocessing import Process
7- from TicTacToe .Grid import Grid
7+
8+ from TicTacToe import Grid
9+ from TicTacToe import GameException
10+ from TicTacToe import TicTacToeExceptions
811from TicTacToe .util import saveData , displayData
9- from TicTacToe .Players .Player import Player
10- from TicTacToe .Players .AIPlayer import AIPlayer
11- from TicTacToe .Players .RandomAIPlayer import RandomAIPlayer
12- from TicTacToe .Exceptions .TicTacToeExceptions import AlreadyFilledError , WrongTurnError
12+ from TicTacToe import Player , AIPlayer , RandomAIPlayer
1313
1414
1515class Game ():
@@ -21,16 +21,23 @@ def __init__(self, assetsPath, saveToCloud=True):
2121 self .grid = Grid ()
2222 self .AI_DELAY = 1
2323 self .selectAI ()
24- # self.AIPlayer = AIPlayer(2, delay=self.AI_DELAY, name="Minimax AI")
2524
2625 self .COLOR = {
2726 'BLACK' : (000 , 000 , 000 ),
2827 'BLUE' : (145 , 195 , 220 ),
2928 'WHITE' : (255 , 255 , 255 )
3029 }
30+
31+ self .LINE_WIDTH = 10
32+ self .HEADER_HEIGHT = 125
33+ self .SIDE_PADDING = 50
34+ self .GRID_SIZE = 300
35+ self .H_CELLS = 3 # Horizontal Cells in the Grid
36+ self .TOTAL_MOVES = self .H_CELLS * 3
37+ self .CELL_WIDTH = self .GRID_SIZE // self .H_CELLS
3138
32- self .WINDOW_WIDTH = 400
33- self .WINDOW_HEIGHT = 450
39+ self .WINDOW_WIDTH = self . SIDE_PADDING + self . GRID_SIZE + self . SIDE_PADDING
40+ self .WINDOW_HEIGHT = self . HEADER_HEIGHT + self . GRID_SIZE + 25 # Pad the bottom with 25px
3441
3542 self .assets = {}
3643 self .loadAssets (assetsPath )
@@ -76,7 +83,7 @@ def __init__(self, assetsPath, saveToCloud=True):
7683 def selectAI (self ):
7784 try :
7885 AI = self .toggleButtonPool .get_selected ().get_text ()
79- if AI == 'Random AI' : self .AIPlayer = RandomAIPlayer (2 , delay = 0.5 , name = "Random AI" ) # Set to Random AI
86+ if AI == 'Random AI' : self .AIPlayer = RandomAIPlayer (2 , delay = self . AI_DELAY , name = "Random AI" ) # Set to Random AI
8087 else : self .AIPlayer = AIPlayer (2 , delay = self .AI_DELAY , name = "Minimax AI" ) # Set to Minimax AI
8188 except AttributeError :
8289 self .AIPlayer = AIPlayer (2 , delay = self .AI_DELAY , name = "Minimax AI" ) # Set to Minimax AI if any error occurs
@@ -85,8 +92,11 @@ def selectAI(self):
8592 self .AIPlayer = AIPlayer (2 , delay = self .AI_DELAY , name = "Minimax AI" )
8693
8794 def setupUI (self ):
88- analyticsButton = thorpy .make_button (' Analytics ' , func = lambda : Process (target = displayData ).start ())
95+ BUTTON_SIZE = (80 , 28 ) # Default Button Size, 120px wide and 30px high
96+
97+ analyticsButton = thorpy .make_button ('Analytics' , func = lambda : Process (target = displayData ).start ())
8998 analyticsButton .set_main_color (self .COLOR ['BLUE' ])
99+ analyticsButton .set_size (BUTTON_SIZE )
90100
91101 toggleButtons = [thorpy .Togglable ('Minimax AI' ), thorpy .Togglable ('Random AI' )]
92102 self .toggleButtonPool = thorpy .TogglablePool (toggleButtons , first_value = toggleButtons [0 ], always_value = True )
@@ -103,9 +113,8 @@ def setupUI(self):
103113 self .box .update ()
104114
105115
106- @staticmethod
107- def cellPos (y , x ):
108- return (50 + x * 100 ), (130 + y * 100 )
116+ def cellPos (self , y , x ):
117+ return (self .SIDE_PADDING + x * self .CELL_WIDTH ), (self .HEADER_HEIGHT + self .LINE_WIDTH // 2 + y * self .CELL_WIDTH )
109118
110119 def drawCells (self ):
111120 key = [None , self .assets ['X' ], self .assets ['O' ]]
@@ -122,20 +131,20 @@ def saveGameData(self):
122131 'player1' : playerList [1 ].__class__ .__name__ ,
123132 'player2' : playerList [2 ].__class__ .__name__ ,
124133 'startingPlayer' : playerList [self .grid .lastPlayer ].__class__ .__name__ ,
125- 'moves' : 9 - self .grid .movesLeft ,
134+ 'moves' : self . TOTAL_MOVES - self .grid .movesLeft ,
126135 'win' : True if self .grid .win else False ,
127136 'winner' : playerList [self .grid .lastPlayer ].__class__ .__name__ if self .grid .win else '' ,
128- 'draw' : False if self .grid .win and self .grid .movesLeft != 9 else True
137+ 'draw' : False if self .grid .win and self .grid .movesLeft != self . TOTAL_MOVES else True
129138 }
130139 Thread (target = saveData , args = (gameData ,)).start ()
131140
132141 def redraw (self ):
133142 self .window .fill ((255 , 255 , 255 ))
134- pygame .draw .rect (self .window , self .COLOR ['WHITE' ], (50 , 125 , 300 , 300 ))
135- self .window .blit (self .assets ['VLine' ], (50 - 5 + 100 , 125 ))
136- self .window .blit (self .assets ['VLine' ], (50 - 5 + 200 , 125 ))
137- self .window .blit (self .assets ['HLine' ], (50 , 125 + 100 ))
138- self .window .blit (self .assets ['HLine' ], (50 , 125 + 200 ))
143+ pygame .draw .rect (self .window , self .COLOR ['WHITE' ], (self . SIDE_PADDING , self . HEADER_HEIGHT , self . GRID_SIZE , self . GRID_SIZE ))
144+ self .window .blit (self .assets ['VLine' ], (self . SIDE_PADDING - ( self . LINE_WIDTH // 2 ) + self . CELL_WIDTH , self . HEADER_HEIGHT ))
145+ self .window .blit (self .assets ['VLine' ], (self . SIDE_PADDING - ( self . LINE_WIDTH // 2 ) + 2 * self . CELL_WIDTH , self . HEADER_HEIGHT ))
146+ self .window .blit (self .assets ['HLine' ], (self . SIDE_PADDING , self . HEADER_HEIGHT + self . CELL_WIDTH ))
147+ self .window .blit (self .assets ['HLine' ], (self . SIDE_PADDING , self . HEADER_HEIGHT + 2 * self . CELL_WIDTH ))
139148 self .drawCells ()
140149 self .box .blit ()
141150 self .box .update ()
@@ -145,7 +154,7 @@ def redraw(self):
145154 def displayTurn (self ):
146155 playerName = "Your" if self .grid .currentPlayer == 1 else f"{ self .AIPlayer .name } 's"
147156 currentTurnText = self .Font .render (f"{ playerName } turn" , True , self .COLOR ['BLACK' ])
148- rect = currentTurnText .get_rect (center = (self .WINDOW_WIDTH // 2 , 60 ))
157+ rect = currentTurnText .get_rect (center = (self .WINDOW_WIDTH // 2 , self . HEADER_HEIGHT // 2 ))
149158 self .window .blit (currentTurnText , rect )
150159
151160 def handleEvents (self ):
@@ -160,13 +169,13 @@ def handleEvents(self):
160169
161170 def handleClick (self , mousePos ):
162171 x , y = mousePos
163- if 350 >= x >= 50 and 425 >= y >= 125 :
164- move = ((y - 125 ) // 100 , (x - 50 ) // 100 )
172+ if self . GRID_SIZE + self . SIDE_PADDING >= x >= self . SIDE_PADDING and self . HEADER_HEIGHT + self . GRID_SIZE >= y >= self . HEADER_HEIGHT :
173+ move = ((y - self . HEADER_HEIGHT ) // self . LINE_WIDTH , (x - self . SIDE_PADDING ) // self . LINE_WIDTH )
165174 try :
166175 self .grid .play (1 , move )
167- except WrongTurnError :
176+ except TicTacToeExceptions . WrongTurnError :
168177 messagebox .showerror ('Error' , 'Wait for your turn' )
169- except AlreadyFilledError :
178+ except TicTacToeExceptions . AlreadyFilledError :
170179 messagebox .showerror ('Error' , 'Position already filled' )
171180 self .redraw ()
172181
@@ -181,11 +190,8 @@ def loadAssets(self, assetsPath):
181190
182191
183192if __name__ == '__main__' :
184- # For the messagebox, to prevent a Tkinter window from popping up
185- root = Tk ()
186- root .withdraw ()
187193 try :
188194 Game (assetsPath = ('TicTacToe' , 'Resources' ), saveToCloud = True )
189- except Exception as e :
195+ except GameException as e :
190196 messagebox .showerror ('ERROR' , f'{ e .__class__ .__name__ } : { e } ' )
191197 raise e
0 commit comments