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"
63 #include "lib/oldmenuapi.h"
65 #ifdef HAVE_LCD_BITMAP
67 #include <lib/playback_control.h>
69 #include "generator.h"
72 #include "sudoku_normal.h"
73 #include "sudoku_inverse.h"
74 #include "sudoku_start.h"
76 #define BITMAP_HEIGHT (BMPHEIGHT_sudoku_normal/10)
77 #define BITMAP_STRIDE BMPWIDTH_sudoku_normal
81 /* here is a global api struct pointer. while not strictly necessary,
82 it's nice not to have to pass the api pointer in all function calls
85 extern const struct plugin_api
* rb
;
87 /* Default game - used to initialise sudoku.ss if it doesn't exist. */
88 static const char default_game
[9][9] =
90 { '0','1','0', '3','0','7', '0','0','4' },
91 { '0','0','0', '0','6','0', '1','0','2' },
92 { '0','0','0', '0','8','0', '5','6','0' },
94 { '0','6','0', '0','0','0', '0','2','9' },
95 { '0','0','0', '5','0','3', '0','0','0' },
96 { '7','9','0', '0','0','0', '0','3','0' },
98 { '0','8','5', '0','3','0', '0','0','0' },
99 { '1','0','2', '0','7','0', '0','0','0' },
100 { '0','0','0', '4','0','8', '0','5','0' },
103 #if LCD_HEIGHT <= LCD_WIDTH /* Horizontal layout, scratchpad at the left */
105 #if (LCD_HEIGHT==64) && (LCD_WIDTH==112)
106 /* Archos Recorders and Ondios - 112x64, 9 cells @ 8x6 with 10 border lines */
108 /* Internal dimensions of a cell */
110 #define CELL_HEIGHT 6
113 #elif ((LCD_HEIGHT==80) && (LCD_WIDTH==132))
114 /* C200, 9 cells @ 8x8 with 8 border lines */
116 /* Internal dimensions of a cell */
118 #define CELL_HEIGHT 8
121 #elif ((LCD_HEIGHT==96) && (LCD_WIDTH==128))
122 /* iAudio M3, 9 cells @ 9x9 with 14 border lines */
124 /* Internal dimensions of a cell */
126 #define CELL_HEIGHT 9
128 #elif (LCD_HEIGHT==110) && (LCD_WIDTH==138) \
129 || (LCD_HEIGHT==128) && (LCD_WIDTH==128)
130 /* iPod Mini - 138x110, 9 cells @ 10x10 with 14 border lines */
131 /* iriver H10 5-6GB - 128x128, 9 cells @ 10x10 with 14 border lines */
133 /* Internal dimensions of a cell */
134 #define CELL_WIDTH 10
135 #define CELL_HEIGHT 10
137 #elif ((LCD_HEIGHT==128) && (LCD_WIDTH==160)) \
138 || ((LCD_HEIGHT==132) && (LCD_WIDTH==176))
139 /* iAudio X5, Iriver H1x0, iPod G3, G4 - 160x128; */
140 /* iPod Nano - 176x132, 9 cells @ 12x12 with 14 border lines */
142 /* Internal dimensions of a cell */
143 #define CELL_WIDTH 12
144 #define CELL_HEIGHT 12
146 #elif ((LCD_HEIGHT==176) && (LCD_WIDTH==220))
147 /* Iriver h300, iPod Color/Photo - 220x176, 9 cells @ 16x16 with 14 border lines */
149 /* Internal dimensions of a cell */
150 #define CELL_WIDTH 16
151 #define CELL_HEIGHT 16
153 #elif (LCD_HEIGHT>=240) && (LCD_WIDTH>=320)
154 /* iPod Video - 320x240, 9 cells @ 24x24 with 14 border lines */
156 /* Internal dimensions of a cell */
157 #define CELL_WIDTH 24
158 #define CELL_HEIGHT 24
161 #error SUDOKU: Unsupported LCD size
164 #else /* Vertical layout, scratchpad at the bottom */
165 #define VERTICAL_LAYOUT
167 #if ((LCD_HEIGHT==220) && (LCD_WIDTH==176))
168 /* e200, 9 cells @ 16x16 with 14 border lines */
170 /* Internal dimensions of a cell */
171 #define CELL_WIDTH 16
172 #define CELL_HEIGHT 16
174 #elif (LCD_HEIGHT>=320) && (LCD_WIDTH>=240)
175 /* Gigabeat - 240x320, 9 cells @ 24x24 with 14 border lines */
177 /* Internal dimensions of a cell */
178 #define CELL_WIDTH 24
179 #define CELL_HEIGHT 24
182 #error SUDOKU: Unsupported LCD size
187 #define CFGFILE_VERSION 0 /* Current config file version */
188 #define CFGFILE_MINVERSION 0 /* Minimum config file version to accept */
190 #ifdef HAVE_LCD_COLOR
192 struct sudoku_config
{
195 struct sudoku_config sudcfg_disk
= { 0 };
196 struct sudoku_config sudcfg
;
198 static const char cfg_filename
[] = "sudoku.cfg";
199 static char *number_str
[2] = { "black", "coloured" };
201 struct configdata disk_config
[] = {
202 { TYPE_ENUM
, 0, 2, &sudcfg_disk
.number_display
, "numbers", number_str
, NULL
},
204 #define NUMBER_TYPE (sudcfg.number_display*CELL_WIDTH)
206 #define NUMBER_TYPE 0
209 /* Size dependent build-time calculations */
211 #define BOARD_WIDTH (CELL_WIDTH*9+10)
212 #define BOARD_HEIGHT (CELL_HEIGHT*9+10)
213 static unsigned char cellxpos
[9]={
214 1, (CELL_WIDTH
+2), (2*CELL_WIDTH
+3),
215 (3*CELL_WIDTH
+4), (4*CELL_WIDTH
+5), (5*CELL_WIDTH
+6),
216 (6*CELL_WIDTH
+7), (7*CELL_WIDTH
+8), (8*CELL_WIDTH
+9)
218 static unsigned char cellypos
[9]={
219 1, (CELL_HEIGHT
+2), (2*CELL_HEIGHT
+3),
220 (3*CELL_HEIGHT
+4), (4*CELL_HEIGHT
+5), (5*CELL_HEIGHT
+6),
221 (6*CELL_HEIGHT
+7), (7*CELL_HEIGHT
+8), (8*CELL_HEIGHT
+9)
223 #else /* !SMALL_BOARD */
224 #define BOARD_WIDTH (CELL_WIDTH*9+10+4)
225 #define BOARD_HEIGHT (CELL_HEIGHT*9+10+4)
226 static unsigned char cellxpos
[9]={
227 2, (CELL_WIDTH
+3), (2*CELL_WIDTH
+4),
228 (3*CELL_WIDTH
+6), (4*CELL_WIDTH
+7), (5*CELL_WIDTH
+8),
229 (6*CELL_WIDTH
+10), (7*CELL_WIDTH
+11), (8*CELL_WIDTH
+12)
231 static unsigned char cellypos
[9]={
232 2, (CELL_HEIGHT
+3), (2*CELL_HEIGHT
+4),
233 (3*CELL_HEIGHT
+6), (4*CELL_HEIGHT
+7), (5*CELL_HEIGHT
+8),
234 (6*CELL_HEIGHT
+10), (7*CELL_HEIGHT
+11), (8*CELL_HEIGHT
+12)
238 #ifdef VERTICAL_LAYOUT
239 #define XOFS ((LCD_WIDTH-BOARD_WIDTH)/2)
240 #define YOFS ((LCD_HEIGHT-(BOARD_HEIGHT+CELL_HEIGHT*2+2))/2)
241 #define YOFSSCRATCHPAD (YOFS+BOARD_HEIGHT+CELL_WIDTH)
243 #define XOFSSCRATCHPAD ((LCD_WIDTH-(BOARD_WIDTH+CELL_WIDTH*2+2))/2)
244 #define XOFS (XOFSSCRATCHPAD+CELL_WIDTH*2+2)
245 #define YOFS ((LCD_HEIGHT-BOARD_HEIGHT)/2)
248 /****** Solver routine by Tom Shackell <shackell@cs.york.ac.uk>
252 http://www-users.cs.york.ac.uk/~shackell/sudoku/Sudoku.html
258 typedef unsigned int Bitset
;
261 #define SIZE (BLOCK*BLOCK)
266 typedef struct _Sudoku
{
267 Bitset table
[SIZE
][SIZE
];
270 typedef struct _Stats
{
277 typedef struct _Options
{
279 bool uniquenessCheck
;
282 void sudoku_init(Sudoku
* sud
);
283 void sudoku_set(Sudoku
* sud
, int x
, int y
, int num
, bool original
);
284 int sudoku_get(Sudoku
* sud
, int x
, int y
, bool* original
);
286 #define BIT(n) ((Bitset)(1<<(n)))
287 #define BIT_TEST(v,n) ((((Bitset)v) & BIT(n)) != 0)
288 #define BIT_CLEAR(v,n) (v) &= ~BIT(n)
289 #define MARK_BIT BIT(0)
290 #define ORIGINAL_BIT BIT(SIZE+1)
292 #define ALL_BITS (BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9))
294 /* initialize a sudoku problem, should be called before using set or get */
295 void sudoku_init(Sudoku
* sud
)
298 for (y
= 0; y
< SIZE
; y
++){
299 for (x
= 0; x
< SIZE
; x
++){
300 sud
->table
[x
][y
] = ALL_BITS
;
305 /* set the number at a particular x and y column */
306 void sudoku_set(Sudoku
* sud
, int x
, int y
, int num
, bool original
)
312 /* clear the row and columns */
313 for (i
= 0; i
< SIZE
; i
++){
314 BIT_CLEAR(sud
->table
[i
][y
], num
);
315 BIT_CLEAR(sud
->table
[x
][i
], num
);
317 /* clear the block */
318 bx
= x
- (x
% BLOCK
);
319 by
= y
- (y
% BLOCK
);
320 for (i
= 0; i
< BLOCK
; i
++){
321 for (j
= 0; j
< BLOCK
; j
++){
322 BIT_CLEAR(sud
->table
[bx
+j
][by
+i
], num
);
326 orig
= original
? ORIGINAL_BIT
: 0;
327 sud
->table
[x
][y
] = BIT(num
) | MARK_BIT
| orig
;
330 /* get the number at a particular x and y column, if this
331 is not unique return 0 */
332 int sudoku_get(Sudoku
* sud
, int x
, int y
, bool* original
)
334 Bitset val
= sud
->table
[x
][y
];
339 *original
= val
& ORIGINAL_BIT
;
341 for (i
= 1; i
<= SIZE
; i
++){
342 if (BIT_TEST(val
, i
)){
352 /* returns true if this is a valid problem, this is necessary because the input
353 problem might be degenerate which breaks the solver algorithm. */
354 static bool is_valid(const Sudoku
* sud
)
358 for (y
= 0; y
< SIZE
; y
++){
359 for (x
= 0; x
< SIZE
; x
++){
360 if ((sud
->table
[x
][y
] & ALL_BITS
) == 0){
368 /* scan the table for the most constrained item, giving all it's options, sets
369 the best x and y coordinates, the number of options and the options for
370 that coordinate and returns true if the puzzle is finished */
371 static bool scan(const Sudoku
* sud
, int* rX
, int* rY
, int *num
, int* options
)
374 int bestCount
= SIZE
+1;
376 bool allMarked
= true;
378 for (y
= 0; y
< SIZE
; y
++){
379 for (x
= 0; x
< SIZE
; x
++){
380 Bitset val
= sud
->table
[x
][y
];
384 if (val
& MARK_BIT
) {
389 for (i
= 1; i
<= SIZE
; i
++){
390 if (BIT_TEST(val
, i
)){
394 if (count
< bestCount
){
399 /* can't possibly be beaten */
406 /* now copy into options */
408 val
= sud
->table
[*rX
][*rY
];
409 for (i
= 1, j
= 0; i
<= SIZE
; i
++){
410 if (BIT_TEST(val
, i
)){
417 static bool solve(Sudoku
* sud
, Stats
* stats
, const Options
* options
);
419 /* try a particular option and return true if that gives a solution or false
420 if it doesn't, restores board on backtracking */
421 static bool spawn_option(Sudoku
* sud
, Stats
* stats
, const Options
* options
,
422 int x
, int y
, int num
)
426 rb
->memcpy(©
,sud
,sizeof(Sudoku
));
427 sudoku_set(©
, x
, y
, num
, false);
428 stats
->numTries
+= 1;
429 if (solve(©
, stats
, options
)){
430 if (!options
->allSolutions
&& stats
->solutionFound
){
431 rb
->memcpy(sud
,©
,sizeof(Sudoku
));
440 /* solve a sudoku problem, returns true if there is a solution and false
441 otherwise. stats is used to track statisticss */
442 static bool solve(Sudoku
* sud
, Stats
* stats
, const Options
* options
)
450 if (scan(sud
, &x
, &y
, &num
, places
)){
451 /* a solution was found! */
452 if (options
->uniquenessCheck
&& stats
->solutionFound
){
453 /*printf("\n\t... But the solution is not unique!\n"); */
456 stats
->solutionFound
= true;
457 if (options
->allSolutions
|| options
->uniquenessCheck
){
458 /*printf("\n\tSolution after %d iterations\n", stats->numTries); */
459 /*sudoku_print(sud); */
467 /* can't be satisfied */
470 /* try all the places (except the last one) */
471 for (i
= 0; i
< num
-1; i
++){
472 if (spawn_option(sud
, stats
, options
, x
, y
, places
[i
])){
473 /* solution found! */
474 if (!options
->allSolutions
&& stats
->solutionFound
){
479 /* take the last place ourself */
480 stats
->numTries
+= 1;
481 sudoku_set(sud
, x
, y
, places
[num
-1], false);
485 /******** END OF IMPORTED CODE */
488 /* A wrapper function between the Sudoku plugin and the above solver code */
489 void sudoku_solve(struct sudoku_state_t
* state
)
498 /* Initialise the parameters */
500 rb
->memset(&stats
,0,sizeof(stats
));
501 options
.allSolutions
=false;
502 options
.uniquenessCheck
=false;
504 /* Convert Rockbox format into format for solver */
507 if (state
->startboard
[r
][c
]!='0') {
508 sudoku_set(&sud
, c
, r
, state
->startboard
[r
][c
]-'0', true);
513 /* need to check for degenerate input problems ... */
515 ret
= solve(&sud
, &stats
, &options
);
521 /* Populate the board with the solution. */
524 state
->currentboard
[r
][c
]='0'+
525 sudoku_get(&sud
, c
, r
, &original
);
529 rb
->splash(HZ
*2, "Solve failed");
535 void default_state(struct sudoku_state_t
* state
)
539 rb
->strncpy(state
->filename
,GAME_FILE
,MAX_PATH
);
542 state
->startboard
[r
][c
]=default_game
[r
][c
];
543 state
->currentboard
[r
][c
]=default_game
[r
][c
];
544 #ifdef SUDOKU_BUTTON_POSSIBLE
545 state
->possiblevals
[r
][c
]=0;
555 void clear_state(struct sudoku_state_t
* state
)
559 rb
->strncpy(state
->filename
,GAME_FILE
,MAX_PATH
);
562 state
->startboard
[r
][c
]='0';
563 state
->currentboard
[r
][c
]='0';
564 #ifdef SUDOKU_BUTTON_POSSIBLE
565 state
->possiblevals
[r
][c
]=0;
575 /* Check the status of the board, assuming a change at the cursor location */
576 bool check_status(struct sudoku_state_t
* state
)
583 /* First, check the column */
584 for (cell
=0;cell
<9;cell
++) {
588 cell
=state
->currentboard
[r
][state
->x
];
590 if (check
[cell
-'1']==1) {
597 /* Second, check the row */
598 for (cell
=0;cell
<9;cell
++) {
602 cell
=state
->currentboard
[state
->y
][c
];
604 if (check
[cell
-'1']==1) {
611 /* Finally, check the 3x3 sub-grid */
612 for (cell
=0;cell
<9;cell
++) {
617 for (r
=r1
;r
<r1
+3;r
++) {
618 for (c
=c1
;c
<c1
+3;c
++) {
619 cell
=state
->currentboard
[r
][c
];
621 if (check
[cell
-'1']==1) {
629 /* We passed all the checks :) */
634 /* Load game - only ".ss" is officially supported, but any sensible
635 text representation (one line per row) may load.
637 bool load_sudoku(struct sudoku_state_t
* state
, char* filename
)
644 char buf
[300]; /* A buffer to read a sudoku board from */
646 fd
=rb
->open(filename
, O_RDONLY
);
648 LOGF("Invalid sudoku file: %s\n",filename
);
652 rb
->strncpy(state
->filename
,filename
,MAX_PATH
);
653 n
=rb
->read(fd
,buf
,300);
662 while ((i
< n
) && (r
< 9)) {
682 if (c
>= SIZE
|| r
>= SIZE
){
683 LOGF("ERROR: sudoku problem is the wrong size (%d,%d)\n",
690 if (((buf
[i
]>='A') && (buf
[i
]<='I')) ||
691 ((buf
[i
]>='0') && (buf
[i
]<='9'))) {
693 if (r
>= SIZE
|| c
>= SIZE
){
694 LOGF("ERROR: sudoku problem is the wrong size "
698 if ((buf
[i
]>='0') && (buf
[i
]<='9')) {
699 state
->startboard
[r
][c
]=buf
[i
];
700 state
->currentboard
[r
][c
]=buf
[i
];
702 state
->currentboard
[r
][c
]='1'+(buf
[i
]-'A');
706 /* Ignore any other characters */
712 /* Check that the board is valid - we need to check every row/column
713 individually, so we check the diagonal from top-left to bottom-right */
714 for (state
->x
= 0; state
->x
< 9; state
->x
++) {
716 if (check_status(state
)) return false;
721 /* Save a copy of the saved state - so we can reload without using the
723 rb
->memcpy(state
->savedboard
,state
->currentboard
,81);
727 bool save_sudoku(struct sudoku_state_t
* state
)
735 rb
->splash(0, "Saving...");
736 rb
->memcpy(line
,"...|...|...\r\n",13);
737 rb
->memcpy(sep
,"-----------\r\n",13);
739 if (state
->filename
[0]==0) {
743 fd
=rb
->open(state
->filename
, O_WRONLY
|O_CREAT
);
748 if (state
->startboard
[r
][c
]!='0') {
749 line
[i
]=state
->startboard
[r
][c
];
750 } else if (state
->currentboard
[r
][c
]!='0') {
751 line
[i
]='A'+(state
->currentboard
[r
][c
]-'1');
756 if ((c
==2) || (c
==5)) {
760 rb
->write(fd
,line
,sizeof(line
));
761 if ((r
==2) || (r
==5)) {
762 rb
->write(fd
,sep
,sizeof(sep
));
765 /* Add a blank line at end */
766 rb
->write(fd
,"\r\n",2);
768 rb
->reload_directory();
769 /* Save a copy of the saved state - so we can reload without
771 rb
->memcpy(state
->savedboard
,state
->currentboard
,81);
778 void restore_state(struct sudoku_state_t
* state
)
780 rb
->memcpy(state
->currentboard
,state
->savedboard
,81);
783 void clear_board(struct sudoku_state_t
* state
)
789 state
->currentboard
[r
][c
]=state
->startboard
[r
][c
];
796 void update_cell(struct sudoku_state_t
* state
, int r
, int c
)
798 /* We have four types of cell:
799 1) User-entered number
804 if ((r
==state
->y
) && (c
==state
->x
)) {
805 rb
->lcd_bitmap_part(sudoku_inverse
,NUMBER_TYPE
,
806 BITMAP_HEIGHT
*(state
->currentboard
[r
][c
]-'0'),
808 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],CELL_WIDTH
,
811 if (state
->startboard
[r
][c
]!='0') {
812 rb
->lcd_bitmap_part(sudoku_start
,NUMBER_TYPE
,
813 BITMAP_HEIGHT
*(state
->startboard
[r
][c
]-'0'),
815 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],
816 CELL_WIDTH
,CELL_HEIGHT
);
818 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,
819 BITMAP_HEIGHT
*(state
->currentboard
[r
][c
]-'0'),
821 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],
822 CELL_WIDTH
,CELL_HEIGHT
);
826 rb
->lcd_update_rect(cellxpos
[c
],cellypos
[r
],CELL_WIDTH
,CELL_HEIGHT
);
830 void display_board(struct sudoku_state_t
* state
)
834 /* Clear the display buffer */
835 rb
->lcd_clear_display();
837 /* Draw the gridlines - differently for different targets */
840 /* Small targets - draw dotted/single lines */
844 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[r
]-1);
845 rb
->lcd_vline(XOFS
+cellxpos
[r
]-1,YOFS
,YOFS
+BOARD_HEIGHT
-1);
848 for (c
=XOFS
;c
<XOFS
+BOARD_WIDTH
;c
+=2) {
849 rb
->lcd_drawpixel(c
,YOFS
+cellypos
[r
]-1);
851 for (c
=YOFS
;c
<YOFS
+BOARD_HEIGHT
;c
+=2) {
852 rb
->lcd_drawpixel(XOFS
+cellxpos
[r
]-1,c
);
856 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[8]+CELL_HEIGHT
);
857 rb
->lcd_vline(XOFS
+cellxpos
[8]+CELL_WIDTH
,YOFS
,YOFS
+BOARD_HEIGHT
-1);
859 /* Large targets - draw single/double lines */
861 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[r
]-1);
862 rb
->lcd_vline(XOFS
+cellxpos
[r
]-1,YOFS
,YOFS
+BOARD_HEIGHT
-1);
864 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[r
]-2);
865 rb
->lcd_vline(XOFS
+cellxpos
[r
]-2,YOFS
,YOFS
+BOARD_HEIGHT
-1);
868 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[8]+CELL_HEIGHT
);
869 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[8]+CELL_HEIGHT
+1);
870 rb
->lcd_vline(XOFS
+cellxpos
[8]+CELL_WIDTH
,YOFS
,YOFS
+BOARD_HEIGHT
-1);
871 rb
->lcd_vline(XOFS
+cellxpos
[8]+CELL_WIDTH
+1,YOFS
,YOFS
+BOARD_HEIGHT
-1);
874 #ifdef SUDOKU_BUTTON_POSSIBLE
875 #ifdef VERTICAL_LAYOUT
876 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFSSCRATCHPAD
);
877 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
880 /* Small targets - draw dotted/single lines */
883 rb
->lcd_vline(XOFS
+cellxpos
[r
]-1,YOFSSCRATCHPAD
,
884 YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
887 for (c
=YOFSSCRATCHPAD
;c
<YOFSSCRATCHPAD
+CELL_HEIGHT
+1;c
+=2) {
888 rb
->lcd_drawpixel(XOFS
+cellxpos
[r
]-1,c
);
892 /* Large targets - draw single/double lines */
893 rb
->lcd_vline(XOFS
+cellxpos
[r
]-1,YOFSSCRATCHPAD
,
894 YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
896 rb
->lcd_vline(XOFS
+cellxpos
[r
]-2,YOFSSCRATCHPAD
,
897 YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
899 if ((r
>0) && state
->possiblevals
[state
->y
][state
->x
]&(1<<(r
)))
900 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,BITMAP_HEIGHT
*r
,
901 BITMAP_STRIDE
,XOFS
+cellxpos
[r
-1],
902 YOFSSCRATCHPAD
+1,CELL_WIDTH
,CELL_HEIGHT
);
904 rb
->lcd_vline(XOFS
+cellxpos
[8]+CELL_WIDTH
,YOFSSCRATCHPAD
,
905 YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
907 rb
->lcd_vline(XOFS
+cellxpos
[8]+CELL_WIDTH
+1,YOFSSCRATCHPAD
,
908 YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
910 if (state
->possiblevals
[state
->y
][state
->x
]&(1<<(r
)))
911 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,BITMAP_HEIGHT
*r
,
912 BITMAP_STRIDE
,XOFS
+cellxpos
[8],YOFSSCRATCHPAD
+1,
913 CELL_WIDTH
,CELL_HEIGHT
);
914 #else /* Horizontal layout */
915 rb
->lcd_vline(XOFSSCRATCHPAD
,YOFS
,YOFS
+BOARD_HEIGHT
-1);
916 rb
->lcd_vline(XOFSSCRATCHPAD
+CELL_WIDTH
+1,YOFS
,YOFS
+BOARD_HEIGHT
-1);
919 /* Small targets - draw dotted/single lines */
922 rb
->lcd_hline(XOFSSCRATCHPAD
,XOFSSCRATCHPAD
+CELL_WIDTH
+1,
926 for (c
=XOFSSCRATCHPAD
;c
<XOFSSCRATCHPAD
+CELL_WIDTH
+1;c
+=2) {
927 rb
->lcd_drawpixel(c
,YOFS
+cellypos
[r
]-1);
931 /* Large targets - draw single/double lines */
932 rb
->lcd_hline(XOFSSCRATCHPAD
,XOFSSCRATCHPAD
+CELL_WIDTH
+1,
935 rb
->lcd_hline(XOFSSCRATCHPAD
,XOFSSCRATCHPAD
+CELL_WIDTH
+1,
938 if ((r
>0) && state
->possiblevals
[state
->y
][state
->x
]&(1<<(r
)))
939 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,BITMAP_HEIGHT
*r
,
940 BITMAP_STRIDE
,XOFSSCRATCHPAD
+1,
941 YOFS
+cellypos
[r
-1],CELL_WIDTH
,CELL_HEIGHT
);
943 rb
->lcd_hline(XOFSSCRATCHPAD
,XOFSSCRATCHPAD
+CELL_WIDTH
+1,
944 YOFS
+cellypos
[8]+CELL_HEIGHT
);
946 rb
->lcd_hline(XOFSSCRATCHPAD
,XOFSSCRATCHPAD
+CELL_WIDTH
+1,
947 YOFS
+cellypos
[8]+CELL_HEIGHT
+1);
949 if (state
->possiblevals
[state
->y
][state
->x
]&(1<<(r
)))
950 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,BITMAP_HEIGHT
*r
,
951 BITMAP_STRIDE
,XOFSSCRATCHPAD
+1,YOFS
+cellypos
[8],
952 CELL_WIDTH
,CELL_HEIGHT
);
954 #endif /* SUDOKU_BUTTON_POSSIBLE */
956 /* Draw the numbers */
959 /* We have four types of cell:
960 1) User-entered number
965 if ((r
==state
->y
) && (c
==state
->x
)) {
966 rb
->lcd_bitmap_part(sudoku_inverse
,NUMBER_TYPE
,
967 BITMAP_HEIGHT
*(state
->currentboard
[r
][c
]-
970 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],
971 CELL_WIDTH
,CELL_HEIGHT
);
973 if (state
->startboard
[r
][c
]!='0') {
974 rb
->lcd_bitmap_part(sudoku_start
,NUMBER_TYPE
,
975 BITMAP_HEIGHT
*(state
->startboard
[r
][c
]-
978 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],
979 CELL_WIDTH
,CELL_HEIGHT
);
981 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,
983 (state
->currentboard
[r
][c
]-'0'),
985 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],
986 CELL_WIDTH
,CELL_HEIGHT
);
992 /* update the screen */
996 bool sudoku_generate(struct sudoku_state_t
* state
)
1001 struct sudoku_state_t new_state
;
1003 clear_state(&new_state
);
1004 display_board(&new_state
);
1005 rb
->splash(0, "Generating...");
1007 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1008 rb
->cpu_boost(true);
1011 res
= sudoku_generate_board(&new_state
,&difficulty
);
1013 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1014 rb
->cpu_boost(false);
1018 rb
->memcpy(state
,&new_state
,sizeof(new_state
));
1019 rb
->snprintf(str
,sizeof(str
),"Difficulty: %s",difficulty
);
1020 display_board(state
);
1021 rb
->splash(HZ
*3, str
);
1022 rb
->strncpy(state
->filename
,GAME_FILE
,MAX_PATH
);
1024 display_board(&new_state
);
1025 rb
->splash(HZ
*2, "Aborted");
1030 #ifdef HAVE_LCD_COLOR
1031 static bool numdisplay_setting(void)
1033 static const struct opt_items names
[] = {
1038 return rb
->set_option("Number Display", &sudcfg
.number_display
, INT
, names
,
1039 sizeof(names
) / sizeof(names
[0]), NULL
);
1044 SM_AUDIO_PLAYBACK
= 0,
1045 #ifdef HAVE_LCD_COLOR
1057 bool sudoku_menu(struct sudoku_state_t
* state
)
1062 static const struct menu_item items
[] = {
1063 [SM_AUDIO_PLAYBACK
] = { "Audio Playback", NULL
},
1064 #ifdef HAVE_LCD_COLOR
1065 [SM_NUMBER_DISPLAY
] = { "Number Display", NULL
},
1067 [SM_SAVE
] = { "Save", NULL
},
1068 [SM_RELOAD
] = { "Reload", NULL
},
1069 [SM_CLEAR
] = { "Clear", NULL
},
1070 [SM_SOLVE
] = { "Solve", NULL
},
1071 [SM_GENERATE
] = { "Generate", NULL
},
1072 [SM_NEW
] = { "New", NULL
},
1073 [SM_QUIT
] = { "Quit", NULL
},
1076 m
= menu_init(rb
,items
, sizeof(items
) / sizeof(*items
),
1077 NULL
, NULL
, NULL
, NULL
);
1079 result
=menu_show(m
);
1082 case SM_AUDIO_PLAYBACK
:
1083 playback_control(rb
, NULL
);
1086 #ifdef HAVE_LCD_COLOR
1087 case SM_NUMBER_DISPLAY
:
1088 numdisplay_setting();
1096 restore_state(state
);
1104 sudoku_solve(state
);
1108 sudoku_generate(state
);
1128 return (result
==MENU_ATTACHED_USB
);
1131 /* Menu used when user is in edit mode - i.e. creating a new game manually */
1132 int sudoku_edit_menu(struct sudoku_state_t
* state
)
1137 static const struct menu_item items
[] = {
1138 { "Save as", NULL
},
1142 m
= menu_init(rb
,items
, sizeof(items
) / sizeof(*items
),
1143 NULL
, NULL
, NULL
, NULL
);
1145 result
=menu_show(m
);
1148 case 0: /* Save new game */
1149 rb
->kbd_input(state
->filename
,MAX_PATH
);
1150 if (save_sudoku(state
)) {
1153 rb
->splash(HZ
*2, "Save failed");
1169 void move_cursor(struct sudoku_state_t
* state
, int newx
, int newy
)
1173 /* Check that the character at the cursor position is legal */
1174 if (check_status(state
)) {
1175 rb
->splash(HZ
*2, "Illegal move!");
1176 /* Ignore any button presses during the splash */
1177 rb
->button_clear_queue();
1187 /* Redraw current and old cells */
1188 update_cell(state
,oldx
,oldy
);
1189 update_cell(state
,newx
,newy
);
1192 /* plugin entry point */
1193 enum plugin_status
plugin_start(const struct plugin_api
* api
, const void* parameter
)
1197 int lastbutton
= BUTTON_NONE
;
1201 struct sudoku_state_t state
;
1205 /* end of plugin init */
1207 #ifdef HAVE_LCD_COLOR
1208 configfile_init(rb
);
1209 configfile_load(cfg_filename
, disk_config
,
1210 sizeof(disk_config
) / sizeof(disk_config
[0]),
1211 CFGFILE_MINVERSION
);
1212 rb
->memcpy(&sudcfg
, &sudcfg_disk
, sizeof(sudcfg
)); /* copy to running config */
1216 rb
->lcd_set_backdrop(NULL
);
1217 rb
->lcd_set_foreground(LCD_BLACK
);
1218 rb
->lcd_set_background(LCD_WHITE
);
1221 clear_state(&state
);
1223 if (parameter
==NULL
) {
1224 /* We have been started as a plugin - try default sudoku.ss */
1225 if (!load_sudoku(&state
,GAME_FILE
)) {
1226 /* No previous game saved, use the default */
1227 default_state(&state
);
1230 if (!load_sudoku(&state
,(char*)parameter
)) {
1231 rb
->splash(HZ
*2, "Load error");
1232 return(PLUGIN_ERROR
);
1237 display_board(&state
);
1239 /* The main game loop */
1243 button
= rb
->button_get(true);
1246 #ifdef SUDOKU_BUTTON_QUIT
1248 case SUDOKU_BUTTON_QUIT
:
1249 if (check_status(&state
)) {
1250 rb
->splash(HZ
*2, "Illegal move!");
1251 /* Ignore any button presses during the splash */
1252 rb
->button_clear_queue();
1254 save_sudoku(&state
);
1260 /* Increment digit */
1261 #ifdef SUDOKU_BUTTON_ALTTOGGLE
1262 case SUDOKU_BUTTON_ALTTOGGLE
| BUTTON_REPEAT
:
1264 case SUDOKU_BUTTON_TOGGLE
| BUTTON_REPEAT
:
1265 /* Slow down the repeat speed to 1/3 second */
1266 if ((*rb
->current_tick
-ticks
) < (HZ
/3)) {
1270 #ifdef SUDOKU_BUTTON_ALTTOGGLE
1271 case SUDOKU_BUTTON_ALTTOGGLE
:
1273 case SUDOKU_BUTTON_TOGGLE
:
1274 #ifdef SUDOKU_BUTTON_TOGGLE_PRE
1275 if ((button
== SUDOKU_BUTTON_TOGGLE
)
1276 && (lastbutton
!= SUDOKU_BUTTON_TOGGLE_PRE
))
1279 /* Increment digit */
1280 ticks
=*rb
->current_tick
;
1281 if (state
.editmode
) {
1282 if (state
.startboard
[state
.y
][state
.x
]=='9') {
1283 state
.startboard
[state
.y
][state
.x
]='0';
1284 state
.currentboard
[state
.y
][state
.x
]='0';
1286 state
.startboard
[state
.y
][state
.x
]++;
1287 state
.currentboard
[state
.y
][state
.x
]++;
1290 if (state
.startboard
[state
.y
][state
.x
]=='0') {
1291 if (state
.currentboard
[state
.y
][state
.x
]=='9') {
1292 state
.currentboard
[state
.y
][state
.x
]='0';
1294 state
.currentboard
[state
.y
][state
.x
]++;
1298 update_cell(&state
,state
.y
,state
.x
);
1301 #ifdef SUDOKU_BUTTON_TOGGLEBACK
1302 case SUDOKU_BUTTON_TOGGLEBACK
| BUTTON_REPEAT
:
1303 /* Slow down the repeat speed to 1/3 second */
1304 if ((*rb
->current_tick
-ticks
) < (HZ
/3)) {
1308 case SUDOKU_BUTTON_TOGGLEBACK
:
1309 /* Decrement digit */
1310 ticks
=*rb
->current_tick
;
1311 if (state
.editmode
) {
1312 if (state
.startboard
[state
.y
][state
.x
]=='0') {
1313 state
.startboard
[state
.y
][state
.x
]='9';
1314 state
.currentboard
[state
.y
][state
.x
]='9';
1316 state
.startboard
[state
.y
][state
.x
]--;
1317 state
.currentboard
[state
.y
][state
.x
]--;
1320 if (state
.startboard
[state
.y
][state
.x
]=='0') {
1321 if (state
.currentboard
[state
.y
][state
.x
]=='0') {
1322 state
.currentboard
[state
.y
][state
.x
]='9';
1324 state
.currentboard
[state
.y
][state
.x
]--;
1328 update_cell(&state
,state
.y
,state
.x
);
1332 /* move cursor left */
1333 case SUDOKU_BUTTON_LEFT
:
1334 case (SUDOKU_BUTTON_LEFT
| BUTTON_REPEAT
):
1336 #ifndef SUDOKU_BUTTON_UP
1338 move_cursor(&state
,8,8);
1340 move_cursor(&state
,8,state
.y
-1);
1343 move_cursor(&state
,8,state
.y
);
1346 move_cursor(&state
,state
.x
-1,state
.y
);
1350 /* move cursor right */
1351 case SUDOKU_BUTTON_RIGHT
:
1352 case (SUDOKU_BUTTON_RIGHT
| BUTTON_REPEAT
):
1354 #ifndef SUDOKU_BUTTON_DOWN
1356 move_cursor(&state
,0,0);
1358 move_cursor(&state
,0,state
.y
+1);
1361 move_cursor(&state
,0,state
.y
);
1364 move_cursor(&state
,state
.x
+1,state
.y
);
1368 #ifdef SUDOKU_BUTTON_UP
1369 /* move cursor up */
1370 case SUDOKU_BUTTON_UP
:
1371 case (SUDOKU_BUTTON_UP
| BUTTON_REPEAT
):
1373 move_cursor(&state
,state
.x
,8);
1375 move_cursor(&state
,state
.x
,state
.y
-1);
1380 #ifdef SUDOKU_BUTTON_DOWN
1381 /* move cursor down */
1382 case SUDOKU_BUTTON_DOWN
:
1383 case (SUDOKU_BUTTON_DOWN
| BUTTON_REPEAT
):
1385 move_cursor(&state
,state
.x
,0);
1387 move_cursor(&state
,state
.x
,state
.y
+1);
1392 case SUDOKU_BUTTON_MENU
:
1393 #ifdef SUDOKU_BUTTON_MENU_PRE
1394 if (lastbutton
!= SUDOKU_BUTTON_MENU_PRE
)
1397 /* Don't let the user leave a game in a bad state */
1398 if (check_status(&state
)) {
1399 rb
->splash(HZ
*2, "Illegal move!");
1400 /* Ignore any button presses during the splash */
1401 rb
->button_clear_queue();
1403 if (state
.editmode
) {
1404 res
= sudoku_edit_menu(&state
);
1405 if (res
== MENU_ATTACHED_USB
) {
1406 rc
= PLUGIN_USB_CONNECTED
;
1408 } else if (res
== 1) { /* Quit */
1412 if (sudoku_menu(&state
)) {
1413 rc
= PLUGIN_USB_CONNECTED
;
1419 #ifdef SUDOKU_BUTTON_POSSIBLE
1420 case SUDOKU_BUTTON_POSSIBLE
:
1421 /* Toggle current number in the possiblevals structure */
1422 if (state
.currentboard
[state
.y
][state
.x
]!='0') {
1423 state
.possiblevals
[state
.y
][state
.x
]^=
1424 (1 << (state
.currentboard
[state
.y
][state
.x
] - '0'));
1429 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
) {
1430 /* Quit if USB has been connected */
1431 rc
= PLUGIN_USB_CONNECTED
;
1436 if (button
!= BUTTON_NONE
)
1437 lastbutton
= button
;
1439 display_board(&state
);
1441 #ifdef HAVE_LCD_COLOR
1442 if (rb
->memcmp(&sudcfg
, &sudcfg_disk
, sizeof(sudcfg
))) /* save settings if changed */
1444 rb
->memcpy(&sudcfg_disk
, &sudcfg
, sizeof(sudcfg
));
1445 configfile_save(cfg_filename
, disk_config
,
1446 sizeof(disk_config
) / sizeof(disk_config
[0]),