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)
89 static const struct plugin_api
* rb
;
91 // we can and should change this to make square boxes
92 #if ( LCD_WIDTH == 112 )
94 #define MAZE_HEIGHT 12
95 #elif( LCD_WIDTH == 132 )
97 #define MAZE_HEIGHT 16
100 #define MAZE_HEIGHT 24
109 uint8_t maze
[MAZE_WIDTH
][MAZE_HEIGHT
];
112 static void maze_init(struct maze
* maze
)
116 /* initialize the properties */
117 maze
->show_path
= false;
118 maze
->solved
= false;
122 /* all walls are up */
123 for(y
=0; y
<MAZE_HEIGHT
; y
++){
124 for(x
=0; x
<MAZE_WIDTH
; x
++){
125 maze
->maze
[x
][y
] = WALL_ALL
;
130 static void maze_draw(struct maze
* maze
, struct screen
* display
)
134 int point_width
, point_height
, point_offset_x
, point_offset_y
;
137 /* calculate the size variables */
139 wx
= (int) display
->lcdwidth
/ MAZE_WIDTH
;
140 wy
= (int) display
->lcdheight
/ MAZE_HEIGHT
;
160 display
->clear_display();
163 for(y
=0; y
<MAZE_HEIGHT
; y
++){
164 for(x
=0; x
<MAZE_WIDTH
; x
++){
165 cell
= maze
->maze
[x
][y
];
167 display
->hline(x
*wx
, x
*wx
+wx
, y
*wy
);
169 display
->vline(x
*wx
+wx
, y
*wy
, y
*wy
+wy
);
171 display
->hline(x
*wx
, x
*wx
+wx
, y
*wy
+wy
);
173 display
->vline(x
*wx
, y
*wy
, y
*wy
+wy
);
180 if(display
->depth
>=16)
181 display
->set_foreground(LCD_RGBPACK(127,127,127));
184 if(display
->depth
==2)
185 display
->set_foreground(1);
188 /* highlight the path */
189 for(y
=0; y
<MAZE_HEIGHT
; y
++){
190 for(x
=0; x
<MAZE_WIDTH
; x
++){
191 cell
= maze
->maze
[x
][y
];
193 display
->fillrect(x
*wx
+point_offset_x
,
195 point_width
, point_height
);
199 /* link the cells in the path together */
200 for(y
=0; y
<MAZE_HEIGHT
; y
++){
201 for(x
=0; x
<MAZE_WIDTH
; x
++){
202 cell
= maze
->maze
[x
][y
];
204 if(!(cell
& WALL_N
) && (maze
->maze
[x
][y
-1] & PATH
))
205 display
->fillrect(x
*wx
+point_offset_x
,
207 point_width
, wy
-point_height
);
208 if(!(cell
& WALL_E
) && (maze
->maze
[x
+1][y
] & PATH
))
209 display
->fillrect(x
*wx
+wx
-point_offset_x
,
211 wx
-point_width
, point_height
);
212 if(!(cell
& WALL_S
) && (maze
->maze
[x
][y
+1] & PATH
))
213 display
->fillrect(x
*wx
+point_offset_x
,
214 y
*wy
+wy
-point_offset_y
,
215 point_width
, wy
-point_height
);
216 if(!(cell
& WALL_W
) && (maze
->maze
[x
-1][y
] & PATH
))
217 display
->fillrect(x
*wx
,
219 wx
-point_width
, point_height
);
225 if(display
->depth
>=16)
226 display
->set_foreground(LCD_RGBPACK(0,0,0));
229 if(display
->depth
==2)
230 display
->set_foreground(0);
234 /* mark start and end */
235 display
->drawline(0, 0, wx
, wy
);
236 display
->drawline(0, wy
, wx
, 0);
237 display
->drawline((MAZE_WIDTH
-1)*wx
,(MAZE_HEIGHT
-1)*wy
,
238 (MAZE_WIDTH
-1)*wx
+wx
, (MAZE_HEIGHT
-1)*wy
+wy
);
239 display
->drawline((MAZE_WIDTH
-1)*wx
,(MAZE_HEIGHT
-1)*wy
+wy
,
240 (MAZE_WIDTH
-1)*wx
+wx
, (MAZE_HEIGHT
-1)*wy
);
242 /* draw current position */
243 display
->fillrect(maze
->player_x
*wx
+point_offset_x
,
244 maze
->player_y
*wy
+point_offset_y
,
245 point_width
, point_height
);
247 /* update the display */
254 uint8_t x
[MAZE_WIDTH
*MAZE_HEIGHT
];
255 uint8_t y
[MAZE_WIDTH
*MAZE_HEIGHT
];
259 static void coord_stack_init(struct coord_stack
* stack
)
261 rb
->memset(stack
->x
, 0, sizeof(stack
->x
));
262 rb
->memset(stack
->y
, 0, sizeof(stack
->y
));
266 static void coord_stack_push(struct coord_stack
* stack
, int x
, int y
)
268 stack
->x
[stack
->stp
] = x
;
269 stack
->y
[stack
->stp
] = y
;
273 static void coord_stack_pop(struct coord_stack
* stack
, int* x
, int* y
)
276 *x
= stack
->x
[stack
->stp
];
277 *y
= stack
->y
[stack
->stp
];
280 static int maze_pick_random_neighbour_cell_with_walls(struct maze
* maze
,
281 int x
, int y
, int *pnx
, int *pny
)
288 /* look for neighbours with all walls set up */
290 if(!BORDER_N(y
) && ((maze
->maze
[x
][y
-1] & WALL_ALL
) == WALL_ALL
)){
296 if(!BORDER_E(x
) && ((maze
->maze
[x
+1][y
] & WALL_ALL
) == WALL_ALL
)){
302 if(!BORDER_S(y
) && ((maze
->maze
[x
][y
+1] & WALL_ALL
) == WALL_ALL
)){
308 if(!BORDER_W(x
) && ((maze
->maze
[x
-1][y
] & WALL_ALL
) == WALL_ALL
)){
314 /* then choose one */
324 /* Removes the wall between the cell (x,y) and the cell (nx,ny) */
325 static void maze_remove_wall(struct maze
* maze
, int x
, int y
, int nx
, int ny
)
327 /* where is our neighbour? */
333 maze
->maze
[x
][y
] &= ~WALL_S
;
334 maze
->maze
[nx
][ny
] &= ~WALL_N
;
337 maze
->maze
[x
][y
] &= ~WALL_N
;
338 maze
->maze
[nx
][ny
] &= ~WALL_S
;
345 maze
->maze
[x
][y
] &= ~WALL_E
;
346 maze
->maze
[nx
][ny
] &= ~WALL_W
;
349 maze
->maze
[x
][y
] &= ~WALL_W
;
350 maze
->maze
[nx
][ny
] &= ~WALL_E
;
356 static void maze_generate(struct maze
* maze
)
358 int total_cells
= MAZE_WIDTH
* MAZE_HEIGHT
;
360 int available_neighbours
;
364 struct coord_stack done_cells
;
366 coord_stack_init(&done_cells
);
368 x
= rb
->rand()%MAZE_WIDTH
;
369 y
= rb
->rand()%MAZE_HEIGHT
;
372 while (visited_cells
< total_cells
){
373 available_neighbours
=
374 maze_pick_random_neighbour_cell_with_walls(maze
, x
, y
, &nx
, &ny
);
375 if(available_neighbours
== 0){
377 coord_stack_pop(&done_cells
, &x
, &y
);
379 /* remove the wall */
380 maze_remove_wall(maze
, x
, y
, nx
, ny
);
381 /* save our position on the stack */
382 coord_stack_push(&done_cells
, x
, y
);
383 /* move to the next cell */
386 /* keep track of visited cells count */
393 static void maze_solve(struct maze
* maze
)
399 uint8_t solved_maze
[MAZE_WIDTH
][MAZE_HEIGHT
];
401 /* toggle the visibility of the path */
402 maze
->show_path
= ~(maze
->show_path
);
404 /* no need to solve the maze if already solved */
408 /* work on a copy of the maze */
409 rb
->memcpy(solved_maze
, maze
->maze
, sizeof(maze
->maze
));
411 /* remove walls on start and end point */
412 solved_maze
[0][0] &= ~WALL_N
;
413 solved_maze
[MAZE_WIDTH
-1][MAZE_HEIGHT
-1] &= ~WALL_S
;
415 /* first, mark all the cells as reachable */
416 for(y
=0; y
<MAZE_HEIGHT
; y
++){
417 for(x
=0; x
<MAZE_WIDTH
; x
++){
418 solved_maze
[x
][y
] |= PATH
;
424 /* solve by blocking off dead ends -- backward approach */
426 /* scan for dead ends */
427 for(y
=0; y
<MAZE_HEIGHT
; y
++){
429 for(x
=0; x
<MAZE_WIDTH
; x
++){
430 cell
= solved_maze
[x
][y
];
431 wall
= cell
& WALL_ALL
;
432 if((wall
== (WALL_E
| WALL_S
| WALL_W
)) ||
433 (wall
== (WALL_N
| WALL_S
| WALL_W
)) ||
434 (wall
== (WALL_N
| WALL_E
| WALL_W
)) ||
435 (wall
== (WALL_N
| WALL_E
| WALL_S
))){
436 /* found dead end, clear path bit and set all its walls */
437 solved_maze
[x
][y
] &= ~PATH
;
438 solved_maze
[x
][y
] |= WALL_ALL
;
439 /* don't forget the neighbours */
441 solved_maze
[x
][y
+1] |= WALL_N
;
443 solved_maze
[x
-1][y
] |= WALL_E
;
445 solved_maze
[x
][y
-1] |= WALL_S
;
447 solved_maze
[x
+1][y
] |= WALL_W
;
454 /* copy all the path bits to the maze */
455 for(y
=0; y
<MAZE_HEIGHT
; y
++){
456 for(x
=0; x
<MAZE_WIDTH
; x
++){
457 maze
->maze
[x
][y
] |= solved_maze
[x
][y
] & PATH
;
461 /* mark the maze as solved */
465 static void maze_move_player_up(struct maze
* maze
)
467 uint8_t cell
= maze
->maze
[maze
->player_x
][maze
->player_y
];
468 if(!BORDER_N(maze
->player_y
) && !(cell
& WALL_N
))
472 static void maze_move_player_right(struct maze
* maze
)
474 uint8_t cell
= maze
->maze
[maze
->player_x
][maze
->player_y
];
475 if(!BORDER_E(maze
->player_x
) && !(cell
& WALL_E
))
479 static void maze_move_player_down(struct maze
* maze
)
481 uint8_t cell
= maze
->maze
[maze
->player_x
][maze
->player_y
];
482 if(!BORDER_S(maze
->player_y
) && !(cell
& WALL_S
))
486 static void maze_move_player_left(struct maze
* maze
)
488 uint8_t cell
= maze
->maze
[maze
->player_x
][maze
->player_y
];
489 if(!BORDER_W(maze
->player_x
) && !(cell
& WALL_W
))
493 /**********************************/
494 /* this is the plugin entry point */
495 /**********************************/
496 enum plugin_status
plugin_start(const struct plugin_api
* api
, const void* parameter
)
498 int button
, lastbutton
= BUTTON_NONE
;
505 /* Turn off backlight timeout */
506 backlight_force_on(rb
); /* backlight control in lib/helper.c */
509 rb
->srand(*rb
->current_tick
);
512 rb
->screens
[i
]->set_viewport(NULL
);
514 /* Draw the background */
516 rb
->lcd_set_backdrop(NULL
);
518 rb
->lcd_set_foreground(LCD_RGBPACK( 0, 0, 0));
519 rb
->lcd_set_background(LCD_RGBPACK(182, 198, 229)); /* rockbox blue */
521 rb
->lcd_set_foreground(0);
522 rb
->lcd_set_background(LCD_DEFAULT_BG
);
526 /* Initialize and draw the maze */
528 maze_generate(&maze
);
530 maze_draw(&maze
, rb
->screens
[i
]);
533 #ifdef __PLUGINLIB_ACTIONS_H__
534 button
= pluginlib_getaction(rb
, TIMEOUT_BLOCK
, plugin_contexts
, 2);
536 button
= rb
->button_get(true);
541 if(lastbutton
!= MAZE_NEW_PRE
)
545 maze_generate(&maze
);
547 maze_draw(&maze
, rb
->screens
[i
]);
552 maze_draw(&maze
, rb
->screens
[i
]);
556 maze_move_player_up(&maze
);
558 maze_draw(&maze
, rb
->screens
[i
]);
562 maze_move_player_right(&maze
);
564 maze_draw(&maze
, rb
->screens
[i
]);
568 maze_move_player_down(&maze
);
570 maze_draw(&maze
, rb
->screens
[i
]);
574 maze_move_player_left(&maze
);
576 maze_draw(&maze
, rb
->screens
[i
]);
583 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
) {
589 if( button
!= BUTTON_NONE
)
593 /* Turn on backlight timeout (revert to settings) */
594 backlight_use_settings(rb
); /* backlight control in lib/helper.c */
595 return ((quit
== 1) ? PLUGIN_OK
: PLUGIN_USB_CONNECTED
);