@@ -175,6 +175,7 @@ class MazeApp(object):
175
175
Maze class.
176
176
"""
177
177
178
+ _algorithms : dict
178
179
_diagonals : bool
179
180
_end_point : _Point2
180
181
_grid : _MazeType
@@ -190,8 +191,8 @@ class MazeApp(object):
190
191
191
192
def __init__ (
192
193
self ,
193
- width : int = 7 ,
194
- rows : int = 85 ,
194
+ width : int = 8 ,
195
+ rows : int = 75 ,
195
196
margin : int = 0
196
197
) -> None :
197
198
"""
@@ -231,8 +232,9 @@ def __init__(
231
232
self ._drag_end_point = False
232
233
233
234
# Used for deciding what to do in different situations
234
- self ._path_found = False
235
235
self ._algorithm_run = False
236
+ self ._algorithms = {0 : 'dijkstra' , 1 : 'astar' , 2 : 'dfs' , 3 : 'bfs' }
237
+ self ._path_found = False
236
238
237
239
# Create the window
238
240
self ._clock = pygame .time .Clock ()
@@ -297,6 +299,8 @@ def _diagonals(value: bool) -> None:
297
299
Changes diagonals
298
300
"""
299
301
self ._diagonals = value
302
+ if self ._algorithm_run :
303
+ self ._path_found = self ._update_path ()
300
304
301
305
theme = pygame_menu .Theme (
302
306
background_color = pygame_menu .themes .TRANSPARENT_COLOR ,
@@ -309,7 +313,7 @@ def _diagonals(value: bool) -> None:
309
313
self ._menu = pygame_menu .Menu (
310
314
height = self ._screen_width ,
311
315
mouse_motion_selection = True ,
312
- position = (640 , 25 , False ),
316
+ position = (645 , 25 , False ),
313
317
theme = theme ,
314
318
title = '' ,
315
319
width = 240
@@ -349,7 +353,7 @@ def _diagonals(value: bool) -> None:
349
353
items = [('Prim' , 0 ),
350
354
('Alt Prim' , 1 ),
351
355
('Recursive' , 2 ),
352
- ('Terrain' , 3 )],
356
+ ('(+) Terrain' , 3 )],
353
357
dropselect_id = 'generator' ,
354
358
font_size = 16 ,
355
359
onchange = onchange_dropselect ,
@@ -428,15 +432,16 @@ def _diagonals(value: bool) -> None:
428
432
429
433
# Create about menu
430
434
menu_about = pygame_menu .Menu (
431
- height = self ._screen_width ,
435
+ height = self ._screen_width + 20 ,
432
436
mouse_motion_selection = True ,
433
- position = (640 , 25 , False ),
437
+ position = (645 , 8 , False ),
434
438
theme = theme ,
435
439
title = '' ,
436
440
width = 240
437
441
)
438
442
menu_about .add .label ('pygame-menu\n Maze' , font_name = pygame_menu .font .FONT_FIRACODE_BOLD , font_size = 25 ,
439
443
margin = (0 , 5 ))
444
+ menu_about .add .vertical_margin (10 )
440
445
text = 'Left click to create a wall or move the start and end points.\n ' \
441
446
'Hold left CTRL and left click to create a sticky mud patch (whi' \
442
447
'ch reduces movement speed to 1/3).\n '
@@ -478,7 +483,8 @@ def _diagonals(value: bool) -> None:
478
483
for btn in self ._menu .get_widgets (['run_generator' , 'run_solver' , 'about' , 'about_back' ]):
479
484
btn .set_onmouseover (button_onmouseover )
480
485
btn .set_onmouseleave (button_onmouseleave )
481
- btn .set_cursor (pygame_menu .locals .CURSOR_HAND )
486
+ if not btn .readonly :
487
+ btn .set_cursor (pygame_menu .locals .CURSOR_HAND )
482
488
btn .set_background_color ((75 , 79 , 81 ))
483
489
484
490
def _clear_maze (self ) -> None :
@@ -491,6 +497,7 @@ def _clear_maze(self) -> None:
491
497
for column in range (self ._rows ):
492
498
if (row , column ) != self ._start_point and (row , column ) != self ._end_point :
493
499
self ._grid [row ][column ].update (nodetype = 'blank' , is_visited = False , is_path = False )
500
+ self ._clear_visited ()
494
501
495
502
def _run_generator (self ) -> None :
496
503
"""
@@ -499,30 +506,37 @@ def _run_generator(self) -> None:
499
506
if self ._visualize :
500
507
ut .set_pygame_cursor (pygame_menu .locals .CURSOR_NO )
501
508
o_visualize = self ._visualize
502
- self ._clear_maze ()
503
509
gen_type = self ._menu .get_widget ('generator' ).get_value ()[1 ]
510
+ if gen_type != 3 :
511
+ self ._clear_maze ()
504
512
if gen_type == 0 :
505
- self ._grid = self ._prim ()
513
+ self ._grid = self ._prim (start_point = self . _start_point )
506
514
elif gen_type == 1 :
507
- self ._grid = self ._better_prim ()
515
+ self ._grid = self ._better_prim (start_point = self . _start_point )
508
516
elif gen_type == 2 :
517
+ pygame .display .flip ()
509
518
self ._recursive_division ()
510
519
elif gen_type == 3 :
511
520
self ._random_terrain ()
512
521
self ._visualize = o_visualize
522
+ if self ._visualize :
523
+ ut .set_pygame_cursor (pygame_menu .locals .CURSOR_ARROW )
513
524
514
525
def _run_solver (self ) -> None :
515
526
"""
516
527
Run the solver.
517
528
"""
518
- if self ._visualize :
519
- ut .set_pygame_cursor (pygame_menu .locals .CURSOR_NO )
520
529
o_visualize = self ._visualize
521
530
solver_type = self ._menu .get_widget ('solver' ).get_value ()[1 ]
522
- self ._clear_visited ()
523
- self ._update_gui ()
524
531
if self ._visualize :
525
532
pygame .display .flip ()
533
+ if self ._path_found and self ._algorithms [solver_type ] == self ._algorithm_run :
534
+ self ._visualize = False
535
+ else :
536
+ self ._clear_visited ()
537
+ pygame .display .flip ()
538
+ if self ._visualize :
539
+ ut .set_pygame_cursor (pygame_menu .locals .CURSOR_NO )
526
540
if solver_type == 0 :
527
541
self ._path_found = self ._dijkstra (self ._grid , self ._start_point , self ._end_point )
528
542
self ._algorithm_run = 'dijkstra'
@@ -538,6 +552,8 @@ def _run_solver(self) -> None:
538
552
self ._visualize = o_visualize
539
553
self ._grid [self ._start_point [0 ]][self ._start_point [1 ]].update (nodetype = 'start' )
540
554
self ._update_path ()
555
+ if self ._visualize :
556
+ ut .set_pygame_cursor (pygame_menu .locals .CURSOR_ARROW )
541
557
542
558
def _check_esc (self ) -> None :
543
559
"""
@@ -546,7 +562,7 @@ def _check_esc(self) -> None:
546
562
if self ._visualize :
547
563
for event in pygame .event .get ():
548
564
if event .type == pygame .QUIT :
549
- exit ()
565
+ self . _quit ()
550
566
if event .type == pygame .KEYDOWN and event .key == pygame .K_ESCAPE :
551
567
self ._visualize = False
552
568
@@ -559,7 +575,7 @@ def _sleep(ms: float) -> None:
559
575
"""
560
576
time .sleep (ms )
561
577
562
- def _better_prim (self , mazearray : Optional [_MazeType ] = None , start_point : bool = False ) -> _MazeType :
578
+ def _better_prim (self , mazearray : Optional [_MazeType ] = None , start_point : Optional [ _Point2 ] = None ) -> _MazeType :
563
579
"""
564
580
Randomized Prim's algorithm for creating random mazes.
565
581
This version maintains the traditional "maze" look, where a route cannot
@@ -646,12 +662,18 @@ def _better_prim(self, mazearray: Optional[_MazeType] = None, start_point: bool
646
662
647
663
walls .remove (wall )
648
664
665
+ # Iterate through each dormant, if so, change to blank
666
+ for row in range (self ._rows ):
667
+ for column in range (self ._rows ):
668
+ if mazearray [row ][column ].nodetype == 'dormant' :
669
+ mazearray [row ][column ].update (nodetype = 'blank' )
670
+
649
671
mazearray [self ._end_point [0 ]][self ._end_point [1 ]].update (nodetype = 'end' )
650
672
mazearray [self ._start_point [0 ]][self ._start_point [1 ]].update (nodetype = 'start' )
651
673
652
674
return mazearray
653
675
654
- def _prim (self , mazearray : Optional [_MazeType ] = None , start_point : bool = False ) -> _MazeType :
676
+ def _prim (self , mazearray : Optional [_MazeType ] = None , start_point : Optional [ _Point2 ] = None ) -> _MazeType :
655
677
"""
656
678
Randomized Prim's algorithm for creating random mazes.
657
679
@@ -828,7 +850,7 @@ def _gaps_to_offset() -> List[int]:
828
850
y += 1
829
851
if y >= self ._rows :
830
852
y = self ._rows - 1
831
- self ._grid [x ][y ].update (nodetype = " blank" )
853
+ self ._grid [x ][y ].update (nodetype = ' blank' )
832
854
self ._draw_square (self ._grid , x , y )
833
855
if self ._visualize :
834
856
self ._update_square (x , y )
@@ -925,10 +947,8 @@ def _update_path(self) -> Union[bool, _MazeType]:
925
947
Updates the path.
926
948
"""
927
949
self ._clear_visited ()
928
- valid_algorithms = ['dijkstra' , 'astar' , 'dfs' , 'bfs' ]
929
-
930
- assert self ._algorithm_run in valid_algorithms , \
931
- f'last algorithm used ({ self ._algorithm_run } ) is not in valid algorithms: { valid_algorithms } '
950
+ assert self ._algorithm_run in self ._algorithms .values (), \
951
+ f'last algorithm used ({ self ._algorithm_run } ) is not in valid algorithms: { self ._algorithms .values ()} '
932
952
933
953
visualize = self ._visualize
934
954
self ._visualize = False
@@ -1116,8 +1136,8 @@ def _dijkstra(
1116
1136
time_taken = end - start
1117
1137
1118
1138
# Print timings
1119
- print (f'Program finished in { time_taken :.4f} seconds after checking { num_visited } '
1120
- f' nodes. That is { time_taken / num_visited :.8f} seconds per node. ' )
1139
+ print (f'Program finished in { time_taken :.4f} s after checking { num_visited } '
1140
+ f' nodes ( { time_taken / num_visited :.8f} s/ node) ' )
1121
1141
1122
1142
return False if v_distances [goal_node ] == float ('inf' ) else True
1123
1143
@@ -1275,19 +1295,39 @@ def _pos_in_grid(self, pos: _Point2) -> bool:
1275
1295
y = pos [1 ] - self ._offset [1 ]
1276
1296
return 1 <= x <= self ._screen_width and 1 <= y <= self ._screen_width
1277
1297
1298
+ @staticmethod
1299
+ def _quit () -> None :
1300
+ """
1301
+ Quit app.
1302
+ """
1303
+ pygame .quit ()
1304
+ exit ()
1305
+
1278
1306
def mainloop (self , test : bool ) -> None :
1279
1307
"""
1280
1308
Executes the main loop of the app.
1281
1309
1282
1310
:param test: If True, runs only 1 frame
1283
1311
"""
1312
+ print ('Press [ESC] to skip process if Visualize is On' )
1284
1313
while True :
1314
+
1285
1315
# Application events
1286
1316
events = pygame .event .get ()
1317
+
1318
+ # Update the menu
1319
+ self ._menu .update (events )
1320
+
1321
+ # If a menu widget disable its active state, disable the events, this is due to
1322
+ # user can click outside a dropselection box, and that triggers the disable active
1323
+ # state. If so, the event is destroyed, thus avoiding clicking the canvas
1324
+ if pygame_menu .events .MENU_LAST_WIDGET_DISABLE_ACTIVE_STATE in self ._menu .get_last_update_mode ()[0 ]:
1325
+ events = []
1326
+
1287
1327
for event in events :
1288
1328
# User closes
1289
1329
if event .type == pygame .QUIT :
1290
- exit ()
1330
+ self . _quit ()
1291
1331
1292
1332
# Write in the board
1293
1333
elif event .type == pygame .MOUSEBUTTONDOWN :
@@ -1394,9 +1434,6 @@ def mainloop(self, test: bool) -> None:
1394
1434
1395
1435
pygame .display .flip ()
1396
1436
1397
- # Update the menu events
1398
- self ._menu .update (events )
1399
-
1400
1437
# Update the app
1401
1438
self ._update_gui ()
1402
1439
0 commit comments