Skip to content

Commit ad3f5a6

Browse files
authored
Merge pull request #3129 from RetiredWizard/minesweepchord
Minesweeper: add mouse "chording" feature
2 parents 9dc3e24 + c9c4dd2 commit ad3f5a6

File tree

2 files changed

+96
-26
lines changed

2 files changed

+96
-26
lines changed

Metro/Metro_RP2350_Minesweeper/code.py

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ def update_ui():
123123

124124
buf = array.array("b", [0] * 4)
125125
waiting_for_release = False
126+
chord_selected = False
126127
left_button = right_button = False
127128
mouse_coords = (display.width // 2, display.height // 2)
128129

@@ -231,41 +232,21 @@ def hide_group(group):
231232

232233
menus = (reset_menu, difficulty_menu)
233234

234-
# Mouse state
235-
last_left_button_state = 0
236-
last_right_button_state = 0
237-
left_button_pressed = False
238-
right_button_pressed = False
239-
240235
# main loop
241236
while True:
242237
update_ui()
243238
# update cursor position, and check for clicks
244-
buttons = mouse.update()
239+
mouse.update()
240+
buttons = mouse.pressed_btns
245241

246242
# Extract button states
247-
if buttons is None:
243+
if buttons is None or buttons == ():
248244
left_button = 0
249245
right_button = 0
250246
else:
251247
left_button = 1 if 'left' in buttons else 0
252248
right_button = 1 if 'right' in buttons else 0
253249

254-
# Detect button presses
255-
if left_button == 1 and last_left_button_state == 0:
256-
left_button_pressed = True
257-
elif left_button == 0 and last_left_button_state == 1:
258-
left_button_pressed = False
259-
260-
if right_button == 1 and last_right_button_state == 0:
261-
right_button_pressed = True
262-
elif right_button == 0 and last_right_button_state == 1:
263-
right_button_pressed = False
264-
265-
# Update button states
266-
last_left_button_state = left_button
267-
last_right_button_state = right_button
268-
269250
mouse_coords = (mouse_tg.x, mouse_tg.y)
270251

271252
if waiting_for_release and not left_button and not right_button:
@@ -282,11 +263,23 @@ def hide_group(group):
282263
else:
283264
# process gameboard click if no menu
284265
ms_board = game_logic.game_board
266+
267+
if left_button and right_button and not chord_selected:
268+
chord_coords = ((mouse_tg.x - ms_board.x) // 16, (mouse_tg.y - ms_board.y) // 16)
269+
chord_selected = game_logic.square_chord_highlight(chord_coords)
270+
if chord_selected:
271+
waiting_for_release = True
272+
285273
if (ms_board.x <= mouse_tg.x <= ms_board.x + game_logic.grid_width * 16 and
286274
ms_board.y <= mouse_tg.y <= ms_board.y + game_logic.grid_height * 16 and
287275
not waiting_for_release):
276+
288277
coords = ((mouse_tg.x - ms_board.x) // 16, (mouse_tg.y - ms_board.y) // 16)
289-
if right_button:
278+
if chord_selected:
279+
chord_selected = False
280+
game_logic.square_chord_highlight(chord_coords, False)
281+
game_logic.square_chorded(chord_coords)
282+
elif right_button:
290283
game_logic.square_flagged(coords)
291284
elif left_button:
292285
if not game_logic.square_clicked(coords):

Metro/Metro_RP2350_Minesweeper/gamelogic.py

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ def _set_data(self, x, y, value):
9292
self._board_data[y * self.grid_width + x] = value
9393

9494
def _get_data(self, x, y):
95+
if x < 0 or x >= self.grid_width or y < 0 or y >= self.grid_height:
96+
return None # out of bounds, do nothing
9597
return self._board_data[y * self.grid_width + x]
9698

9799
def _set_board(self, x, y, value):
@@ -102,6 +104,8 @@ def _set_board(self, x, y, value):
102104
def _get_board(self, x, y):
103105
if not isinstance(self.game_board, TileGrid):
104106
raise ValueError("Game board not initialized")
107+
if x < 0 or x >= self.grid_width or y < 0 or y >= self.grid_height:
108+
return None # out of bounds, do nothing
105109
return self.game_board[x, y] # pylint: disable=unsubscriptable-object
106110

107111
def _compute_counts(self):
@@ -165,6 +169,77 @@ def square_flagged(self, coords):
165169
break
166170
return True
167171

172+
def square_chorded(self, coords):
173+
if self._status in (STATUS_WON, STATUS_LOST):
174+
return False
175+
176+
x, y = coords
177+
if x < 0 or x >= self.grid_width or y < 0 or y >= self.grid_height:
178+
return True # out of bounds, do nothing
179+
180+
value = self._get_board(x, y)
181+
182+
if value not in (OPEN1, OPEN2, OPEN3, OPEN4, OPEN5, OPEN6, OPEN7, OPEN8):
183+
return True # Nothing to do if not an open numbered square
184+
185+
# Pre-compute valid neighbors
186+
neighbors = [
187+
(nx, ny)
188+
for nx in range(x - 1, x + 2)
189+
for ny in range(y - 1, y + 2)
190+
if (0 <= nx < self.grid_width
191+
and 0 <= ny < self.grid_height
192+
and not (nx == x and ny == y))
193+
]
194+
195+
# Count flagged neighbors
196+
flags = sum(1 for nx, ny in neighbors if self._get_board(nx, ny) == FLAG)
197+
198+
if flags != value:
199+
return True # not enough flags, do nothing
200+
201+
# Uncover all non-flagged neighbors
202+
for nx, ny in neighbors:
203+
if self._get_board(nx, ny) != FLAG:
204+
if not self.square_clicked((nx, ny)):
205+
return False # lost
206+
207+
return True
208+
209+
def square_chord_highlight(self, coords, highlight=True):
210+
if self._status in (STATUS_WON, STATUS_LOST):
211+
return False
212+
213+
x, y = coords
214+
if x < 0 or x >= self.grid_width or y < 0 or y >= self.grid_height:
215+
return False # out of bounds, do nothing
216+
217+
value = self._get_board(x, y)
218+
219+
if value not in (OPEN1, OPEN2, OPEN3, OPEN4, OPEN5, OPEN6, OPEN7, OPEN8):
220+
return False # Nothing to do if not an open numbered square
221+
222+
# Pre-compute valid neighbors
223+
neighbors = [
224+
(nx, ny)
225+
for nx in range(x - 1, x + 2)
226+
for ny in range(y - 1, y + 2)
227+
if (0 <= nx < self.grid_width
228+
and 0 <= ny < self.grid_height
229+
and not (nx == x and ny == y))
230+
]
231+
232+
# Highlight all non-flagged squares around here
233+
for nx, ny in neighbors:
234+
if highlight:
235+
if self._get_board(nx, ny) == BLANK:
236+
self._set_board(nx, ny,MINE_QUESTION_OPEN)
237+
else:
238+
if self._get_board(nx, ny) == MINE_QUESTION_OPEN:
239+
self._set_board(nx, ny, BLANK)
240+
241+
return True
242+
168243
def square_clicked(self, coords):
169244
x, y = coords
170245

@@ -178,7 +253,7 @@ def square_clicked(self, coords):
178253
if self._start_time is None:
179254
self._start_time = ticks_ms()
180255

181-
if self._get_board(x, y) != FLAG:
256+
if self._get_board(x, y) not in (FLAG, None):
182257
under_the_tile = self._get_data(x, y)
183258
if under_the_tile == MINE:
184259
self._set_data(x, y, MINE_CLICKED)
@@ -215,7 +290,9 @@ def check_for_win(self):
215290
# first make sure everything has been explored and decided
216291
for x in range(self.grid_width):
217292
for y in range(self.grid_height):
218-
if self._get_board(x, y) == BLANK or self._get_board(x, y) == MINE_QUESTION:
293+
if self._get_board(x, y) == BLANK or \
294+
self._get_board(x, y) == MINE_QUESTION or \
295+
self._get_board(x, y) == MINE_QUESTION_OPEN:
219296
return None # still ignored or question squares
220297
# then check for mistagged bombs
221298
for x in range(self.grid_width):

0 commit comments

Comments
 (0)