1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2005 Dave Chapman
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 Sudoku by Dave Chapman
28 Use the arrow keys to move cursor, and press SELECT/ON/F2 to increment
29 the number under the cursor.
31 At any time during the game, press On to bring up the game menu with
39 Sudoku is implemented as a "viewer" for a ".ss" file, as generated by
40 Simple Sudoku and other applications - http://angusj.com/sudoku/
42 In-progress game positions are saved in the original .ss file, with
43 A-I used to indicate numbers entered by the user.
45 Example ".ss" file, and one with a saved state:
47 ...|...|... ...|...|...
48 2..|8.4|9.1 2.C|8.4|9.1
49 ...|1.6|32. E..|1.6|32.
50 ----------- -----------
51 ...|..5|.4. ...|..5|.4.
52 8..|423|..6 8..|423|..6
53 .3.|9..|... .3D|9..|A..
54 ----------- -----------
55 .63|7.9|... .63|7.9|...
56 4.9|5.2|..8 4.9|5.2|.C8
57 ...|...|... ...|...|...
62 #include "lib/configfile.h"
64 #include <lib/playback_control.h>
66 #include "generator.h"
69 #include "pluginbitmaps/sudoku_normal.h"
70 #include "pluginbitmaps/sudoku_inverse.h"
71 #include "pluginbitmaps/sudoku_start.h"
73 #define BITMAP_HEIGHT (BMPHEIGHT_sudoku_normal/10)
74 #define BITMAP_STRIDE STRIDE(SCREEN_MAIN, BMPWIDTH_sudoku_normal, BMPHEIGHT_sudoku_normal)
77 #define BITMAP_WIDTH (BMPWIDTH_sudoku_normal/2)
79 #define BITMAP_WIDTH BMPWIDTH_sudoku_normal
84 /* Default game - used to initialise sudoku.ss if it doesn't exist. */
85 static const char default_game
[9][9] =
87 { '0','1','0', '3','0','7', '0','0','4' },
88 { '0','0','0', '0','6','0', '1','0','2' },
89 { '0','0','0', '0','8','0', '5','6','0' },
91 { '0','6','0', '0','0','0', '0','2','9' },
92 { '0','0','0', '5','0','3', '0','0','0' },
93 { '7','9','0', '0','0','0', '0','3','0' },
95 { '0','8','5', '0','3','0', '0','0','0' },
96 { '1','0','2', '0','7','0', '0','0','0' },
97 { '0','0','0', '4','0','8', '0','5','0' },
100 #if LCD_HEIGHT <= LCD_WIDTH /* Horizontal layout, scratchpad at the left */
102 #if (LCD_HEIGHT==64) && (LCD_WIDTH==112 || LCD_WIDTH==128)
103 /* Archos Recorders and Ondios - 112x64, 9 cells @ 8x6 with 10 border lines */
105 #define MARK_OFFS 1 /* Pixels between border and mark */
106 #define MARK_SPACE 1 /* Pixels between two marks */
107 #define MARK_SIZE 1 /* Mark width and height */
109 #elif ((LCD_HEIGHT==80) && (LCD_WIDTH==132))
110 /* C200, 9 cells @ 8x8 with 8 border lines */
112 #define MARK_OFFS 1 /* Pixels between border and mark */
113 #define MARK_SPACE 1 /* Pixels between two marks */
114 #define MARK_SIZE 1 /* Mark width and height */
116 #elif ((LCD_HEIGHT==96) && (LCD_WIDTH==128))
117 /* iAudio M3, 9 cells @ 9x9 with 14 border lines */
118 #define MARK_OFFS 1 /* Pixels between border and mark */
119 #define MARK_SPACE 2 /* Pixels between two marks */
120 #define MARK_SIZE 1 /* Mark width and height */
122 #elif (LCD_HEIGHT==110) && (LCD_WIDTH==138) \
123 || (LCD_HEIGHT==128) && (LCD_WIDTH==128)
124 /* iPod Mini - 138x110, 9 cells @ 10x10 with 14 border lines */
125 /* iriver H10 5-6GB - 128x128, 9 cells @ 10x10 with 14 border lines */
126 #define MARK_OFFS 1 /* Pixels between border and mark */
127 #define MARK_SPACE 1 /* Pixels between two marks */
128 #define MARK_SIZE 2 /* Mark width and height */
130 #elif ((LCD_HEIGHT==128) && (LCD_WIDTH==160)) \
131 || ((LCD_HEIGHT==132) && (LCD_WIDTH==176))
132 /* iAudio X5, Iriver H1x0, iPod G3, G4 - 160x128; */
133 /* iPod Nano - 176x132, 9 cells @ 12x12 with 14 border lines */
134 #define MARK_OFFS 1 /* Pixels between border and mark */
135 #define MARK_SPACE 2 /* Pixels between two marks */
136 #define MARK_SIZE 2 /* Mark width and height */
138 #elif ((LCD_HEIGHT==176) && (LCD_WIDTH==220))
139 /* Iriver h300, iPod Color/Photo - 220x176, 9 cells @ 16x16 with 14 border lines */
140 #define MARK_OFFS 1 /* Pixels between border and mark */
141 #define MARK_SPACE 1 /* Pixels between two marks */
142 #define MARK_SIZE 4 /* Mark width and height */
144 #elif (LCD_HEIGHT==240) && (LCD_WIDTH==320)
145 /* iPod Video - 320x240, 9 cells @ 24x24 with 14 border lines */
146 #define MARK_OFFS 1 /* Pixels between border and mark */
147 #define MARK_SPACE 2 /* Pixels between two marks */
148 #define MARK_SIZE 6 /* Mark width and height */
150 #elif (LCD_HEIGHT==480) && (LCD_WIDTH==640)
151 /* M:Robe 500 - 640x480, 9 cells @ 48x48 with 14 border lines */
152 #define MARK_OFFS 1 /* Pixels between border and mark */
153 #define MARK_SPACE 2 /* Pixels between two marks */
154 #define MARK_SIZE 6 /* Mark width and height */
157 #error SUDOKU: Unsupported LCD size
160 #else /* Vertical layout, scratchpad at the bottom */
161 #define VERTICAL_LAYOUT
163 #if ((LCD_HEIGHT==220) && (LCD_WIDTH==176))
164 /* e200, 9 cells @ 16x16 with 14 border lines */
165 #define MARK_OFFS 1 /* Pixels between border and mark */
166 #define MARK_SPACE 1 /* Pixels between two marks */
167 #define MARK_SIZE 4 /* Mark width and height */
169 #elif (LCD_HEIGHT>=320) && (LCD_WIDTH>=240)
170 /* Gigabeat - 240x320, 9 cells @ 24x24 with 14 border lines */
171 #define MARK_OFFS 1 /* Pixels between border and mark */
172 #define MARK_SPACE 2 /* Pixels between two marks */
173 #define MARK_SIZE 6 /* Mark width and height */
175 #elif ((LCD_HEIGHT==160) && (LCD_WIDTH==128))
176 /* Philips GoGear SA9200 - 128x160, 9 cells @ 10x10 with 14 border tiles */
177 #define MARK_OFFS 1 /* Pixels between border and mark */
178 #define MARK_SPACE 1 /* Pixels between two marks */
179 #define MARK_SIZE 2 /* Mark width and height */
182 #error SUDOKU: Unsupported LCD size
187 #define CELL_WIDTH BITMAP_WIDTH
188 #define CELL_HEIGHT BITMAP_HEIGHT
190 #ifdef SUDOKU_BUTTON_CHANGEDIR
196 #define CFGFILE_VERSION 0 /* Current config file version */
197 #define CFGFILE_MINVERSION 0 /* Minimum config file version to accept */
199 #if defined(HAVE_LCD_COLOR) || defined(SUDOKU_BUTTON_POSSIBLE)
201 struct sudoku_config
{
202 #ifdef HAVE_LCD_COLOR
205 #ifdef SUDOKU_BUTTON_POSSIBLE
210 struct sudoku_config sudcfg_disk
= {
211 #ifdef HAVE_LCD_COLOR
214 #ifdef SUDOKU_BUTTON_POSSIBLE
218 struct sudoku_config sudcfg
;
220 static const char cfg_filename
[] = "sudoku.cfg";
221 #ifdef HAVE_LCD_COLOR
222 static char *number_str
[2] = { "black", "coloured" };
224 #ifdef SUDOKU_BUTTON_POSSIBLE
225 static char *mark_str
[2] = { "hide", "show" };
228 struct configdata disk_config
[] = {
229 #ifdef HAVE_LCD_COLOR
230 { TYPE_ENUM
, 0, 2, { .int_p
= &sudcfg_disk
.number_display
}, "numbers",
233 #ifdef SUDOKU_BUTTON_POSSIBLE
234 { TYPE_ENUM
, 0, 2, { .int_p
= &sudcfg_disk
.show_markings
}, "markings",
239 #ifdef HAVE_LCD_COLOR
240 #define NUMBER_TYPE (sudcfg.number_display*CELL_WIDTH)
242 #define NUMBER_TYPE 0
245 /* Size dependent build-time calculations */
247 #define BOARD_WIDTH (CELL_WIDTH*9+10)
248 #define BOARD_HEIGHT (CELL_HEIGHT*9+10)
249 static unsigned int cellxpos
[9]={
250 1, (CELL_WIDTH
+2), (2*CELL_WIDTH
+3),
251 (3*CELL_WIDTH
+4), (4*CELL_WIDTH
+5), (5*CELL_WIDTH
+6),
252 (6*CELL_WIDTH
+7), (7*CELL_WIDTH
+8), (8*CELL_WIDTH
+9)
254 static unsigned int cellypos
[9]={
255 1, (CELL_HEIGHT
+2), (2*CELL_HEIGHT
+3),
256 (3*CELL_HEIGHT
+4), (4*CELL_HEIGHT
+5), (5*CELL_HEIGHT
+6),
257 (6*CELL_HEIGHT
+7), (7*CELL_HEIGHT
+8), (8*CELL_HEIGHT
+9)
259 #else /* !SMALL_BOARD */
260 #define BOARD_WIDTH (CELL_WIDTH*9+10+4)
261 #define BOARD_HEIGHT (CELL_HEIGHT*9+10+4)
262 static unsigned int cellxpos
[9]={
263 2, (CELL_WIDTH
+3), (2*CELL_WIDTH
+4),
264 (3*CELL_WIDTH
+6), (4*CELL_WIDTH
+7), (5*CELL_WIDTH
+8),
265 (6*CELL_WIDTH
+10), (7*CELL_WIDTH
+11), (8*CELL_WIDTH
+12)
267 static unsigned int cellypos
[9]={
268 2, (CELL_HEIGHT
+3), (2*CELL_HEIGHT
+4),
269 (3*CELL_HEIGHT
+6), (4*CELL_HEIGHT
+7), (5*CELL_HEIGHT
+8),
270 (6*CELL_HEIGHT
+10), (7*CELL_HEIGHT
+11), (8*CELL_HEIGHT
+12)
274 #ifdef VERTICAL_LAYOUT
275 #define XOFS ((LCD_WIDTH-BOARD_WIDTH)/2)
276 #define YOFS ((LCD_HEIGHT-(BOARD_HEIGHT+CELL_HEIGHT*2+2))/2)
277 #define YOFSSCRATCHPAD (YOFS+BOARD_HEIGHT+CELL_WIDTH)
279 #define XOFSSCRATCHPAD ((LCD_WIDTH-(BOARD_WIDTH+CELL_WIDTH*2+2))/2)
280 #define XOFS (XOFSSCRATCHPAD+CELL_WIDTH*2+2)
281 #define YOFS ((LCD_HEIGHT-BOARD_HEIGHT)/2)
285 #define SIZE (BLOCK*BLOCK)
287 void sudoku_solve(struct sudoku_state_t
* state
)
289 bool ret
= sudoku_solve_board(state
);
292 rb
->splash(HZ
*2, "Solve failed");
298 /* Copies the current to the saved board */
299 static void save_state(struct sudoku_state_t
*state
)
301 rb
->memcpy(state
->savedboard
, state
->currentboard
,
302 sizeof(state
->savedboard
));
303 #ifdef SUDOKU_BUTTON_POSSIBLE
304 rb
->memcpy(state
->savedpossible
, state
->possiblevals
,
305 sizeof(state
->savedpossible
));
309 /* Copies the saved to the current board */
310 static void restore_state(struct sudoku_state_t
*state
)
312 rb
->memcpy(state
->currentboard
, state
->savedboard
,
313 sizeof(state
->savedboard
));
314 #ifdef SUDOKU_BUTTON_POSSIBLE
315 rb
->memcpy(state
->possiblevals
, state
->savedpossible
,
316 sizeof(state
->possiblevals
));
320 void default_state(struct sudoku_state_t
* state
)
324 rb
->strlcpy(state
->filename
,GAME_FILE
,MAX_PATH
);
327 state
->startboard
[r
][c
]=default_game
[r
][c
];
328 state
->currentboard
[r
][c
]=default_game
[r
][c
];
329 #ifdef SUDOKU_BUTTON_POSSIBLE
330 state
->possiblevals
[r
][c
]=0;
335 /* initialize the saved board so reload function works */
343 void clear_state(struct sudoku_state_t
* state
)
347 rb
->strlcpy(state
->filename
,GAME_FILE
,MAX_PATH
);
350 state
->startboard
[r
][c
]='0';
351 state
->currentboard
[r
][c
]='0';
352 #ifdef SUDOKU_BUTTON_POSSIBLE
353 state
->possiblevals
[r
][c
]=0;
363 /* Check the status of the board, assuming a change at the cursor location */
364 bool check_status(struct sudoku_state_t
* state
)
371 /* First, check the column */
372 for (cell
=0;cell
<9;cell
++) {
376 cell
=state
->currentboard
[r
][state
->x
];
378 if (check
[cell
-'1']==1) {
385 /* Second, check the row */
386 for (cell
=0;cell
<9;cell
++) {
390 cell
=state
->currentboard
[state
->y
][c
];
392 if (check
[cell
-'1']==1) {
399 /* Finally, check the 3x3 sub-grid */
400 for (cell
=0;cell
<9;cell
++) {
405 for (r
=r1
;r
<r1
+3;r
++) {
406 for (c
=c1
;c
<c1
+3;c
++) {
407 cell
=state
->currentboard
[r
][c
];
409 if (check
[cell
-'1']==1) {
417 /* We passed all the checks :) */
422 /* Load game - only ".ss" is officially supported, but any sensible
423 text representation (one line per row) may load.
425 bool load_sudoku(struct sudoku_state_t
* state
, char* filename
)
429 int r
= 0, c
= 0, d
= 0;
432 char buf
[500]; /* A buffer to read a sudoku board from */
434 fd
=rb
->open(filename
, O_RDONLY
);
436 LOGF("Invalid sudoku file: %s\n",filename
);
440 rb
->strlcpy(state
->filename
,filename
,MAX_PATH
);
441 n
=rb
->read(fd
,buf
,500);
450 while ((i
< n
) && (r
< 9)) {
471 if (c
>= SIZE
|| r
>= SIZE
){
472 LOGF("ERROR: sudoku problem is the wrong size (%d,%d)\n",
479 if (((buf
[i
]>='A') && (buf
[i
]<='I')) ||
480 ((buf
[i
]>='0') && (buf
[i
]<='9'))) {
482 if (r
>= SIZE
|| c
>= SIZE
){
483 LOGF("ERROR: sudoku problem is the wrong size "
487 if ((buf
[i
]>='0') && (buf
[i
]<='9')) {
488 state
->startboard
[r
][c
]=buf
[i
];
489 state
->currentboard
[r
][c
]=buf
[i
];
491 state
->currentboard
[r
][c
]='1'+(buf
[i
]-'A');
495 if((buf
[i
]>='a' && buf
[i
] <= 'z') && i
< (n
-1)
496 && (buf
[i
+1] >= 'a' && buf
[i
+1] <= 'z')) {
497 state
->possiblevals
[r
][d
]
498 = (((buf
[i
]-'a') * 26 + buf
[i
+1]-'a')<<1);
502 /* Ignore any other characters */
508 /* Check that the board is valid - we need to check every row/column
509 and block individually */
510 for (state
->y
= 0; state
->y
< 9; state
->y
++) {
511 state
->x
= (state
->y
%3)*3 + (state
->y
/3);
512 if (check_status(state
)) return false;
517 /* Save a copy of the saved state - so we can reload without using the
523 bool save_sudoku(struct sudoku_state_t
* state
)
528 #ifdef SUDOKU_BUTTON_POSSIBLE
530 char line
[41]="...|...|... ; \r\n";
532 char line
[13]="...|...|...\r\n";
534 char sep
[13]="-----------\r\n";
536 rb
->splash(0, "Saving...");
538 if (state
->filename
[0]==0) {
542 fd
=rb
->open(state
->filename
, O_WRONLY
|O_CREAT
, 0666);
547 if (state
->startboard
[r
][c
]!='0') {
548 line
[i
]=state
->startboard
[r
][c
];
549 } else if (state
->currentboard
[r
][c
]!='0') {
550 line
[i
]='A'+(state
->currentboard
[r
][c
]-'1');
555 if ((c
==2) || (c
==5)) {
559 #ifdef SUDOKU_BUTTON_POSSIBLE
562 x
= ((state
->possiblevals
[r
][c
]>>1)/26);
564 x
= ((state
->possiblevals
[r
][c
]>>1)%26);
568 rb
->write(fd
,line
,sizeof(line
));
569 if ((r
==2) || (r
==5)) {
570 rb
->write(fd
,sep
,sizeof(sep
));
573 /* Add a blank line at end */
574 rb
->write(fd
,"\r\n",2);
576 rb
->reload_directory();
577 /* Save a copy of the saved state - so we can reload without
586 void clear_board(struct sudoku_state_t
* state
)
592 state
->currentboard
[r
][c
]=state
->startboard
[r
][c
];
599 void update_cell(struct sudoku_state_t
* state
, int r
, int c
)
601 /* We have four types of cell:
602 1) User-entered number
607 if ((r
==state
->y
) && (c
==state
->x
)) {
608 rb
->lcd_bitmap_part(sudoku_inverse
,NUMBER_TYPE
,
609 BITMAP_HEIGHT
*(state
->currentboard
[r
][c
]-'0'),
611 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],CELL_WIDTH
,
614 if (state
->startboard
[r
][c
]!='0') {
615 rb
->lcd_bitmap_part(sudoku_start
,NUMBER_TYPE
,
616 BITMAP_HEIGHT
*(state
->startboard
[r
][c
]-'0'),
618 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],
619 CELL_WIDTH
,CELL_HEIGHT
);
621 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,
622 BITMAP_HEIGHT
*(state
->currentboard
[r
][c
]-'0'),
624 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],
625 CELL_WIDTH
,CELL_HEIGHT
);
629 rb
->lcd_update_rect(cellxpos
[c
],cellypos
[r
],CELL_WIDTH
,CELL_HEIGHT
);
633 void display_board(struct sudoku_state_t
* state
)
636 #ifdef SUDOKU_BUTTON_POSSIBLE
640 /* Clear the display buffer */
641 rb
->lcd_clear_display();
643 /* Draw the gridlines - differently for different targets */
646 /* Small targets - draw dotted/single lines */
650 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[r
]-1);
651 rb
->lcd_vline(XOFS
+cellxpos
[r
]-1,YOFS
,YOFS
+BOARD_HEIGHT
-1);
654 for (c
=XOFS
;c
<XOFS
+BOARD_WIDTH
;c
+=2) {
655 rb
->lcd_drawpixel(c
,YOFS
+cellypos
[r
]-1);
657 for (c
=YOFS
;c
<YOFS
+BOARD_HEIGHT
;c
+=2) {
658 rb
->lcd_drawpixel(XOFS
+cellxpos
[r
]-1,c
);
662 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[8]+CELL_HEIGHT
);
663 rb
->lcd_vline(XOFS
+cellxpos
[8]+CELL_WIDTH
,YOFS
,YOFS
+BOARD_HEIGHT
-1);
665 /* Large targets - draw single/double lines */
667 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[r
]-1);
668 rb
->lcd_vline(XOFS
+cellxpos
[r
]-1,YOFS
,YOFS
+BOARD_HEIGHT
-1);
670 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[r
]-2);
671 rb
->lcd_vline(XOFS
+cellxpos
[r
]-2,YOFS
,YOFS
+BOARD_HEIGHT
-1);
674 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[8]+CELL_HEIGHT
);
675 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[8]+CELL_HEIGHT
+1);
676 rb
->lcd_vline(XOFS
+cellxpos
[8]+CELL_WIDTH
,YOFS
,YOFS
+BOARD_HEIGHT
-1);
677 rb
->lcd_vline(XOFS
+cellxpos
[8]+CELL_WIDTH
+1,YOFS
,YOFS
+BOARD_HEIGHT
-1);
680 #ifdef SUDOKU_BUTTON_POSSIBLE
681 #ifdef VERTICAL_LAYOUT
682 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFSSCRATCHPAD
);
683 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
686 /* Small targets - draw dotted/single lines */
689 rb
->lcd_vline(XOFS
+cellxpos
[r
]-1,YOFSSCRATCHPAD
,
690 YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
693 for (c
=YOFSSCRATCHPAD
;c
<YOFSSCRATCHPAD
+CELL_HEIGHT
+1;c
+=2) {
694 rb
->lcd_drawpixel(XOFS
+cellxpos
[r
]-1,c
);
698 /* Large targets - draw single/double lines */
699 rb
->lcd_vline(XOFS
+cellxpos
[r
]-1,YOFSSCRATCHPAD
,
700 YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
702 rb
->lcd_vline(XOFS
+cellxpos
[r
]-2,YOFSSCRATCHPAD
,
703 YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
705 if ((r
>0) && state
->possiblevals
[state
->y
][state
->x
]&BIT_N(r
))
706 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,BITMAP_HEIGHT
*r
,
707 BITMAP_STRIDE
,XOFS
+cellxpos
[r
-1],
708 YOFSSCRATCHPAD
+1,CELL_WIDTH
,CELL_HEIGHT
);
710 rb
->lcd_vline(XOFS
+cellxpos
[8]+CELL_WIDTH
,YOFSSCRATCHPAD
,
711 YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
713 rb
->lcd_vline(XOFS
+cellxpos
[8]+CELL_WIDTH
+1,YOFSSCRATCHPAD
,
714 YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
716 if (state
->possiblevals
[state
->y
][state
->x
]&BIT_N(r
))
717 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,BITMAP_HEIGHT
*r
,
718 BITMAP_STRIDE
,XOFS
+cellxpos
[8],YOFSSCRATCHPAD
+1,
719 CELL_WIDTH
,CELL_HEIGHT
);
720 #else /* Horizontal layout */
721 rb
->lcd_vline(XOFSSCRATCHPAD
,YOFS
,YOFS
+BOARD_HEIGHT
-1);
722 rb
->lcd_vline(XOFSSCRATCHPAD
+CELL_WIDTH
+1,YOFS
,YOFS
+BOARD_HEIGHT
-1);
725 /* Small targets - draw dotted/single lines */
728 rb
->lcd_hline(XOFSSCRATCHPAD
,XOFSSCRATCHPAD
+CELL_WIDTH
+1,
732 for (c
=XOFSSCRATCHPAD
;c
<XOFSSCRATCHPAD
+CELL_WIDTH
+1;c
+=2) {
733 rb
->lcd_drawpixel(c
,YOFS
+cellypos
[r
]-1);
737 /* Large targets - draw single/double lines */
738 rb
->lcd_hline(XOFSSCRATCHPAD
,XOFSSCRATCHPAD
+CELL_WIDTH
+1,
741 rb
->lcd_hline(XOFSSCRATCHPAD
,XOFSSCRATCHPAD
+CELL_WIDTH
+1,
744 if ((r
>0) && state
->possiblevals
[state
->y
][state
->x
]&BIT_N(r
))
745 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,BITMAP_HEIGHT
*r
,
746 BITMAP_STRIDE
,XOFSSCRATCHPAD
+1,
747 YOFS
+cellypos
[r
-1],CELL_WIDTH
,CELL_HEIGHT
);
749 rb
->lcd_hline(XOFSSCRATCHPAD
,XOFSSCRATCHPAD
+CELL_WIDTH
+1,
750 YOFS
+cellypos
[8]+CELL_HEIGHT
);
752 rb
->lcd_hline(XOFSSCRATCHPAD
,XOFSSCRATCHPAD
+CELL_WIDTH
+1,
753 YOFS
+cellypos
[8]+CELL_HEIGHT
+1);
755 if (state
->possiblevals
[state
->y
][state
->x
]&BIT_N(r
))
756 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,BITMAP_HEIGHT
*r
,
757 BITMAP_STRIDE
,XOFSSCRATCHPAD
+1,YOFS
+cellypos
[8],
758 CELL_WIDTH
,CELL_HEIGHT
);
760 #endif /* SUDOKU_BUTTON_POSSIBLE */
762 /* Draw the numbers */
765 /* We have four types of cell:
766 1) User-entered number
771 if ((r
==state
->y
) && (c
==state
->x
)) {
772 rb
->lcd_bitmap_part(sudoku_inverse
,NUMBER_TYPE
,
773 BITMAP_HEIGHT
*(state
->currentboard
[r
][c
]-
776 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],
777 CELL_WIDTH
,CELL_HEIGHT
);
779 if (state
->startboard
[r
][c
]!='0') {
780 rb
->lcd_bitmap_part(sudoku_start
,NUMBER_TYPE
,
781 BITMAP_HEIGHT
*(state
->startboard
[r
][c
]-
784 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],
785 CELL_WIDTH
,CELL_HEIGHT
);
787 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,
789 (state
->currentboard
[r
][c
]-'0'),
791 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],
792 CELL_WIDTH
,CELL_HEIGHT
);
795 #ifdef SUDOKU_BUTTON_POSSIBLE
796 /* Draw the possible number markings on the board */
797 if(sudcfg
.show_markings
&& state
->startboard
[r
][c
]=='0'
798 && state
->currentboard
[r
][c
]=='0') {
800 if(state
->possiblevals
[r
][c
]&(2<<i
)) {
802 /* draw markings in dark grey */
803 rb
->lcd_set_foreground(LCD_DARKGRAY
);
805 rb
->lcd_fillrect(XOFS
+cellxpos
[c
]+MARK_OFFS
806 +(i
%3)*(MARK_SIZE
+MARK_SPACE
),
807 YOFS
+cellypos
[r
]+MARK_OFFS
808 +(i
/3)*(MARK_SIZE
+MARK_SPACE
),
812 rb
->lcd_set_foreground(LCD_BLACK
);
817 #endif /* SUDOKU_BUTTON_POSSIBLE */
822 /* update the screen */
826 bool sudoku_generate(struct sudoku_state_t
* state
)
831 struct sudoku_state_t new_state
;
833 clear_state(&new_state
);
834 display_board(&new_state
);
835 rb
->splash(0, "Generating...");
837 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
841 res
= sudoku_generate_board(&new_state
,&difficulty
);
843 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
844 rb
->cpu_boost(false);
848 rb
->memcpy(state
,&new_state
,sizeof(new_state
));
849 rb
->snprintf(str
,sizeof(str
),"Difficulty: %s",difficulty
);
850 display_board(state
);
851 rb
->splash(HZ
*3, str
);
852 rb
->strlcpy(state
->filename
,GAME_FILE
,MAX_PATH
);
854 display_board(&new_state
);
855 rb
->splash(HZ
*2, "Aborted");
857 /* initialize the saved board so reload function works */
862 #ifdef HAVE_LCD_COLOR
863 static bool numdisplay_setting(void)
865 static const struct opt_items names
[] = {
870 return rb
->set_option("Number Display", &sudcfg
.number_display
, INT
, names
,
871 sizeof(names
) / sizeof(names
[0]), NULL
);
875 #ifdef SUDOKU_BUTTON_POSSIBLE
876 static bool showmarkings_setting(void)
878 static const struct opt_items names
[] = {
883 return rb
->set_option("Show Markings", &sudcfg
.show_markings
, INT
, names
,
884 sizeof(names
) / sizeof(names
[0]), NULL
);
889 SM_AUDIO_PLAYBACK
= 0,
890 #ifdef HAVE_LCD_COLOR
893 #ifdef SUDOKU_BUTTON_POSSIBLE
905 int sudoku_menu(struct sudoku_state_t
* state
)
909 MENUITEM_STRINGLIST(menu
, "Sudoku Menu", NULL
,
911 #ifdef HAVE_LCD_COLOR
914 #ifdef SUDOKU_BUTTON_POSSIBLE
917 "Save", "Reload", "Clear", "Solve",
918 "Generate", "New", "Quit");
920 result
= rb
->do_menu(&menu
, NULL
, NULL
, false);
923 case SM_AUDIO_PLAYBACK
:
924 playback_control(NULL
);
927 #ifdef HAVE_LCD_COLOR
928 case SM_NUMBER_DISPLAY
:
929 numdisplay_setting();
933 #ifdef SUDOKU_BUTTON_POSSIBLE
934 case SM_SHOW_MARKINGS
:
935 showmarkings_setting();
943 restore_state(state
);
955 sudoku_generate(state
);
974 /* Menu used when user is in edit mode - i.e. creating a new game manually */
975 int sudoku_edit_menu(struct sudoku_state_t
* state
)
979 MENUITEM_STRINGLIST(menu
, "Edit Menu", NULL
,
982 result
= rb
->do_menu(&menu
, NULL
, NULL
, false);
985 case 0: /* Save new game */
986 rb
->kbd_input(state
->filename
,MAX_PATH
);
987 if (save_sudoku(state
)) {
990 rb
->splash(HZ
*2, "Save failed");
1004 void move_cursor(struct sudoku_state_t
* state
, int newx
, int newy
)
1008 /* Check that the character at the cursor position is legal */
1009 if (check_status(state
)) {
1010 rb
->splash(HZ
*2, "Illegal move!");
1011 /* Ignore any button presses during the splash */
1012 rb
->button_clear_queue();
1022 /* Redraw current and old cells */
1023 update_cell(state
,oldx
,oldy
);
1024 update_cell(state
,newx
,newy
);
1027 /* plugin entry point */
1028 enum plugin_status
plugin_start(const void* parameter
)
1032 #if defined(SUDOKU_BUTTON_TOGGLE_PRE) || defined(SUDOKU_BUTTON_MENU_PRE)
1033 int lastbutton
= BUTTON_NONE
;
1038 struct sudoku_state_t state
;
1040 #if defined(HAVE_LCD_COLOR) || defined(SUDOKU_BUTTON_POSSIBLE)
1041 configfile_load(cfg_filename
, disk_config
,
1042 sizeof(disk_config
) / sizeof(disk_config
[0]),
1043 CFGFILE_MINVERSION
);
1044 rb
->memcpy(&sudcfg
, &sudcfg_disk
, sizeof(sudcfg
)); /* copy to running config */
1048 rb
->lcd_set_backdrop(NULL
);
1049 rb
->lcd_set_foreground(LCD_BLACK
);
1050 rb
->lcd_set_background(LCD_WHITE
);
1053 clear_state(&state
);
1055 if (parameter
==NULL
) {
1056 /* We have been started as a plugin - try default sudoku.ss */
1057 if (!load_sudoku(&state
,GAME_FILE
)) {
1058 /* No previous game saved, use the default */
1059 default_state(&state
);
1062 if (!load_sudoku(&state
,(char*)parameter
)) {
1063 rb
->splash(HZ
*2, "Load error");
1064 return(PLUGIN_ERROR
);
1069 display_board(&state
);
1071 /* The main game loop */
1075 button
= rb
->button_get(true);
1078 #ifdef SUDOKU_BUTTON_QUIT
1080 case SUDOKU_BUTTON_QUIT
:
1081 if (check_status(&state
)) {
1082 rb
->splash(HZ
*2, "Illegal move!");
1083 /* Ignore any button presses during the splash */
1084 rb
->button_clear_queue();
1086 save_sudoku(&state
);
1092 /* Increment digit */
1093 #ifdef SUDOKU_BUTTON_ALTTOGGLE
1094 case SUDOKU_BUTTON_ALTTOGGLE
| BUTTON_REPEAT
:
1096 case SUDOKU_BUTTON_TOGGLE
| BUTTON_REPEAT
:
1097 /* Slow down the repeat speed to 1/3 second */
1098 if ((*rb
->current_tick
-ticks
) < (HZ
/3)) {
1102 #ifdef SUDOKU_BUTTON_ALTTOGGLE
1103 case SUDOKU_BUTTON_ALTTOGGLE
:
1105 case SUDOKU_BUTTON_TOGGLE
:
1106 #ifdef SUDOKU_BUTTON_TOGGLE_PRE
1107 if ((button
== SUDOKU_BUTTON_TOGGLE
)
1108 && (lastbutton
!= SUDOKU_BUTTON_TOGGLE_PRE
))
1111 /* Increment digit */
1112 ticks
=*rb
->current_tick
;
1113 if (state
.editmode
) {
1114 if (state
.startboard
[state
.y
][state
.x
]=='9') {
1115 state
.startboard
[state
.y
][state
.x
]='0';
1116 state
.currentboard
[state
.y
][state
.x
]='0';
1118 state
.startboard
[state
.y
][state
.x
]++;
1119 state
.currentboard
[state
.y
][state
.x
]++;
1122 if (state
.startboard
[state
.y
][state
.x
]=='0') {
1123 if (state
.currentboard
[state
.y
][state
.x
]=='9') {
1124 state
.currentboard
[state
.y
][state
.x
]='0';
1126 state
.currentboard
[state
.y
][state
.x
]++;
1130 update_cell(&state
,state
.y
,state
.x
);
1133 #ifdef SUDOKU_BUTTON_TOGGLEBACK
1134 case SUDOKU_BUTTON_TOGGLEBACK
| BUTTON_REPEAT
:
1135 /* Slow down the repeat speed to 1/3 second */
1136 if ((*rb
->current_tick
-ticks
) < (HZ
/3)) {
1140 case SUDOKU_BUTTON_TOGGLEBACK
:
1141 /* Decrement digit */
1142 ticks
=*rb
->current_tick
;
1143 if (state
.editmode
) {
1144 if (state
.startboard
[state
.y
][state
.x
]=='0') {
1145 state
.startboard
[state
.y
][state
.x
]='9';
1146 state
.currentboard
[state
.y
][state
.x
]='9';
1148 state
.startboard
[state
.y
][state
.x
]--;
1149 state
.currentboard
[state
.y
][state
.x
]--;
1152 if (state
.startboard
[state
.y
][state
.x
]=='0') {
1153 if (state
.currentboard
[state
.y
][state
.x
]=='0') {
1154 state
.currentboard
[state
.y
][state
.x
]='9';
1156 state
.currentboard
[state
.y
][state
.x
]--;
1160 update_cell(&state
,state
.y
,state
.x
);
1164 /* move cursor left */
1165 case SUDOKU_BUTTON_LEFT
:
1166 case (SUDOKU_BUTTON_LEFT
| BUTTON_REPEAT
):
1167 if ( (state
.x
==0&&invertdir
==0) || (state
.y
==0&&invertdir
==1) ) {
1168 #ifndef SUDOKU_BUTTON_UP
1169 if ( (state
.y
==0&&invertdir
==0) || (state
.x
==0&&invertdir
==1)) {
1170 move_cursor(&state
,8,8);
1173 move_cursor(&state
,8,state
.y
-1);
1175 move_cursor(&state
,state
.x
-1,8);
1179 move_cursor(&state
,8,state
.y
);
1183 move_cursor(&state
,state
.x
-1,state
.y
);
1185 move_cursor(&state
,state
.x
,state
.y
-1);
1190 /* move cursor right */
1191 case SUDOKU_BUTTON_RIGHT
:
1192 case (SUDOKU_BUTTON_RIGHT
| BUTTON_REPEAT
):
1193 if ( (state
.x
==8&&invertdir
==0) || (state
.y
==8&&invertdir
==1) ) {
1194 #ifndef SUDOKU_BUTTON_DOWN
1195 if ( (state
.y
==8&&invertdir
==0) || (state
.x
==8&&invertdir
==1) ) {
1196 move_cursor(&state
,0,0);
1199 move_cursor(&state
,0,state
.y
+1);
1201 move_cursor(&state
,state
.x
+1,0);
1205 move_cursor(&state
,0,state
.y
);
1209 move_cursor(&state
,state
.x
+1,state
.y
);
1211 move_cursor(&state
,state
.x
,state
.y
+1);
1216 #ifdef SUDOKU_BUTTON_UP
1217 /* move cursor up */
1218 case SUDOKU_BUTTON_UP
:
1219 case (SUDOKU_BUTTON_UP
| BUTTON_REPEAT
):
1221 move_cursor(&state
,state
.x
,8);
1223 move_cursor(&state
,state
.x
,state
.y
-1);
1228 #ifdef SUDOKU_BUTTON_DOWN
1229 /* move cursor down */
1230 case SUDOKU_BUTTON_DOWN
:
1231 case (SUDOKU_BUTTON_DOWN
| BUTTON_REPEAT
):
1233 move_cursor(&state
,state
.x
,0);
1235 move_cursor(&state
,state
.x
,state
.y
+1);
1240 case SUDOKU_BUTTON_MENU
:
1241 #ifdef SUDOKU_BUTTON_MENU_PRE
1242 if (lastbutton
!= SUDOKU_BUTTON_MENU_PRE
)
1245 /* Don't let the user leave a game in a bad state */
1246 if (check_status(&state
)) {
1247 rb
->splash(HZ
*2, "Illegal move!");
1248 /* Ignore any button presses during the splash */
1249 rb
->button_clear_queue();
1251 if (state
.editmode
) {
1252 res
= sudoku_edit_menu(&state
);
1253 if (res
== MENU_ATTACHED_USB
) {
1254 rc
= PLUGIN_USB_CONNECTED
;
1256 } else if (res
== 1) { /* Quit */
1260 res
= sudoku_menu(&state
);
1261 if (res
== MENU_ATTACHED_USB
) {
1262 rc
= PLUGIN_USB_CONNECTED
;
1264 } else if (res
== SM_QUIT
) {
1270 #ifdef SUDOKU_BUTTON_POSSIBLE
1271 case SUDOKU_BUTTON_POSSIBLE
:
1272 /* Toggle current number in the possiblevals structure */
1273 if (state
.currentboard
[state
.y
][state
.x
]!='0') {
1274 state
.possiblevals
[state
.y
][state
.x
]^=
1275 BIT_N(state
.currentboard
[state
.y
][state
.x
] - '0');
1280 #ifdef SUDOKU_BUTTON_CHANGEDIR
1281 case SUDOKU_BUTTON_CHANGEDIR
:
1282 /* Change scroll wheel direction */
1283 invertdir
=!invertdir
;
1287 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
) {
1288 /* Quit if USB has been connected */
1289 rc
= PLUGIN_USB_CONNECTED
;
1294 #if defined(SUDOKU_BUTTON_TOGGLE_PRE) || defined(SUDOKU_BUTTON_MENU_PRE)
1295 if (button
!= BUTTON_NONE
)
1296 lastbutton
= button
;
1299 display_board(&state
);
1301 #if defined(HAVE_LCD_COLOR) || defined(SUDOKU_BUTTON_POSSIBLE)
1302 if (rb
->memcmp(&sudcfg
, &sudcfg_disk
, sizeof(sudcfg
))) /* save settings if changed */
1304 rb
->memcpy(&sudcfg_disk
, &sudcfg
, sizeof(sudcfg
));
1305 configfile_save(cfg_filename
, disk_config
,
1306 sizeof(disk_config
) / sizeof(disk_config
[0]),