Save a few bytes by changing unit selection strategy
[kugel-rb.git] / apps / plugins / flipit.c
blobbdb9e68360e76a68e00180bc1c4f0a97470210f7
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Vicentini Martin
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include "plugin.h"
21 PLUGIN_HEADER
23 /* variable button definitions */
24 #if CONFIG_KEYPAD == RECORDER_PAD
25 #define FLIPIT_UP BUTTON_UP
26 #define FLIPIT_DOWN BUTTON_DOWN
27 #define FLIPIT_QUIT BUTTON_OFF
28 #define FLIPIT_SHUFFLE BUTTON_F1
29 #define FLIPIT_SOLVE BUTTON_F2
30 #define FLIPIT_STEP_BY_STEP BUTTON_F3
31 #define FLIPIT_TOGGLE BUTTON_PLAY
33 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
34 #define FLIPIT_UP BUTTON_UP
35 #define FLIPIT_DOWN BUTTON_DOWN
36 #define FLIPIT_QUIT BUTTON_OFF
37 #define FLIPIT_SHUFFLE BUTTON_F1
38 #define FLIPIT_SOLVE BUTTON_F2
39 #define FLIPIT_STEP_BY_STEP BUTTON_F3
40 #define FLIPIT_TOGGLE BUTTON_SELECT
42 #elif CONFIG_KEYPAD == PLAYER_PAD
43 #define FLIPIT_UP_PRE BUTTON_ON
44 #define FLIPIT_UP (BUTTON_ON | BUTTON_REL)
45 #define FLIPIT_DOWN BUTTON_MENU
46 #define FLIPIT_QUIT BUTTON_STOP
47 #define FLIPIT_SHUFFLE (BUTTON_ON | BUTTON_LEFT)
48 #define FLIPIT_SOLVE (BUTTON_ON | BUTTON_RIGHT)
49 #define FLIPIT_STEP_BY_STEP (BUTTON_ON | BUTTON_PLAY)
50 #define FLIPIT_TOGGLE BUTTON_PLAY
52 #elif CONFIG_KEYPAD == ONDIO_PAD
53 #define FLIPIT_UP BUTTON_UP
54 #define FLIPIT_DOWN BUTTON_DOWN
55 #define FLIPIT_QUIT BUTTON_OFF
56 #define FLIPIT_SHUFFLE (BUTTON_MENU | BUTTON_LEFT)
57 #define FLIPIT_SOLVE (BUTTON_MENU | BUTTON_UP)
58 #define FLIPIT_STEP_BY_STEP (BUTTON_MENU | BUTTON_RIGHT)
59 #define FLIPIT_TOGGLE_PRE BUTTON_MENU
60 #define FLIPIT_TOGGLE (BUTTON_MENU | BUTTON_REL)
62 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
63 (CONFIG_KEYPAD == IRIVER_H300_PAD)
64 #define FLIPIT_UP BUTTON_UP
65 #define FLIPIT_DOWN BUTTON_DOWN
66 #define FLIPIT_QUIT BUTTON_OFF
67 #define FLIPIT_SHUFFLE BUTTON_MODE
68 #define FLIPIT_SOLVE BUTTON_ON
69 #define FLIPIT_STEP_BY_STEP BUTTON_REC
70 #define FLIPIT_TOGGLE_PRE BUTTON_SELECT
71 #define FLIPIT_TOGGLE (BUTTON_SELECT | BUTTON_REL)
73 #define FLIPIT_RC_QUIT BUTTON_RC_STOP
75 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
76 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
77 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
79 #define FLIPIT_UP BUTTON_MENU
80 #define FLIPIT_DOWN BUTTON_PLAY
81 #define FLIPIT_QUIT (BUTTON_SELECT | BUTTON_MENU)
82 #define FLIPIT_SHUFFLE (BUTTON_SELECT | BUTTON_LEFT)
83 #define FLIPIT_SOLVE (BUTTON_SELECT | BUTTON_PLAY)
84 #define FLIPIT_STEP_BY_STEP (BUTTON_SELECT | BUTTON_RIGHT)
85 #define FLIPIT_TOGGLE_PRE BUTTON_SELECT
86 #define FLIPIT_TOGGLE (BUTTON_SELECT | BUTTON_REL)
88 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
90 #define FLIPIT_UP BUTTON_UP
91 #define FLIPIT_DOWN BUTTON_DOWN
92 #define FLIPIT_QUIT BUTTON_POWER
93 #define FLIPIT_SHUFFLE BUTTON_REC
94 #define FLIPIT_SOLVE_PRE BUTTON_PLAY
95 #define FLIPIT_SOLVE (BUTTON_PLAY | BUTTON_REPEAT)
96 #define FLIPIT_STEP_PRE BUTTON_PLAY
97 #define FLIPIT_STEP_BY_STEP (BUTTON_PLAY | BUTTON_REL)
98 #define FLIPIT_TOGGLE BUTTON_SELECT
100 #elif CONFIG_KEYPAD == GIGABEAT_PAD
102 #define FLIPIT_UP BUTTON_UP
103 #define FLIPIT_DOWN BUTTON_DOWN
104 #define FLIPIT_QUIT BUTTON_POWER
105 #define FLIPIT_SHUFFLE BUTTON_MENU
106 #define FLIPIT_SOLVE BUTTON_VOL_UP
107 #define FLIPIT_STEP_BY_STEP BUTTON_VOL_DOWN
108 #define FLIPIT_TOGGLE BUTTON_SELECT
110 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
111 (CONFIG_KEYPAD == SANSA_C200_PAD)
113 #define FLIPIT_UP BUTTON_UP
114 #define FLIPIT_DOWN BUTTON_DOWN
115 #define FLIPIT_QUIT BUTTON_POWER
116 #define FLIPIT_SHUFFLE (BUTTON_REC | BUTTON_LEFT)
117 #define FLIPIT_SOLVE (BUTTON_REC | BUTTON_RIGHT)
118 #define FLIPIT_STEP_BY_STEP (BUTTON_REC | BUTTON_SELECT)
119 #define FLIPIT_TOGGLE BUTTON_SELECT
121 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
123 #define FLIPIT_UP BUTTON_SCROLL_UP
124 #define FLIPIT_DOWN BUTTON_SCROLL_DOWN
125 #define FLIPIT_QUIT BUTTON_POWER
126 #define FLIPIT_SHUFFLE (BUTTON_PLAY | BUTTON_LEFT)
127 #define FLIPIT_SOLVE (BUTTON_PLAY | BUTTON_RIGHT)
128 #define FLIPIT_STEP_BY_STEP (BUTTON_PLAY | BUTTON_SCROLL_UP)
129 #define FLIPIT_TOGGLE_PRE BUTTON_REW
130 #define FLIPIT_TOGGLE (BUTTON_REW | BUTTON_REL)
132 #endif
134 static struct plugin_api* rb;
135 static int spots[20];
136 static int toggle[20];
137 static int cursor_pos, moves;
139 #ifdef HAVE_LCD_BITMAP
141 #include "flipit_cursor.h"
142 #include "flipit_tokens.h"
144 #define PANEL_HEIGHT 12
145 #define TK_WIDTH BMPWIDTH_flipit_cursor
146 #define TK_HEIGHT BMPHEIGHT_flipit_cursor
147 #define TK_SPACE MAX(0, MIN((LCD_WIDTH - 5*TK_WIDTH)/4, \
148 (LCD_HEIGHT - PANEL_HEIGHT - 4*TK_HEIGHT)/4))
149 #define GRID_WIDTH (5*TK_WIDTH + 4*TK_SPACE)
150 #define GRID_LEFT ((LCD_WIDTH - GRID_WIDTH)/2)
151 #define GRID_HEIGHT (4*TK_HEIGHT + 4*TK_SPACE) /* includes grid-panel space */
152 #define GRID_TOP MAX(0, ((LCD_HEIGHT - PANEL_HEIGHT - GRID_HEIGHT)/2))
154 /* draw a spot at the coordinates (x,y), range of p is 0-19 */
155 static void draw_spot(int p)
157 rb->lcd_bitmap_part( flipit_tokens, 0, spots[p] * TK_HEIGHT, TK_WIDTH,
158 GRID_LEFT + (p%5) * (TK_WIDTH+TK_SPACE),
159 GRID_TOP + (p/5) * (TK_HEIGHT+TK_SPACE),
160 TK_WIDTH, TK_HEIGHT );
163 /* draw the cursor at the current cursor position */
164 static void draw_cursor(void)
166 #ifdef HAVE_LCD_COLOR
167 rb->lcd_bitmap_transparent( flipit_cursor,
168 GRID_LEFT + (cursor_pos%5) * (TK_WIDTH+TK_SPACE),
169 GRID_TOP + (cursor_pos/5) * (TK_HEIGHT+TK_SPACE),
170 TK_WIDTH, TK_HEIGHT );
171 #else
172 rb->lcd_set_drawmode(DRMODE_FG);
173 rb->lcd_mono_bitmap( flipit_cursor,
174 GRID_LEFT + (cursor_pos%5) * (TK_WIDTH+TK_SPACE),
175 GRID_TOP + (cursor_pos/5) * (TK_HEIGHT+TK_SPACE),
176 TK_WIDTH, TK_HEIGHT );
177 rb->lcd_set_drawmode(DRMODE_SOLID);
178 #endif
181 /* draw the info panel ... duh */
182 static void draw_info_panel(void)
184 char s[32];
186 rb->lcd_set_drawmode( DRMODE_SOLID|DRMODE_INVERSEVID );
187 rb->lcd_fillrect( GRID_LEFT, GRID_TOP + 4*(TK_HEIGHT+TK_SPACE),
188 GRID_WIDTH, PANEL_HEIGHT );
189 rb->lcd_set_drawmode( DRMODE_SOLID );
190 rb->lcd_drawrect( GRID_LEFT, GRID_TOP + 4*(TK_HEIGHT+TK_SPACE),
191 GRID_WIDTH, PANEL_HEIGHT );
193 rb->snprintf( s, sizeof(s), "Flips: %d", moves );
194 rb->lcd_putsxy( (LCD_WIDTH - rb->lcd_getstringsize(s, NULL, NULL)) / 2,
195 GRID_TOP + 4*(TK_HEIGHT+TK_SPACE) + 2, s );
198 #else /* HAVE_LCD_CHARCELLS */
200 static const unsigned char tk_pat[4][7] = {
201 { 0x0e, 0x11, 0x0e, 0x00, 0x0e, 0x11, 0x0e }, /* white - white */
202 { 0x0e, 0x11, 0x0e, 0x00, 0x0e, 0x1f, 0x0e }, /* white - black */
203 { 0x0e, 0x1f, 0x0e, 0x00, 0x0e, 0x11, 0x0e }, /* black - white */
204 { 0x0e, 0x1f, 0x0e, 0x00, 0x0e, 0x1f, 0x0e } /* black - black */
207 static unsigned char cur_pat[7];
208 static unsigned long gfx_chars[5];
210 static void release_gfx(void)
212 int i;
214 for (i = 0; i < 5; i++)
215 if (gfx_chars[i])
216 rb->lcd_unlock_pattern(gfx_chars[i]);
219 static bool init_gfx(void)
221 int i;
223 for (i = 0; i < 5; i++) {
224 if ((gfx_chars[i] = rb->lcd_get_locked_pattern()) == 0) {
225 release_gfx();
226 return false;
229 for (i = 0; i < 4; i++)
230 rb->lcd_define_pattern(gfx_chars[i], tk_pat[i]);
231 return true;
234 /* draw a spot at the coordinates (x,y), range of p is 0-19 */
235 static void draw_spot(int p)
237 if ((p/5) & 1)
238 p -= 5;
240 rb->lcd_putc (p%5, p/10, gfx_chars[2*spots[p]+spots[p+5]]);
243 /* draw the cursor at the current cursor position */
244 static void draw_cursor(void)
246 if ((cursor_pos/5) & 1) {
247 rb->memcpy( cur_pat, tk_pat[2*spots[cursor_pos-5]+spots[cursor_pos]], 7 );
248 cur_pat[4] ^= 0x15;
249 cur_pat[6] ^= 0x11;
251 else {
252 rb->memcpy( cur_pat, tk_pat[2*spots[cursor_pos]+spots[cursor_pos+5]], 7 );
253 cur_pat[0] ^= 0x15;
254 cur_pat[2] ^= 0x11;
256 rb->lcd_define_pattern(gfx_chars[4], cur_pat);
257 rb->lcd_putc( cursor_pos%5, cursor_pos/10, gfx_chars[4] );
260 /* draw the info panel ... duh */
261 static void draw_info_panel(void)
263 char s[16];
265 rb->lcd_puts( 6, 0, "Flips" );
266 rb->snprintf( s, sizeof(s), "%d", moves );
267 rb->lcd_puts( 6, 1, s );
270 #endif /* LCD */
272 /* clear the cursor where it is */
273 static inline void clear_cursor(void)
275 draw_spot( cursor_pos );
278 /* check if the puzzle is finished */
279 static bool flipit_finished(void)
281 int i;
282 for (i=0; i<20; i++)
283 if (!spots[i])
284 return false;
285 clear_cursor();
286 return true;
289 /* draws the toggled spots */
290 static void flipit_toggle(void)
292 spots[cursor_pos] = 1-spots[cursor_pos];
293 toggle[cursor_pos] = 1-toggle[cursor_pos];
294 draw_spot(cursor_pos);
295 if (cursor_pos%5 > 0) {
296 spots[cursor_pos-1] = 1-spots[cursor_pos-1];
297 draw_spot(cursor_pos-1);
299 if (cursor_pos%5 < 4) {
300 spots[cursor_pos+1] = 1-spots[cursor_pos+1];
301 draw_spot(cursor_pos+1);
303 if (cursor_pos/5 > 0) {
304 spots[cursor_pos-5] = 1-spots[cursor_pos-5];
305 draw_spot(cursor_pos-5);
307 if (cursor_pos/5 < 3) {
308 spots[cursor_pos+5] = 1-spots[cursor_pos+5];
309 draw_spot(cursor_pos+5);
311 moves++;
313 draw_info_panel();
315 if (flipit_finished())
316 clear_cursor();
319 /* move the cursor in any direction */
320 static void move_cursor(int x, int y)
322 if (!(flipit_finished())) {
323 clear_cursor();
324 cursor_pos = ( x + 5 + cursor_pos%5 )%5
325 + ( ( y + 4 + cursor_pos/5 )%4 )*5;
326 draw_cursor();
328 rb->lcd_update();
331 /* initialize the board */
332 static void flipit_init(void)
334 int i;
336 rb->lcd_clear_display();
337 for (i=0; i<20; i++) {
338 spots[i]=1;
339 toggle[i]=1;
340 draw_spot(i);
342 rb->lcd_update();
344 for (i=0; i<20; i++) {
345 cursor_pos = (rb->rand() % 20);
346 flipit_toggle();
349 cursor_pos = 0;
350 draw_cursor();
351 moves = 0;
352 draw_info_panel();
353 rb->lcd_update();
356 /* the main game loop */
357 static bool flipit_loop(void)
359 int i;
360 int button;
361 int lastbutton = BUTTON_NONE;
363 flipit_init();
364 while(true) {
365 button = rb->button_get(true);
366 switch (button) {
367 #ifdef FLIPIT_RC_QUIT
368 case FLIPIT_RC_QUIT:
369 #endif
370 case FLIPIT_QUIT:
371 /* get out of here */
372 return PLUGIN_OK;
374 case FLIPIT_SHUFFLE:
375 /* mix up the pieces */
376 flipit_init();
377 break;
379 case FLIPIT_SOLVE:
380 #ifdef FLIPIT_SOLVE_PRE
381 if (lastbutton != FLIPIT_SOLVE_PRE)
382 break;
383 #endif
384 /* solve the puzzle */
385 if (!flipit_finished()) {
386 for (i=0; i<20; i++)
387 if (!toggle[i]) {
388 clear_cursor();
389 cursor_pos = i;
390 flipit_toggle();
391 draw_cursor();
392 rb->lcd_update();
393 rb->sleep(HZ*2/3);
396 break;
398 case FLIPIT_STEP_BY_STEP:
399 #ifdef FLIPIT_STEP_PRE
400 if (lastbutton != FLIPIT_STEP_PRE)
401 break;
402 #endif
403 if (!flipit_finished()) {
404 for (i=0; i<20; i++)
405 if (!toggle[i]) {
406 clear_cursor();
407 cursor_pos = i;
408 flipit_toggle();
409 draw_cursor();
410 rb->lcd_update();
411 break;
414 break;
416 case FLIPIT_TOGGLE:
417 #ifdef FLIPIT_TOGGLE_PRE
418 if (lastbutton != FLIPIT_TOGGLE_PRE)
419 break;
420 #endif
421 /* toggle the pieces */
422 if (!flipit_finished()) {
423 flipit_toggle();
424 draw_cursor();
425 rb->lcd_update();
427 break;
429 case BUTTON_LEFT:
430 move_cursor(-1, 0);
431 break;
433 case BUTTON_RIGHT:
434 move_cursor(1, 0);
435 break;
437 case FLIPIT_UP:
438 #ifdef FLIPIT_UP_PRE
439 if (lastbutton != FLIPIT_UP_PRE)
440 break;
441 #endif
442 move_cursor(0, -1);
443 break;
445 case FLIPIT_DOWN:
446 move_cursor(0, 1);
447 break;
449 default:
450 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
451 return PLUGIN_USB_CONNECTED;
452 break;
454 if (button != BUTTON_NONE)
455 lastbutton = button;
459 /* called function from outside */
460 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
462 int i, rc;
464 (void)parameter;
465 rb = api;
467 #ifdef HAVE_LCD_COLOR
468 rb->lcd_set_background(LCD_WHITE);
469 rb->lcd_set_foreground(LCD_BLACK);
470 #endif
472 #if LCD_DEPTH > 1
473 rb->lcd_set_backdrop(NULL);
474 #endif
476 rb->splash(HZ, "FlipIt!");
478 #ifdef HAVE_LCD_BITMAP
479 /* print instructions */
480 rb->lcd_clear_display();
481 rb->lcd_setfont(FONT_SYSFIXED);
482 #if CONFIG_KEYPAD == RECORDER_PAD
483 rb->lcd_putsxy(2, 8, "[OFF] to stop");
484 rb->lcd_putsxy(2, 18, "[PLAY] toggle");
485 rb->lcd_putsxy(2, 28, "[F1] shuffle");
486 rb->lcd_putsxy(2, 38, "[F2] solution");
487 rb->lcd_putsxy(2, 48, "[F3] step by step");
488 #elif CONFIG_KEYPAD == ONDIO_PAD
489 rb->lcd_putsxy(2, 8, "[OFF] to stop");
490 rb->lcd_putsxy(2, 18, "[MODE] toggle");
491 rb->lcd_putsxy(2, 28, "[M-LEFT] shuffle");
492 rb->lcd_putsxy(2, 38, "[M-UP] solution");
493 rb->lcd_putsxy(2, 48, "[M-RIGHT] step by step");
494 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
495 (CONFIG_KEYPAD == IRIVER_H300_PAD)
496 rb->lcd_putsxy(2, 8, "[STOP] to stop");
497 rb->lcd_putsxy(2, 18, "[SELECT] toggle");
498 rb->lcd_putsxy(2, 28, "[MODE] shuffle");
499 rb->lcd_putsxy(2, 38, "[PLAY] solution");
500 rb->lcd_putsxy(2, 48, "[REC] step by step");
501 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
502 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
503 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
504 rb->lcd_putsxy(2, 8, "[S-MENU] to stop");
505 rb->lcd_putsxy(2, 18, "[SELECT] toggle");
506 rb->lcd_putsxy(2, 28, "[S-LEFT] shuffle");
507 rb->lcd_putsxy(2, 38, "[S-PLAY] solution");
508 rb->lcd_putsxy(2, 48, "[S-RIGHT] step by step");
509 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
510 rb->lcd_putsxy(2, 8, "[POWER] to stop");
511 rb->lcd_putsxy(2, 18, "[SELECT] toggle");
512 rb->lcd_putsxy(2, 28, "[REC] shuffle");
513 rb->lcd_putsxy(2, 38, "[PLAY..] solution");
514 rb->lcd_putsxy(2, 48, "[PLAY] step by step");
515 #elif CONFIG_KEYPAD == GIGABEAT_PAD
516 rb->lcd_putsxy(2, 8, "[POWER] to stop");
517 rb->lcd_putsxy(2, 18, "[SELECT] toggle");
518 rb->lcd_putsxy(2, 28, "[MENU] shuffle");
519 rb->lcd_putsxy(2, 38, "[VOL+] solution");
520 rb->lcd_putsxy(2, 48, "[VOL-] step by step");
521 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
522 rb->lcd_putsxy(2, 8, "[POWER] to stop");
523 rb->lcd_putsxy(2, 18, "[REW] toggle");
524 rb->lcd_putsxy(2, 28, "[PL-LEFT] shuffle");
525 rb->lcd_putsxy(2, 38, "[PL-RIGHT] solution");
526 rb->lcd_putsxy(2, 48, "[PL-UP] step by step");
527 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
528 (CONFIG_KEYPAD == SANSA_C200_PAD)
529 rb->lcd_putsxy(2, 8, "[POWER] to stop");
530 rb->lcd_putsxy(2, 18, "[SELECT] toggle");
531 rb->lcd_putsxy(2, 28, "[REC-LEFT] shuffle");
532 rb->lcd_putsxy(2, 38, "[REC-RIGHT] solution");
533 rb->lcd_putsxy(2, 48, "[REC-SEL] step by step");
534 #endif
535 rb->lcd_update();
536 #else /* HAVE_LCD_CHARCELLS */
537 if (!init_gfx())
538 return PLUGIN_ERROR;
539 #endif
540 rb->button_get_w_tmo(HZ*3);
542 rb->lcd_clear_display();
543 draw_info_panel();
544 for (i=0; i<20; i++) {
545 spots[i]=1;
546 draw_spot(i);
548 rb->lcd_update();
549 rb->sleep(HZ*3/2);
550 rb->srand(*rb->current_tick);
552 rc = flipit_loop();
553 #ifdef HAVE_LCD_CHARCELLS
554 release_gfx();
555 #endif
556 return rc;