Skip to content

Commit 177ee7a

Browse files
authored
Merge pull request #3052 from makermelissa/main
Fix many bugs
2 parents 07967a2 + f476d0a commit 177ee7a

File tree

2 files changed

+91
-62
lines changed

2 files changed

+91
-62
lines changed

Metro/Metro_RP2350_Matching_Game/code.py

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
An implementation of a match3 jewel swap game. The idea is to move one character at a time
55
to line up at least 3 characters.
66
"""
7+
import time
78
from displayio import Group, OnDiskBitmap, TileGrid, Bitmap, Palette
89
from adafruit_display_text.bitmap_label import Label
910
from adafruit_display_text.text_box import TextBox
@@ -141,22 +142,25 @@ def get_color_index(color, shader=None):
141142
ui_group = Group()
142143
main_group.append(ui_group)
143144

145+
# Create the mouse graphics and add to the main group
146+
time.sleep(1) # Allow time for USB host to initialize
147+
mouse = find_and_init_boot_mouse("/bitmaps/mouse_cursor.bmp")
148+
if mouse is None:
149+
raise RuntimeError("No mouse found connected to USB Host")
150+
main_group.append(mouse.tilegrid)
151+
144152
# Create the game logic object
145153
# pylint: disable=no-value-for-parameter, too-many-function-args
146154
game_logic = GameLogic(
147155
display,
156+
mouse,
148157
game_grid,
149158
swap_piece,
150159
selected_piece_group,
151-
GAME_PIECES
160+
GAME_PIECES,
161+
HINT_TIMEOUT
152162
)
153163

154-
# Create the mouse graphics and add to the main group
155-
mouse = find_and_init_boot_mouse("/bitmaps/mouse_cursor.bmp")
156-
if mouse is None:
157-
raise RuntimeError("No mouse found connected to USB Host")
158-
main_group.append(mouse.tilegrid)
159-
160164
def update_ui():
161165
# Update the UI elements with the current game state
162166
score_label.text = f"Score:\n{game_logic.score}"
@@ -232,38 +236,29 @@ def hide_group(group):
232236
while True:
233237
update_ui()
234238
# update mouse
235-
pressed_btns = mouse.update()
236-
237-
if waiting_for_release and not pressed_btns:
238-
# If both buttons are released, we can process the next click
239-
waiting_for_release = False
239+
game_logic.update_mouse()
240240

241241
if not message_dialog.hidden:
242-
if message_button.handle_mouse((mouse.x, mouse.y),
243-
pressed_btns and "left" in pressed_btns,
244-
waiting_for_release):
245-
waiting_for_release = True
242+
if message_button.handle_mouse(
243+
(mouse.x, mouse.y),
244+
game_logic.pressed_btns and "left" in game_logic.pressed_btns,
245+
waiting_for_release
246+
):
247+
game_logic.waiting_for_release = True
246248
continue
247249

248-
if reset_button.handle_mouse((mouse.x, mouse.y),
249-
pressed_btns and "left" in pressed_btns,
250-
waiting_for_release):
251-
waiting_for_release = True
250+
if reset_button.handle_mouse(
251+
(mouse.x, mouse.y),
252+
game_logic.pressed_btns is not None and "left" in game_logic.pressed_btns,
253+
game_logic.waiting_for_release
254+
):
255+
game_logic.waiting_for_release = True
252256

253257
# process gameboard click if no menu
254-
game_board = game_logic.game_board
255-
if (game_board.x <= mouse.x <= game_board.x + game_board.columns * 32 and
256-
game_board.y <= mouse.y <= game_board.y + game_board.rows * 32 and
257-
not waiting_for_release):
258-
piece_coords = ((mouse.x - game_board.x) // 32, (mouse.y - game_board.y) // 32)
259-
if pressed_btns and "left" in pressed_btns:
260-
game_logic.piece_clicked(piece_coords)
261-
waiting_for_release = True
258+
game_logic.update()
262259
game_over = game_logic.check_for_game_over()
263260
if game_over and not game_over_shown:
264261
message_label.text = ("No more moves available. your final score is:\n"
265262
+ str(game_logic.score))
266263
message_dialog.hidden = False
267264
game_over_shown = True
268-
if game_logic.time_since_last_update > HINT_TIMEOUT:
269-
game_logic.show_hint()

Metro/Metro_RP2350_Matching_Game/gamelogic.py

Lines changed: 66 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
SELECTOR_SPRITE = 9
1111
EMPTY_SPRITE = 10
12-
DEBOUNCE_TIME = 0.1 # seconds for debouncing mouse clicks
12+
DEBOUNCE_TIME = 0.2 # seconds for debouncing mouse clicks
1313

1414
class 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

154158
class 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

Comments
 (0)