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_4G_PAD) || \
41 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
42 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
43 # define MAZE_NEW (BUTTON_SELECT | BUTTON_REPEAT)
44 # define MAZE_NEW_PRE BUTTON_SELECT
45 # define MAZE_QUIT (BUTTON_SELECT | BUTTON_MENU)
46 # define MAZE_SOLVE (BUTTON_SELECT | BUTTON_PLAY)
47 # define MAZE_RIGHT BUTTON_RIGHT
48 # define MAZE_LEFT BUTTON_LEFT
49 # define MAZE_UP BUTTON_MENU
50 # define MAZE_DOWN BUTTON_PLAY
51 # define MAZE_RRIGHT (BUTTON_RIGHT | BUTTON_REPEAT)
52 # define MAZE_RLEFT (BUTTON_LEFT | BUTTON_REPEAT)
53 # define MAZE_RUP (BUTTON_MENU | BUTTON_REPEAT)
54 # define MAZE_RDOWN (BUTTON_PLAY | BUTTON_REPEAT)
57 # include "lib/pluginlib_actions.h"
58 # define MAZE_NEW PLA_START
59 # define MAZE_QUIT PLA_QUIT
60 # define MAZE_SOLVE PLA_FIRE
61 # define MAZE_RIGHT PLA_RIGHT
62 # define MAZE_LEFT PLA_LEFT
63 # define MAZE_UP PLA_UP
64 # define MAZE_DOWN PLA_DOWN
65 # define MAZE_RRIGHT PLA_RIGHT_REPEAT
66 # define MAZE_RLEFT PLA_LEFT_REPEAT
67 # define MAZE_RUP PLA_UP_REPEAT
68 # define MAZE_RDOWN PLA_DOWN_REPEAT
69 static const struct button_mapping
*plugin_contexts
[]
70 = {generic_directions
, generic_actions
};
74 /* cell property bits */
79 #define WALL_ALL (WALL_N | WALL_E | WALL_S | WALL_W)
83 #define BORDER_N(y) ((y) == 0)
84 #define BORDER_E(x) ((x) == MAZE_WIDTH-1)
85 #define BORDER_S(y) ((y) == MAZE_HEIGHT-1)
86 #define BORDER_W(x) ((x) == 0)
88 // we can and should change this to make square boxes
89 #if ( LCD_WIDTH == 112 )
91 #define MAZE_HEIGHT 12
92 #elif( LCD_WIDTH == 132 )
94 #define MAZE_HEIGHT 16
97 #define MAZE_HEIGHT 24
106 uint8_t maze
[MAZE_WIDTH
][MAZE_HEIGHT
];
109 static void maze_init(struct maze
* maze
)
113 /* initialize the properties */
114 maze
->show_path
= false;
115 maze
->solved
= false;
119 /* all walls are up */
120 for(y
=0; y
<MAZE_HEIGHT
; y
++){
121 for(x
=0; x
<MAZE_WIDTH
; x
++){
122 maze
->maze
[x
][y
] = WALL_ALL
;
127 static void maze_draw(struct maze
* maze
, struct screen
* display
)
131 int point_width
, point_height
, point_offset_x
, point_offset_y
;
134 /* calculate the size variables */
136 wx
= (int) display
->lcdwidth
/ MAZE_WIDTH
;
137 wy
= (int) display
->lcdheight
/ MAZE_HEIGHT
;
157 display
->clear_display();
160 for(y
=0; y
<MAZE_HEIGHT
; y
++){
161 for(x
=0; x
<MAZE_WIDTH
; x
++){
162 cell
= maze
->maze
[x
][y
];
164 display
->hline(x
*wx
, x
*wx
+wx
, y
*wy
);
166 display
->vline(x
*wx
+wx
, y
*wy
, y
*wy
+wy
);
168 display
->hline(x
*wx
, x
*wx
+wx
, y
*wy
+wy
);
170 display
->vline(x
*wx
, y
*wy
, y
*wy
+wy
);
177 if(display
->depth
>=16)
178 display
->set_foreground(LCD_RGBPACK(127,127,127));
181 if(display
->depth
==2)
182 display
->set_foreground(1);
185 /* highlight the path */
186 for(y
=0; y
<MAZE_HEIGHT
; y
++){
187 for(x
=0; x
<MAZE_WIDTH
; x
++){
188 cell
= maze
->maze
[x
][y
];
190 display
->fillrect(x
*wx
+point_offset_x
,
192 point_width
, point_height
);
196 /* link the cells in the path together */
197 for(y
=0; y
<MAZE_HEIGHT
; y
++){
198 for(x
=0; x
<MAZE_WIDTH
; x
++){
199 cell
= maze
->maze
[x
][y
];
201 if(!(cell
& WALL_N
) && (maze
->maze
[x
][y
-1] & PATH
))
202 display
->fillrect(x
*wx
+point_offset_x
,
204 point_width
, wy
-point_height
);
205 if(!(cell
& WALL_E
) && (maze
->maze
[x
+1][y
] & PATH
))
206 display
->fillrect(x
*wx
+wx
-point_offset_x
,
208 wx
-point_width
, point_height
);
209 if(!(cell
& WALL_S
) && (maze
->maze
[x
][y
+1] & PATH
))
210 display
->fillrect(x
*wx
+point_offset_x
,
211 y
*wy
+wy
-point_offset_y
,
212 point_width
, wy
-point_height
);
213 if(!(cell
& WALL_W
) && (maze
->maze
[x
-1][y
] & PATH
))
214 display
->fillrect(x
*wx
,
216 wx
-point_width
, point_height
);
222 if(display
->depth
>=16)
223 display
->set_foreground(LCD_RGBPACK(0,0,0));
226 if(display
->depth
==2)
227 display
->set_foreground(0);
231 /* mark start and end */
232 display
->drawline(0, 0, wx
, wy
);
233 display
->drawline(0, wy
, wx
, 0);
234 display
->drawline((MAZE_WIDTH
-1)*wx
,(MAZE_HEIGHT
-1)*wy
,
235 (MAZE_WIDTH
-1)*wx
+wx
, (MAZE_HEIGHT
-1)*wy
+wy
);
236 display
->drawline((MAZE_WIDTH
-1)*wx
,(MAZE_HEIGHT
-1)*wy
+wy
,
237 (MAZE_WIDTH
-1)*wx
+wx
, (MAZE_HEIGHT
-1)*wy
);
239 /* draw current position */
240 display
->fillrect(maze
->player_x
*wx
+point_offset_x
,
241 maze
->player_y
*wy
+point_offset_y
,
242 point_width
, point_height
);
244 /* update the display */
251 uint8_t x
[MAZE_WIDTH
*MAZE_HEIGHT
];
252 uint8_t y
[MAZE_WIDTH
*MAZE_HEIGHT
];
256 static void coord_stack_init(struct coord_stack
* stack
)
258 rb
->memset(stack
->x
, 0, sizeof(stack
->x
));
259 rb
->memset(stack
->y
, 0, sizeof(stack
->y
));
263 static void coord_stack_push(struct coord_stack
* stack
, int x
, int y
)
265 stack
->x
[stack
->stp
] = x
;
266 stack
->y
[stack
->stp
] = y
;
270 static void coord_stack_pop(struct coord_stack
* stack
, int* x
, int* y
)
273 *x
= stack
->x
[stack
->stp
];
274 *y
= stack
->y
[stack
->stp
];
277 static int maze_pick_random_neighbour_cell_with_walls(struct maze
* maze
,
278 int x
, int y
, int *pnx
, int *pny
)
285 /* look for neighbours with all walls set up */
287 if(!BORDER_N(y
) && ((maze
->maze
[x
][y
-1] & WALL_ALL
) == WALL_ALL
)){
293 if(!BORDER_E(x
) && ((maze
->maze
[x
+1][y
] & WALL_ALL
) == WALL_ALL
)){
299 if(!BORDER_S(y
) && ((maze
->maze
[x
][y
+1] & WALL_ALL
) == WALL_ALL
)){
305 if(!BORDER_W(x
) && ((maze
->maze
[x
-1][y
] & WALL_ALL
) == WALL_ALL
)){
311 /* then choose one */
321 /* Removes the wall between the cell (x,y) and the cell (nx,ny) */
322 static void maze_remove_wall(struct maze
* maze
, int x
, int y
, int nx
, int ny
)
324 /* where is our neighbour? */
330 maze
->maze
[x
][y
] &= ~WALL_S
;
331 maze
->maze
[nx
][ny
] &= ~WALL_N
;
334 maze
->maze
[x
][y
] &= ~WALL_N
;
335 maze
->maze
[nx
][ny
] &= ~WALL_S
;
342 maze
->maze
[x
][y
] &= ~WALL_E
;
343 maze
->maze
[nx
][ny
] &= ~WALL_W
;
346 maze
->maze
[x
][y
] &= ~WALL_W
;
347 maze
->maze
[nx
][ny
] &= ~WALL_E
;
353 static void maze_generate(struct maze
* maze
)
355 int total_cells
= MAZE_WIDTH
* MAZE_HEIGHT
;
357 int available_neighbours
;
361 struct coord_stack done_cells
;
363 coord_stack_init(&done_cells
);
365 x
= rb
->rand()%MAZE_WIDTH
;
366 y
= rb
->rand()%MAZE_HEIGHT
;
369 while (visited_cells
< total_cells
){
370 available_neighbours
=
371 maze_pick_random_neighbour_cell_with_walls(maze
, x
, y
, &nx
, &ny
);
372 if(available_neighbours
== 0){
374 coord_stack_pop(&done_cells
, &x
, &y
);
376 /* remove the wall */
377 maze_remove_wall(maze
, x
, y
, nx
, ny
);
378 /* save our position on the stack */
379 coord_stack_push(&done_cells
, x
, y
);
380 /* move to the next cell */
383 /* keep track of visited cells count */
390 static void maze_solve(struct maze
* maze
)
396 uint8_t solved_maze
[MAZE_WIDTH
][MAZE_HEIGHT
];
398 /* toggle the visibility of the path */
399 maze
->show_path
= ~(maze
->show_path
);
401 /* no need to solve the maze if already solved */
405 /* work on a copy of the maze */
406 rb
->memcpy(solved_maze
, maze
->maze
, sizeof(maze
->maze
));
408 /* remove walls on start and end point */
409 solved_maze
[0][0] &= ~WALL_N
;
410 solved_maze
[MAZE_WIDTH
-1][MAZE_HEIGHT
-1] &= ~WALL_S
;
412 /* first, mark all the cells as reachable */
413 for(y
=0; y
<MAZE_HEIGHT
; y
++){
414 for(x
=0; x
<MAZE_WIDTH
; x
++){
415 solved_maze
[x
][y
] |= PATH
;
421 /* solve by blocking off dead ends -- backward approach */
423 /* scan for dead ends */
424 for(y
=0; y
<MAZE_HEIGHT
; y
++){
426 for(x
=0; x
<MAZE_WIDTH
; x
++){
427 cell
= solved_maze
[x
][y
];
428 wall
= cell
& WALL_ALL
;
429 if((wall
== (WALL_E
| WALL_S
| WALL_W
)) ||
430 (wall
== (WALL_N
| WALL_S
| WALL_W
)) ||
431 (wall
== (WALL_N
| WALL_E
| WALL_W
)) ||
432 (wall
== (WALL_N
| WALL_E
| WALL_S
))){
433 /* found dead end, clear path bit and set all its walls */
434 solved_maze
[x
][y
] &= ~PATH
;
435 solved_maze
[x
][y
] |= WALL_ALL
;
436 /* don't forget the neighbours */
438 solved_maze
[x
][y
+1] |= WALL_N
;
440 solved_maze
[x
-1][y
] |= WALL_E
;
442 solved_maze
[x
][y
-1] |= WALL_S
;
444 solved_maze
[x
+1][y
] |= WALL_W
;
451 /* copy all the path bits to the maze */
452 for(y
=0; y
<MAZE_HEIGHT
; y
++){
453 for(x
=0; x
<MAZE_WIDTH
; x
++){
454 maze
->maze
[x
][y
] |= solved_maze
[x
][y
] & PATH
;
458 /* mark the maze as solved */
462 static void maze_move_player_up(struct maze
* maze
)
464 uint8_t cell
= maze
->maze
[maze
->player_x
][maze
->player_y
];
465 if(!BORDER_N(maze
->player_y
) && !(cell
& WALL_N
))
469 static void maze_move_player_right(struct maze
* maze
)
471 uint8_t cell
= maze
->maze
[maze
->player_x
][maze
->player_y
];
472 if(!BORDER_E(maze
->player_x
) && !(cell
& WALL_E
))
476 static void maze_move_player_down(struct maze
* maze
)
478 uint8_t cell
= maze
->maze
[maze
->player_x
][maze
->player_y
];
479 if(!BORDER_S(maze
->player_y
) && !(cell
& WALL_S
))
483 static void maze_move_player_left(struct maze
* maze
)
485 uint8_t cell
= maze
->maze
[maze
->player_x
][maze
->player_y
];
486 if(!BORDER_W(maze
->player_x
) && !(cell
& WALL_W
))
490 /**********************************/
491 /* this is the plugin entry point */
492 /**********************************/
493 enum plugin_status
plugin_start(const void* parameter
)
495 int button
, lastbutton
= BUTTON_NONE
;
501 /* Turn off backlight timeout */
502 backlight_force_on(); /* backlight control in lib/helper.c */
505 rb
->srand(*rb
->current_tick
);
508 rb
->screens
[i
]->set_viewport(NULL
);
510 /* Draw the background */
512 rb
->lcd_set_backdrop(NULL
);
514 rb
->lcd_set_foreground(LCD_RGBPACK( 0, 0, 0));
515 rb
->lcd_set_background(LCD_RGBPACK(182, 198, 229)); /* rockbox blue */
517 rb
->lcd_set_foreground(0);
518 rb
->lcd_set_background(LCD_DEFAULT_BG
);
522 /* Initialize and draw the maze */
524 maze_generate(&maze
);
526 maze_draw(&maze
, rb
->screens
[i
]);
529 #ifdef __PLUGINLIB_ACTIONS_H__
530 button
= pluginlib_getaction(TIMEOUT_BLOCK
, plugin_contexts
, 2);
532 button
= rb
->button_get(true);
537 if(lastbutton
!= MAZE_NEW_PRE
)
541 maze_generate(&maze
);
543 maze_draw(&maze
, rb
->screens
[i
]);
548 maze_draw(&maze
, rb
->screens
[i
]);
552 maze_move_player_up(&maze
);
554 maze_draw(&maze
, rb
->screens
[i
]);
558 maze_move_player_right(&maze
);
560 maze_draw(&maze
, rb
->screens
[i
]);
564 maze_move_player_down(&maze
);
566 maze_draw(&maze
, rb
->screens
[i
]);
570 maze_move_player_left(&maze
);
572 maze_draw(&maze
, rb
->screens
[i
]);
579 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
) {
585 if( button
!= BUTTON_NONE
)
589 /* Turn on backlight timeout (revert to settings) */
590 backlight_use_settings(); /* backlight control in lib/helper.c */
591 return ((quit
== 1) ? PLUGIN_OK
: PLUGIN_USB_CONNECTED
);