1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 Matthias Wientapper
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 /* This is the implementation of a maze generation algorithm.
24 * The generated mazes are "perfect", i.e. there is one and only
25 * one path from any point in the maze to any other point.
28 * The implemented algorithm is called "Depth-First search", the
29 * solving is done by a dead-end filler routine.
34 #include "lib/helper.h"
40 #if (CONFIG_KEYPAD == IPOD_3G_PAD)
41 # define MAZE_NEW (BUTTON_SELECT | BUTTON_REPEAT)
42 # define MAZE_NEW_PRE BUTTON_SELECT
43 # define MAZE_QUIT BUTTON_MENU
44 # define MAZE_SOLVE (BUTTON_SELECT | BUTTON_PLAY)
45 # define MAZE_RIGHT BUTTON_RIGHT
46 # define MAZE_RIGHT_REPEAT BUTTON_RIGHT|BUTTON_REPEAT
47 # define MAZE_LEFT BUTTON_LEFT
48 # define MAZE_LEFT_REPEAT BUTTON_LEFT|BUTTON_REPEAT
49 # define MAZE_UP BUTTON_SCROLL_BACK
50 # define MAZE_UP_REPEAT BUTTON_SCROLL_BACK|BUTTON_REPEAT
51 # define MAZE_DOWN BUTTON_SCROLL_FWD
52 # define MAZE_DOWN_REPEAT BUTTON_SCROLL_FWD|BUTTON_REPEAT
55 # include "lib/pluginlib_actions.h"
56 # define MAZE_NEW PLA_SELECT_REPEAT
57 # define MAZE_QUIT PLA_CANCEL
58 # define MAZE_SOLVE PLA_SELECT_REL
59 # define MAZE_RIGHT PLA_RIGHT
60 # define MAZE_RIGHT_REPEAT PLA_RIGHT_REPEAT
61 # define MAZE_LEFT PLA_LEFT
62 # define MAZE_LEFT_REPEAT PLA_LEFT_REPEAT
63 # define MAZE_UP PLA_UP
64 # define MAZE_UP_REPEAT PLA_UP_REPEAT
65 # define MAZE_DOWN PLA_DOWN
66 # define MAZE_DOWN_REPEAT PLA_DOWN_REPEAT
67 static const struct button_mapping
*plugin_contexts
[]
72 /* cell property bits */
77 #define WALL_ALL (WALL_N | WALL_E | WALL_S | WALL_W)
81 #define BORDER_N(y) ((y) == 0)
82 #define BORDER_E(x) ((x) == MAZE_WIDTH-1)
83 #define BORDER_S(y) ((y) == MAZE_HEIGHT-1)
84 #define BORDER_W(x) ((x) == 0)
86 // we can and should change this to make square boxes
87 #if ( LCD_WIDTH == 112 )
89 #define MAZE_HEIGHT 12
90 #elif( LCD_WIDTH == 132 )
92 #define MAZE_HEIGHT 16
95 #define MAZE_HEIGHT 24
104 uint8_t maze
[MAZE_WIDTH
][MAZE_HEIGHT
];
107 static void maze_init(struct maze
* maze
)
111 /* initialize the properties */
112 maze
->show_path
= false;
113 maze
->solved
= false;
117 /* all walls are up */
118 for(y
=0; y
<MAZE_HEIGHT
; y
++){
119 for(x
=0; x
<MAZE_WIDTH
; x
++){
120 maze
->maze
[x
][y
] = WALL_ALL
;
125 static void maze_draw(struct maze
* maze
, struct screen
* display
)
129 int point_width
, point_height
, point_offset_x
, point_offset_y
;
132 /* calculate the size variables */
134 wx
= (int) display
->lcdwidth
/ MAZE_WIDTH
;
135 wy
= (int) display
->lcdheight
/ MAZE_HEIGHT
;
155 display
->clear_display();
158 for(y
=0; y
<MAZE_HEIGHT
; y
++){
159 for(x
=0; x
<MAZE_WIDTH
; x
++){
160 cell
= maze
->maze
[x
][y
];
162 display
->hline(x
*wx
, x
*wx
+wx
, y
*wy
);
164 display
->vline(x
*wx
+wx
, y
*wy
, y
*wy
+wy
);
166 display
->hline(x
*wx
, x
*wx
+wx
, y
*wy
+wy
);
168 display
->vline(x
*wx
, y
*wy
, y
*wy
+wy
);
175 if(display
->depth
>=16)
176 display
->set_foreground(LCD_RGBPACK(127,127,127));
179 if(display
->depth
==2)
180 display
->set_foreground(1);
183 /* highlight the path */
184 for(y
=0; y
<MAZE_HEIGHT
; y
++){
185 for(x
=0; x
<MAZE_WIDTH
; x
++){
186 cell
= maze
->maze
[x
][y
];
188 display
->fillrect(x
*wx
+point_offset_x
,
190 point_width
, point_height
);
194 /* link the cells in the path together */
195 for(y
=0; y
<MAZE_HEIGHT
; y
++){
196 for(x
=0; x
<MAZE_WIDTH
; x
++){
197 cell
= maze
->maze
[x
][y
];
199 if(!(cell
& WALL_N
) && (maze
->maze
[x
][y
-1] & PATH
))
200 display
->fillrect(x
*wx
+point_offset_x
,
202 point_width
, wy
-point_height
);
203 if(!(cell
& WALL_E
) && (maze
->maze
[x
+1][y
] & PATH
))
204 display
->fillrect(x
*wx
+wx
-point_offset_x
,
206 wx
-point_width
, point_height
);
207 if(!(cell
& WALL_S
) && (maze
->maze
[x
][y
+1] & PATH
))
208 display
->fillrect(x
*wx
+point_offset_x
,
209 y
*wy
+wy
-point_offset_y
,
210 point_width
, wy
-point_height
);
211 if(!(cell
& WALL_W
) && (maze
->maze
[x
-1][y
] & PATH
))
212 display
->fillrect(x
*wx
,
214 wx
-point_width
, point_height
);
220 if(display
->depth
>=16)
221 display
->set_foreground(LCD_RGBPACK(0,0,0));
224 if(display
->depth
==2)
225 display
->set_foreground(0);
229 /* mark start and end */
230 display
->drawline(0, 0, wx
, wy
);
231 display
->drawline(0, wy
, wx
, 0);
232 display
->drawline((MAZE_WIDTH
-1)*wx
,(MAZE_HEIGHT
-1)*wy
,
233 (MAZE_WIDTH
-1)*wx
+wx
, (MAZE_HEIGHT
-1)*wy
+wy
);
234 display
->drawline((MAZE_WIDTH
-1)*wx
,(MAZE_HEIGHT
-1)*wy
+wy
,
235 (MAZE_WIDTH
-1)*wx
+wx
, (MAZE_HEIGHT
-1)*wy
);
237 /* draw current position */
238 display
->fillrect(maze
->player_x
*wx
+point_offset_x
,
239 maze
->player_y
*wy
+point_offset_y
,
240 point_width
, point_height
);
242 /* update the display */
249 uint8_t x
[MAZE_WIDTH
*MAZE_HEIGHT
];
250 uint8_t y
[MAZE_WIDTH
*MAZE_HEIGHT
];
254 static void coord_stack_init(struct coord_stack
* stack
)
256 rb
->memset(stack
->x
, 0, sizeof(stack
->x
));
257 rb
->memset(stack
->y
, 0, sizeof(stack
->y
));
261 static void coord_stack_push(struct coord_stack
* stack
, int x
, int y
)
263 stack
->x
[stack
->stp
] = x
;
264 stack
->y
[stack
->stp
] = y
;
268 static void coord_stack_pop(struct coord_stack
* stack
, int* x
, int* y
)
271 *x
= stack
->x
[stack
->stp
];
272 *y
= stack
->y
[stack
->stp
];
275 static int maze_pick_random_neighbour_cell_with_walls(struct maze
* maze
,
276 int x
, int y
, int *pnx
, int *pny
)
283 /* look for neighbours with all walls set up */
285 if(!BORDER_N(y
) && ((maze
->maze
[x
][y
-1] & WALL_ALL
) == WALL_ALL
)){
291 if(!BORDER_E(x
) && ((maze
->maze
[x
+1][y
] & WALL_ALL
) == WALL_ALL
)){
297 if(!BORDER_S(y
) && ((maze
->maze
[x
][y
+1] & WALL_ALL
) == WALL_ALL
)){
303 if(!BORDER_W(x
) && ((maze
->maze
[x
-1][y
] & WALL_ALL
) == WALL_ALL
)){
309 /* then choose one */
319 /* Removes the wall between the cell (x,y) and the cell (nx,ny) */
320 static void maze_remove_wall(struct maze
* maze
, int x
, int y
, int nx
, int ny
)
322 /* where is our neighbour? */
328 maze
->maze
[x
][y
] &= ~WALL_S
;
329 maze
->maze
[nx
][ny
] &= ~WALL_N
;
332 maze
->maze
[x
][y
] &= ~WALL_N
;
333 maze
->maze
[nx
][ny
] &= ~WALL_S
;
340 maze
->maze
[x
][y
] &= ~WALL_E
;
341 maze
->maze
[nx
][ny
] &= ~WALL_W
;
344 maze
->maze
[x
][y
] &= ~WALL_W
;
345 maze
->maze
[nx
][ny
] &= ~WALL_E
;
351 static void maze_generate(struct maze
* maze
)
353 int total_cells
= MAZE_WIDTH
* MAZE_HEIGHT
;
355 int available_neighbours
;
359 struct coord_stack done_cells
;
361 coord_stack_init(&done_cells
);
363 x
= rb
->rand()%MAZE_WIDTH
;
364 y
= rb
->rand()%MAZE_HEIGHT
;
367 while (visited_cells
< total_cells
){
368 available_neighbours
=
369 maze_pick_random_neighbour_cell_with_walls(maze
, x
, y
, &nx
, &ny
);
370 if(available_neighbours
== 0){
372 coord_stack_pop(&done_cells
, &x
, &y
);
374 /* remove the wall */
375 maze_remove_wall(maze
, x
, y
, nx
, ny
);
376 /* save our position on the stack */
377 coord_stack_push(&done_cells
, x
, y
);
378 /* move to the next cell */
381 /* keep track of visited cells count */
388 static void maze_solve(struct maze
* maze
)
394 uint8_t solved_maze
[MAZE_WIDTH
][MAZE_HEIGHT
];
396 /* toggle the visibility of the path */
397 maze
->show_path
= ~(maze
->show_path
);
399 /* no need to solve the maze if already solved */
403 /* work on a copy of the maze */
404 rb
->memcpy(solved_maze
, maze
->maze
, sizeof(maze
->maze
));
406 /* remove walls on start and end point */
407 solved_maze
[0][0] &= ~WALL_N
;
408 solved_maze
[MAZE_WIDTH
-1][MAZE_HEIGHT
-1] &= ~WALL_S
;
410 /* first, mark all the cells as reachable */
411 for(y
=0; y
<MAZE_HEIGHT
; y
++){
412 for(x
=0; x
<MAZE_WIDTH
; x
++){
413 solved_maze
[x
][y
] |= PATH
;
419 /* solve by blocking off dead ends -- backward approach */
421 /* scan for dead ends */
422 for(y
=0; y
<MAZE_HEIGHT
; y
++){
424 for(x
=0; x
<MAZE_WIDTH
; x
++){
425 cell
= solved_maze
[x
][y
];
426 wall
= cell
& WALL_ALL
;
427 if((wall
== (WALL_E
| WALL_S
| WALL_W
)) ||
428 (wall
== (WALL_N
| WALL_S
| WALL_W
)) ||
429 (wall
== (WALL_N
| WALL_E
| WALL_W
)) ||
430 (wall
== (WALL_N
| WALL_E
| WALL_S
))){
431 /* found dead end, clear path bit and set all its walls */
432 solved_maze
[x
][y
] &= ~PATH
;
433 solved_maze
[x
][y
] |= WALL_ALL
;
434 /* don't forget the neighbours */
436 solved_maze
[x
][y
+1] |= WALL_N
;
438 solved_maze
[x
-1][y
] |= WALL_E
;
440 solved_maze
[x
][y
-1] |= WALL_S
;
442 solved_maze
[x
+1][y
] |= WALL_W
;
449 /* copy all the path bits to the maze */
450 for(y
=0; y
<MAZE_HEIGHT
; y
++){
451 for(x
=0; x
<MAZE_WIDTH
; x
++){
452 maze
->maze
[x
][y
] |= solved_maze
[x
][y
] & PATH
;
456 /* mark the maze as solved */
460 static void maze_move_player_up(struct maze
* maze
)
462 uint8_t cell
= maze
->maze
[maze
->player_x
][maze
->player_y
];
463 if(!BORDER_N(maze
->player_y
) && !(cell
& WALL_N
))
467 static void maze_move_player_right(struct maze
* maze
)
469 uint8_t cell
= maze
->maze
[maze
->player_x
][maze
->player_y
];
470 if(!BORDER_E(maze
->player_x
) && !(cell
& WALL_E
))
474 static void maze_move_player_down(struct maze
* maze
)
476 uint8_t cell
= maze
->maze
[maze
->player_x
][maze
->player_y
];
477 if(!BORDER_S(maze
->player_y
) && !(cell
& WALL_S
))
481 static void maze_move_player_left(struct maze
* maze
)
483 uint8_t cell
= maze
->maze
[maze
->player_x
][maze
->player_y
];
484 if(!BORDER_W(maze
->player_x
) && !(cell
& WALL_W
))
488 /**********************************/
489 /* this is the plugin entry point */
490 /**********************************/
491 enum plugin_status
plugin_start(const void* parameter
)
495 int lastbutton
= BUTTON_NONE
;
502 /* Turn off backlight timeout */
503 backlight_ignore_timeout();
506 rb
->srand(*rb
->current_tick
);
509 rb
->screens
[i
]->set_viewport(NULL
);
511 /* Draw the background */
513 rb
->lcd_set_backdrop(NULL
);
515 rb
->lcd_set_foreground(LCD_RGBPACK( 0, 0, 0));
516 rb
->lcd_set_background(LCD_RGBPACK(182, 198, 229)); /* rockbox blue */
518 rb
->lcd_set_foreground(0);
519 rb
->lcd_set_background(LCD_DEFAULT_BG
);
523 /* Initialize and draw the maze */
525 maze_generate(&maze
);
527 maze_draw(&maze
, rb
->screens
[i
]);
530 #ifdef __PLUGINLIB_ACTIONS_H__
531 button
= pluginlib_getaction(TIMEOUT_BLOCK
, plugin_contexts
,
532 ARRAYLEN(plugin_contexts
));
534 button
= rb
->button_get(true);
539 if(lastbutton
!= MAZE_NEW_PRE
)
543 maze_generate(&maze
);
545 maze_draw(&maze
, rb
->screens
[i
]);
550 maze_draw(&maze
, rb
->screens
[i
]);
554 maze_move_player_up(&maze
);
556 maze_draw(&maze
, rb
->screens
[i
]);
559 case MAZE_RIGHT_REPEAT
:
560 maze_move_player_right(&maze
);
562 maze_draw(&maze
, rb
->screens
[i
]);
565 case MAZE_DOWN_REPEAT
:
566 maze_move_player_down(&maze
);
568 maze_draw(&maze
, rb
->screens
[i
]);
571 case MAZE_LEFT_REPEAT
:
572 maze_move_player_left(&maze
);
574 maze_draw(&maze
, rb
->screens
[i
]);
581 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
) {
588 if( button
!= BUTTON_NONE
)
592 /* Turn on backlight timeout (revert to settings) */
593 backlight_use_settings();
594 return ((quit
== 1) ? PLUGIN_OK
: PLUGIN_USB_CONNECTED
);