99
1010SELECTOR_SPRITE = 9
1111EMPTY_SPRITE = 10
12- DEBOUNCE_TIME = 0.1 # seconds for debouncing mouse clicks
12+ DEBOUNCE_TIME = 0.2 # seconds for debouncing mouse clicks
1313
1414class GameBoard :
1515 "Contains the game board"
@@ -42,6 +42,10 @@ def reset(self):
4242 for row in range (self .rows ):
4343 if self ._game_grid [(column , row )] != EMPTY_SPRITE :
4444 self .remove_game_piece (column , row )
45+ # Hide the animation TileGrids
46+ self ._selector .hidden = True
47+ self ._swap_piece .hidden = True
48+ self .selected_piece_group .hidden = True
4549
4650 def move_game_piece (self , old_x , old_y , new_x , new_y ):
4751 if 0 <= old_x < self .columns and 0 <= old_y < self .rows :
@@ -153,18 +157,41 @@ def game_grid_copy(self):
153157
154158class GameLogic :
155159 "Contains the Logic to examine the game board and determine if a move is valid."
156- def __init__ (self , display , game_grid , swap_piece , selected_piece_group , game_pieces ):
160+ def __init__ (self , display , mouse , game_grid , swap_piece ,
161+ selected_piece_group , game_pieces , hint_timeout ):
157162 self ._display = display
163+ self ._mouse = mouse
158164 self .game_board = GameBoard (game_grid , swap_piece , selected_piece_group )
159165 self ._score = 0
160166 self ._available_moves = []
161167 if not 3 <= game_pieces <= 8 :
162168 raise ValueError ("game_pieces must be between 3 and 8" )
163169 self ._game_pieces = game_pieces # Number of different game pieces
170+ self ._hint_timeout = hint_timeout
164171 self ._last_update_time = ticks_ms () # For hint timing
165172 self ._last_click_time = ticks_ms () # For debouncing mouse clicks
173+ self .pressed_btns = None
174+ self .waiting_for_release = False
166175
167- def piece_clicked (self , coords ):
176+ def update_mouse (self ):
177+ self .pressed_btns = self ._mouse .update ()
178+ if self .waiting_for_release and not self .pressed_btns :
179+ # If both buttons are released, we can process the next click
180+ self .waiting_for_release = False
181+
182+ def update (self ):
183+ gb = self .game_board
184+ if (gb .x <= self ._mouse .x <= gb .x + gb .columns * 32 and
185+ gb .y <= self ._mouse .y <= gb .y + gb .rows * 32 and
186+ not self .waiting_for_release ):
187+ piece_coords = ((self ._mouse .x - gb .x ) // 32 , (self ._mouse .y - gb .y ) // 32 )
188+ if self .pressed_btns and "left" in self .pressed_btns :
189+ self ._piece_clicked (piece_coords )
190+ self .waiting_for_release = True
191+ if self .time_since_last_update > self ._hint_timeout :
192+ self .show_hint ()
193+
194+ def _piece_clicked (self , coords ):
168195 """ Handle a piece click event. """
169196 if ticks_ms () <= self ._last_click_time :
170197 self ._last_click_time -= 2 ** 29 # ticks_ms() wraps around after 2**29 ms
@@ -206,7 +233,7 @@ def piece_clicked(self, coords):
206233 if ((abs (previous_x - column ) == 1 and previous_y == row ) or
207234 (previous_x == column and abs (previous_y - row ) == 1 )):
208235 # Swap the pieces
209- self .swap_selected_piece (column , row )
236+ self ._swap_selected_piece (column , row )
210237
211238 def show_hint (self ):
212239 """ Show a hint by selecting a random available
@@ -216,21 +243,21 @@ def show_hint(self):
216243 from_coords = move ['from' ]
217244 to_coords = move ['to' ]
218245 self .game_board .select_piece (from_coords [0 ], from_coords [1 ])
219- self .animate_swap (to_coords [0 ], to_coords [1 ])
246+ self ._animate_swap (to_coords [0 ], to_coords [1 ])
220247 self .game_board .select_piece (from_coords [0 ], from_coords [1 ])
221- self .animate_swap (to_coords [0 ], to_coords [1 ])
248+ self ._animate_swap (to_coords [0 ], to_coords [1 ])
222249 self ._last_update_time = ticks_ms () # Reset hint timer
223250
224- def swap_selected_piece (self , column , row ):
251+ def _swap_selected_piece (self , column , row ):
225252 """ Swap the selected piece with the piece at the specified column and row.
226253 If the swap is not valid, revert to the previous selection. """
227254 old_coords = self .game_board .selected_coords
228- self .animate_swap (column , row )
229- if not self .update ():
255+ self ._animate_swap (column , row )
256+ if not self ._update_board ():
230257 self .game_board .select_piece (column , row , show_selector = False )
231- self .animate_swap (old_coords [0 ], old_coords [1 ])
258+ self ._animate_swap (old_coords [0 ], old_coords [1 ])
232259
233- def animate_swap (self , column , row ):
260+ def _animate_swap (self , column , row ):
234261 """ Copy the pieces to separate tilegrids, animate the swap, and update the game board. """
235262 if 0 <= column < self .game_board .columns and 0 <= row < self .game_board .rows :
236263 selected_coords = self .game_board .selected_coords
@@ -271,11 +298,12 @@ def animate_swap(self, column, row):
271298 # Deselect the selected piece (which sets the value)
272299 self .game_board .select_piece (column , row )
273300
274- def apply_gravity (self ):
301+ def _apply_gravity (self ):
275302 """ Go through each column from the bottom up and move pieces down
276303 continue until there are no more pieces to move """
277304 # pylint:disable=too-many-nested-blocks
278305 while True :
306+ self .pressed_btns = self ._mouse .update ()
279307 moved = False
280308 for x in range (self .game_board .columns ):
281309 for y in range (self .game_board .rows - 1 , - 1 , - 1 ):
@@ -295,7 +323,7 @@ def apply_gravity(self):
295323 if not moved :
296324 break
297325
298- def check_for_matches (self ):
326+ def _check_for_matches (self ):
299327 """ Scan the game board for matches of 3 or more in a row or column """
300328 matches = []
301329 for x in range (self .game_board .columns ):
@@ -325,35 +353,37 @@ def check_for_matches(self):
325353 matches .append (vertical_match )
326354 return matches
327355
328- def update (self ):
356+ def _update_board (self ):
329357 """ Update the game logic, check for matches, and apply gravity. """
330358 matches_found = False
331359 multiplier = 1
332- matches = self .check_for_matches ()
360+ matches = self ._check_for_matches ()
333361 while matches :
334362 if matches :
335363 for match in matches :
336364 for x , y in match :
337365 self .game_board .remove_game_piece (x , y )
338366 self ._score += 10 * multiplier * len (matches ) * (len (match ) - 2 )
339367 time .sleep (0.5 ) # Pause to show the match removal
340- self .apply_gravity ()
368+ self ._apply_gravity ()
341369 matches_found = True
342- matches = self .check_for_matches ()
370+ matches = self ._check_for_matches ()
343371 multiplier += 1
344- self ._available_moves = self .find_all_possible_matches ()
372+ self ._available_moves = self ._find_all_possible_matches ()
345373 print (f"{ len (self ._available_moves )} available moves found." )
346374 return matches_found
347375
348376 def reset (self ):
349377 """ Reset the game board and score. """
378+ print ("Reset started" )
350379 self .game_board .reset ()
351380 self ._score = 0
352381 self ._last_update_time = ticks_ms ()
353- self .apply_gravity ()
354- self .update ()
382+ self ._apply_gravity ()
383+ self ._update_board ()
384+ print ("Reset completed" )
355385
356- def check_match_after_move (self , row , column , direction , move_type = 'horizontal' ):
386+ def _check_match_after_move (self , row , column , direction , move_type = 'horizontal' ):
357387 """ Move the piece in a copy of the board to see if it creates a match."""
358388 if move_type == 'horizontal' :
359389 new_row , new_column = row , column + direction
@@ -371,23 +401,23 @@ def check_match_after_move(self, row, column, direction, move_type='horizontal')
371401 new_grid [row ][column ], new_grid [new_row ][new_column ] = new_grid [new_row ][new_column ], piece
372402
373403 # Check for horizontal matches at the new position
374- horizontal_match = self .check_horizontal_match (new_grid , new_row , new_column , piece )
404+ horizontal_match = self ._check_horizontal_match (new_grid , new_row , new_column , piece )
375405
376406 # Check for vertical matches at the new position
377- vertical_match = self .check_vertical_match (new_grid , new_row , new_column , piece )
407+ vertical_match = self ._check_vertical_match (new_grid , new_row , new_column , piece )
378408
379409 # Also check the original position for matches after the swap
380410 original_piece = new_grid [row ][column ]
381- horizontal_match_orig = self .check_horizontal_match (new_grid , row , column , original_piece )
382- vertical_match_orig = self .check_vertical_match (new_grid , row , column , original_piece )
411+ horizontal_match_orig = self ._check_horizontal_match (new_grid , row , column , original_piece )
412+ vertical_match_orig = self ._check_vertical_match (new_grid , row , column , original_piece )
383413
384414 all_matches = (horizontal_match + vertical_match +
385415 horizontal_match_orig + vertical_match_orig )
386416
387417 return True , len (all_matches ) > 0
388418
389419 @staticmethod
390- def check_horizontal_match (grid , row , column , piece ):
420+ def _check_horizontal_match (grid , row , column , piece ):
391421 """Check for horizontal 3-in-a-row matches centered
392422 around or including the given position."""
393423 matches = []
@@ -406,7 +436,7 @@ def check_horizontal_match(grid, row, column, piece):
406436 return matches
407437
408438 @staticmethod
409- def check_vertical_match (grid , row , column , piece ):
439+ def _check_vertical_match (grid , row , column , piece ):
410440 """Check for vertical 3-in-a-row matches centered around or including the given position."""
411441 matches = []
412442 rows = len (grid )
@@ -429,7 +459,7 @@ def check_for_game_over(self):
429459 return True
430460 return False
431461
432- def find_all_possible_matches (self ):
462+ def _find_all_possible_matches (self ):
433463 """
434464 Scan the entire game board to find all possible moves that would create a 3-in-a-row match.
435465 """
@@ -438,31 +468,35 @@ def find_all_possible_matches(self):
438468 for row in range (self .game_board .rows ):
439469 for column in range (self .game_board .columns ):
440470 # Check move right
441- can_move , creates_match = self .check_match_after_move (row , column , 1 , 'horizontal' )
471+ can_move , creates_match = self ._check_match_after_move (
472+ row , column , 1 , 'horizontal' )
442473 if can_move and creates_match :
443474 possible_moves .append ({
444475 'from' : (column , row ),
445476 'to' : (column + 1 , row ),
446477 })
447478
448479 # Check move left
449- can_move , creates_match = self .check_match_after_move (row , column , - 1 , 'horizontal' )
480+ can_move , creates_match = self ._check_match_after_move (
481+ row , column , - 1 , 'horizontal' )
450482 if can_move and creates_match :
451483 possible_moves .append ({
452484 'from' : (column , row ),
453485 'to' : (column - 1 , row ),
454486 })
455487
456488 # Check move down
457- can_move , creates_match = self .check_match_after_move (row , column , 1 , 'vertical' )
489+ can_move , creates_match = self ._check_match_after_move (
490+ row , column , 1 , 'vertical' )
458491 if can_move and creates_match :
459492 possible_moves .append ({
460493 'from' : (column , row ),
461494 'to' : (column , row + 1 ),
462495 })
463496
464497 # Check move up
465- can_move , creates_match = self .check_match_after_move (row , column , - 1 , 'vertical' )
498+ can_move , creates_match = self ._check_match_after_move (
499+ row , column , - 1 , 'vertical' )
466500 if can_move and creates_match :
467501 possible_moves .append ({
468502 'from' : (column , row ),
0 commit comments