1+ import pygame
2+ from pygame .locals import *
3+
4+ pygame .init ()
5+ pygame .font .init ()
6+
7+ window_size = (450 , 500 )
8+
9+ screen = pygame .display .set_mode (window_size )
10+ pygame .display .set_caption ("Tic Tac Toe" )
11+
12+
13+ class TicTacToe ():
14+
15+ def __init__ (self , table_size ):
16+ self .table_size = table_size
17+ self .cell_size = table_size // 3
18+ self .table_space = 20
19+
20+ self .player = "X"
21+ self .winner = None
22+ self .taking_move = True
23+ self .running = True
24+ self .table = []
25+ for col in range (3 ):
26+ self .table .append ([])
27+ for row in range (3 ):
28+ self .table [col ].append ("-" )
29+
30+ self .background_color = (255 , 174 , 66 )
31+ self .table_color = (50 , 50 , 50 )
32+ self .line_color = (190 , 0 , 10 )
33+ self .instructions_color = (17 , 53 , 165 )
34+ self .game_over_bg_color = (47 , 98 , 162 )
35+ self .game_over_color = (255 , 179 , 1 )
36+ self .font = pygame .font .SysFont ("Courier New" , 35 )
37+ self .FPS = pygame .time .Clock ()
38+
39+
40+ # draws table representation
41+ def _draw_table (self ):
42+ tb_space_point = (self .table_space , self .table_size - self .table_space )
43+ cell_space_point = (self .cell_size , self .cell_size * 2 )
44+ r1 = pygame .draw .line (screen , self .table_color , [tb_space_point [0 ], cell_space_point [0 ]], [tb_space_point [1 ], cell_space_point [0 ]], 8 )
45+ c1 = pygame .draw .line (screen , self .table_color , [cell_space_point [0 ], tb_space_point [0 ]], [cell_space_point [0 ], tb_space_point [1 ]], 8 )
46+ r2 = pygame .draw .line (screen , self .table_color , [tb_space_point [0 ], cell_space_point [1 ]], [tb_space_point [1 ], cell_space_point [1 ]], 8 )
47+ c2 = pygame .draw .line (screen , self .table_color , [cell_space_point [1 ], tb_space_point [0 ]], [cell_space_point [1 ], tb_space_point [1 ]], 8 )
48+
49+
50+ def _change_player (self ):
51+ self .player = "O" if self .player == "X" else "X"
52+
53+
54+ # processing clicks to move
55+ def _move (self , pos ):
56+ try :
57+ x , y = pos [0 ] // self .cell_size , pos [1 ] // self .cell_size
58+ if self .table [x ][y ] == "-" :
59+ self .table [x ][y ] = self .player
60+ self ._draw_char (x ,y ,self .player )
61+ self ._game_check ()
62+ self ._change_player ()
63+ except :
64+ print ("Click inside the table only" )
65+
66+
67+ # draws character of the recent player to the selected table cell
68+ def _draw_char (self , x , y , player ):
69+ if self .player == "O" :
70+ img = pygame .image .load ("images/Tc-O.png" )
71+ elif self .player == "X" :
72+ img = pygame .image .load ("images/Tc-X.png" )
73+ img = pygame .transform .scale (img , (self .cell_size , self .cell_size ))
74+ screen .blit (img , (x * self .cell_size , y * self .cell_size , self .cell_size , self .cell_size ))
75+
76+
77+ # instructions and game-state messages
78+ def _message (self ):
79+ if self .winner is not None :
80+ screen .fill (self .game_over_bg_color , (130 , 445 , 193 , 35 ))
81+ msg = self .font .render (f'{ self .winner } WINS!!' , True , self .game_over_color )
82+ screen .blit (msg ,(144 ,445 ))
83+ elif not self .taking_move :
84+ screen .fill (self .game_over_bg_color , (130 , 445 , 193 , 35 ))
85+ instructions = self .font .render ('DRAW!!' , True , self .game_over_color )
86+ screen .blit (instructions ,(165 ,445 ))
87+ else :
88+ screen .fill (self .background_color , (135 , 445 , 188 , 35 ))
89+ instructions = self .font .render (f'{ self .player } to move' , True , self .instructions_color )
90+ screen .blit (instructions ,(135 ,445 ))
91+
92+
93+ def _game_check (self ):
94+ # vertical check
95+ for x_index , col in enumerate (self .table ):
96+ win = True
97+ pattern_list = []
98+ for y_index , content in enumerate (col ):
99+ if content != self .player :
100+ win = False
101+ break
102+ else :
103+ pattern_list .append ((x_index , y_index ))
104+ if win == True :
105+ self ._pattern_strike (pattern_list [0 ],pattern_list [- 1 ],"ver" )
106+ self .winner = self .player
107+ self .taking_move = False
108+ break
109+
110+ # horizontal check
111+ for row in range (len (self .table )):
112+ win = True
113+ pattern_list = []
114+ for col in range (len (self .table )):
115+ if self .table [col ][row ] != self .player :
116+ win = False
117+ break
118+ else :
119+ pattern_list .append ((col , row ))
120+ if win == True :
121+ self ._pattern_strike (pattern_list [0 ],pattern_list [- 1 ],"hor" )
122+ self .winner = self .player
123+ self .taking_move = False
124+ break
125+
126+ # left diagonal check
127+ for index , row in enumerate (self .table ):
128+ win = True
129+ if row [index ] != self .player :
130+ win = False
131+ break
132+ if win == True :
133+ self ._pattern_strike ((0 ,0 ),(2 ,2 ),"left-diag" )
134+ self .winner = self .player
135+ self .taking_move = False
136+
137+ # right diagonal check
138+ for index , row in enumerate (self .table [::- 1 ]):
139+ win = True
140+ if row [index ] != self .player :
141+ win = False
142+ break
143+ if win == True :
144+ self ._pattern_strike ((2 ,0 ),(0 ,2 ),"right-diag" )
145+ self .winner = self .player
146+ self .taking_move = False
147+
148+ # blank table cells check
149+ blank_cells = 0
150+ for row in self .table :
151+ for cell in row :
152+ if cell == "-" :
153+ blank_cells += 1
154+ if blank_cells == 0 :
155+ self .taking_move = False
156+
157+
158+ # strikes a line to winning patterns if already has
159+ def _pattern_strike (self , start_point , end_point , line_type ):
160+ # gets the middle value of the cell
161+ mid_val = self .cell_size // 2
162+
163+ # for the vertical winning pattern
164+ if line_type == "ver" :
165+ start_x , start_y = start_point [0 ] * self .cell_size + mid_val , self .table_space
166+ end_x , end_y = end_point [0 ] * self .cell_size + mid_val , self .table_size - self .table_space
167+
168+ # for the horizontal winning pattern
169+ elif line_type == "hor" :
170+ start_x , start_y = self .table_space , start_point [- 1 ] * self .cell_size + mid_val
171+ end_x , end_y = self .table_size - self .table_space , end_point [- 1 ] * self .cell_size + mid_val
172+
173+ # for the diagonal winning pattern from top-left to bottom right
174+ elif line_type == "left-diag" :
175+ start_x , start_y = self .table_space , self .table_space
176+ end_x , end_y = self .table_size - self .table_space , self .table_size - self .table_space
177+
178+ # for the diagonal winning pattern from top-right to bottom-left
179+ elif line_type == "right-diag" :
180+ start_x , start_y = self .table_size - self .table_space , self .table_space
181+ end_x , end_y = self .table_space , self .table_size - self .table_space
182+
183+ # draws the line strike
184+ line_strike = pygame .draw .line (screen , self .line_color , [start_x , start_y ], [end_x , end_y ], 8 )
185+
186+
187+ def main (self ):
188+ screen .fill (self .background_color )
189+ self ._draw_table ()
190+ while self .running :
191+ self ._message ()
192+ for self .event in pygame .event .get ():
193+ if self .event .type == pygame .QUIT :
194+ self .running = False
195+
196+ if self .event .type == pygame .MOUSEBUTTONDOWN :
197+ if self .taking_move :
198+ self ._move (self .event .pos )
199+
200+ pygame .display .flip ()
201+ self .FPS .tick (60 )
202+
203+
204+ if __name__ == "__main__" :
205+ g = TicTacToe (window_size [0 ])
206+ g .main ()
0 commit comments