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 #ifdef HAVE_LCD_BITMAP
66 #include <lib/playback_control.h>
68 #include "generator.h"
71 #include "pluginbitmaps/sudoku_normal.h"
72 #include "pluginbitmaps/sudoku_inverse.h"
73 #include "pluginbitmaps/sudoku_start.h"
75 #define BITMAP_HEIGHT (BMPHEIGHT_sudoku_normal/10)
76 #define BITMAP_STRIDE BMPWIDTH_sudoku_normal
80 /* Default game - used to initialise sudoku.ss if it doesn't exist. */
81 static const char default_game
[9][9] =
83 { '0','1','0', '3','0','7', '0','0','4' },
84 { '0','0','0', '0','6','0', '1','0','2' },
85 { '0','0','0', '0','8','0', '5','6','0' },
87 { '0','6','0', '0','0','0', '0','2','9' },
88 { '0','0','0', '5','0','3', '0','0','0' },
89 { '7','9','0', '0','0','0', '0','3','0' },
91 { '0','8','5', '0','3','0', '0','0','0' },
92 { '1','0','2', '0','7','0', '0','0','0' },
93 { '0','0','0', '4','0','8', '0','5','0' },
96 #if LCD_HEIGHT <= LCD_WIDTH /* Horizontal layout, scratchpad at the left */
98 #if (LCD_HEIGHT==64) && (LCD_WIDTH==112 || LCD_WIDTH==128)
99 /* Archos Recorders and Ondios - 112x64, 9 cells @ 8x6 with 10 border lines */
101 /* Internal dimensions of a cell */
103 #define CELL_HEIGHT 6
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 /* Internal dimensions of a cell */
114 #define CELL_HEIGHT 8
116 #define MARK_OFFS 1 /* Pixels between border and mark */
117 #define MARK_SPACE 1 /* Pixels between two marks */
118 #define MARK_SIZE 1 /* Mark width and height */
120 #elif ((LCD_HEIGHT==96) && (LCD_WIDTH==128))
121 /* iAudio M3, 9 cells @ 9x9 with 14 border lines */
123 /* Internal dimensions of a cell */
125 #define CELL_HEIGHT 9
126 #define MARK_OFFS 1 /* Pixels between border and mark */
127 #define MARK_SPACE 2 /* Pixels between two marks */
128 #define MARK_SIZE 1 /* Mark width and height */
130 #elif (LCD_HEIGHT==110) && (LCD_WIDTH==138) \
131 || (LCD_HEIGHT==128) && (LCD_WIDTH==128)
132 /* iPod Mini - 138x110, 9 cells @ 10x10 with 14 border lines */
133 /* iriver H10 5-6GB - 128x128, 9 cells @ 10x10 with 14 border lines */
135 /* Internal dimensions of a cell */
136 #define CELL_WIDTH 10
137 #define CELL_HEIGHT 10
138 #define MARK_OFFS 1 /* Pixels between border and mark */
139 #define MARK_SPACE 1 /* Pixels between two marks */
140 #define MARK_SIZE 2 /* Mark width and height */
142 #elif ((LCD_HEIGHT==128) && (LCD_WIDTH==160)) \
143 || ((LCD_HEIGHT==132) && (LCD_WIDTH==176))
144 /* iAudio X5, Iriver H1x0, iPod G3, G4 - 160x128; */
145 /* iPod Nano - 176x132, 9 cells @ 12x12 with 14 border lines */
147 /* Internal dimensions of a cell */
148 #define CELL_WIDTH 12
149 #define CELL_HEIGHT 12
150 #define MARK_OFFS 1 /* Pixels between border and mark */
151 #define MARK_SPACE 2 /* Pixels between two marks */
152 #define MARK_SIZE 2 /* Mark width and height */
154 #elif ((LCD_HEIGHT==176) && (LCD_WIDTH==220))
155 /* Iriver h300, iPod Color/Photo - 220x176, 9 cells @ 16x16 with 14 border lines */
157 /* Internal dimensions of a cell */
158 #define CELL_WIDTH 16
159 #define CELL_HEIGHT 16
160 #define MARK_OFFS 1 /* Pixels between border and mark */
161 #define MARK_SPACE 1 /* Pixels between two marks */
162 #define MARK_SIZE 4 /* Mark width and height */
164 #elif (LCD_HEIGHT>=240) && (LCD_WIDTH>=320)
165 /* iPod Video - 320x240, 9 cells @ 24x24 with 14 border lines */
167 /* Internal dimensions of a cell */
168 #define CELL_WIDTH 24
169 #define CELL_HEIGHT 24
170 #define MARK_OFFS 1 /* Pixels between border and mark */
171 #define MARK_SPACE 2 /* Pixels between two marks */
172 #define MARK_SIZE 6 /* Mark width and height */
175 #error SUDOKU: Unsupported LCD size
178 #else /* Vertical layout, scratchpad at the bottom */
179 #define VERTICAL_LAYOUT
181 #if ((LCD_HEIGHT==220) && (LCD_WIDTH==176))
182 /* e200, 9 cells @ 16x16 with 14 border lines */
184 /* Internal dimensions of a cell */
185 #define CELL_WIDTH 16
186 #define CELL_HEIGHT 16
187 #define MARK_OFFS 1 /* Pixels between border and mark */
188 #define MARK_SPACE 1 /* Pixels between two marks */
189 #define MARK_SIZE 4 /* Mark width and height */
191 #elif (LCD_HEIGHT>=320) && (LCD_WIDTH>=240)
192 /* Gigabeat - 240x320, 9 cells @ 24x24 with 14 border lines */
194 /* Internal dimensions of a cell */
195 #define CELL_WIDTH 24
196 #define CELL_HEIGHT 24
197 #define MARK_OFFS 1 /* Pixels between border and mark */
198 #define MARK_SPACE 2 /* Pixels between two marks */
199 #define MARK_SIZE 6 /* Mark width and height */
202 #error SUDOKU: Unsupported LCD size
207 #ifdef SUDOKU_BUTTON_CHANGEDIR
213 #define CFGFILE_VERSION 0 /* Current config file version */
214 #define CFGFILE_MINVERSION 0 /* Minimum config file version to accept */
216 #if defined(HAVE_LCD_COLOR) || defined(SUDOKU_BUTTON_POSSIBLE)
218 struct sudoku_config
{
219 #ifdef HAVE_LCD_COLOR
222 #ifdef SUDOKU_BUTTON_POSSIBLE
227 struct sudoku_config sudcfg_disk
= {
228 #ifdef HAVE_LCD_COLOR
231 #ifdef SUDOKU_BUTTON_POSSIBLE
235 struct sudoku_config sudcfg
;
237 static const char cfg_filename
[] = "sudoku.cfg";
238 #ifdef HAVE_LCD_COLOR
239 static char *number_str
[2] = { "black", "coloured" };
241 #ifdef SUDOKU_BUTTON_POSSIBLE
242 static char *mark_str
[2] = { "hide", "show" };
245 struct configdata disk_config
[] = {
246 #ifdef HAVE_LCD_COLOR
247 { TYPE_ENUM
, 0, 2, { .int_p
= &sudcfg_disk
.number_display
}, "numbers",
250 #ifdef SUDOKU_BUTTON_POSSIBLE
251 { TYPE_ENUM
, 0, 2, { .int_p
= &sudcfg_disk
.show_markings
}, "markings",
256 #ifdef HAVE_LCD_COLOR
257 #define NUMBER_TYPE (sudcfg.number_display*CELL_WIDTH)
259 #define NUMBER_TYPE 0
262 /* Size dependent build-time calculations */
264 #define BOARD_WIDTH (CELL_WIDTH*9+10)
265 #define BOARD_HEIGHT (CELL_HEIGHT*9+10)
266 static unsigned char cellxpos
[9]={
267 1, (CELL_WIDTH
+2), (2*CELL_WIDTH
+3),
268 (3*CELL_WIDTH
+4), (4*CELL_WIDTH
+5), (5*CELL_WIDTH
+6),
269 (6*CELL_WIDTH
+7), (7*CELL_WIDTH
+8), (8*CELL_WIDTH
+9)
271 static unsigned char cellypos
[9]={
272 1, (CELL_HEIGHT
+2), (2*CELL_HEIGHT
+3),
273 (3*CELL_HEIGHT
+4), (4*CELL_HEIGHT
+5), (5*CELL_HEIGHT
+6),
274 (6*CELL_HEIGHT
+7), (7*CELL_HEIGHT
+8), (8*CELL_HEIGHT
+9)
276 #else /* !SMALL_BOARD */
277 #define BOARD_WIDTH (CELL_WIDTH*9+10+4)
278 #define BOARD_HEIGHT (CELL_HEIGHT*9+10+4)
279 static unsigned char cellxpos
[9]={
280 2, (CELL_WIDTH
+3), (2*CELL_WIDTH
+4),
281 (3*CELL_WIDTH
+6), (4*CELL_WIDTH
+7), (5*CELL_WIDTH
+8),
282 (6*CELL_WIDTH
+10), (7*CELL_WIDTH
+11), (8*CELL_WIDTH
+12)
284 static unsigned char cellypos
[9]={
285 2, (CELL_HEIGHT
+3), (2*CELL_HEIGHT
+4),
286 (3*CELL_HEIGHT
+6), (4*CELL_HEIGHT
+7), (5*CELL_HEIGHT
+8),
287 (6*CELL_HEIGHT
+10), (7*CELL_HEIGHT
+11), (8*CELL_HEIGHT
+12)
291 #ifdef VERTICAL_LAYOUT
292 #define XOFS ((LCD_WIDTH-BOARD_WIDTH)/2)
293 #define YOFS ((LCD_HEIGHT-(BOARD_HEIGHT+CELL_HEIGHT*2+2))/2)
294 #define YOFSSCRATCHPAD (YOFS+BOARD_HEIGHT+CELL_WIDTH)
296 #define XOFSSCRATCHPAD ((LCD_WIDTH-(BOARD_WIDTH+CELL_WIDTH*2+2))/2)
297 #define XOFS (XOFSSCRATCHPAD+CELL_WIDTH*2+2)
298 #define YOFS ((LCD_HEIGHT-BOARD_HEIGHT)/2)
301 /****** Solver routine by Tom Shackell <shackell@cs.york.ac.uk>
305 http://www-users.cs.york.ac.uk/~shackell/sudoku/Sudoku.html
311 typedef unsigned int Bitset
;
314 #define SIZE (BLOCK*BLOCK)
319 typedef struct _Sudoku
{
320 Bitset table
[SIZE
][SIZE
];
323 typedef struct _Stats
{
330 typedef struct _Options
{
332 bool uniquenessCheck
;
335 void sudoku_init(Sudoku
* sud
);
336 void sudoku_set(Sudoku
* sud
, int x
, int y
, int num
, bool original
);
337 int sudoku_get(Sudoku
* sud
, int x
, int y
, bool* original
);
339 #define BIT(n) ((Bitset)BIT_N(n))
340 #define BIT_TEST(v,n) ((((Bitset)v) & BIT(n)) != 0)
341 #define BIT_CLEAR(v,n) (v) &= ~BIT(n)
342 #define MARK_BIT BIT(0)
343 #define ORIGINAL_BIT BIT(SIZE+1)
345 #define ALL_BITS (BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9))
347 /* initialize a sudoku problem, should be called before using set or get */
348 void sudoku_init(Sudoku
* sud
)
351 for (y
= 0; y
< SIZE
; y
++){
352 for (x
= 0; x
< SIZE
; x
++){
353 sud
->table
[x
][y
] = ALL_BITS
;
358 /* set the number at a particular x and y column */
359 void sudoku_set(Sudoku
* sud
, int x
, int y
, int num
, bool original
)
365 /* clear the row and columns */
366 for (i
= 0; i
< SIZE
; i
++){
367 BIT_CLEAR(sud
->table
[i
][y
], num
);
368 BIT_CLEAR(sud
->table
[x
][i
], num
);
370 /* clear the block */
371 bx
= x
- (x
% BLOCK
);
372 by
= y
- (y
% BLOCK
);
373 for (i
= 0; i
< BLOCK
; i
++){
374 for (j
= 0; j
< BLOCK
; j
++){
375 BIT_CLEAR(sud
->table
[bx
+j
][by
+i
], num
);
379 orig
= original
? ORIGINAL_BIT
: 0;
380 sud
->table
[x
][y
] = BIT(num
) | MARK_BIT
| orig
;
383 /* get the number at a particular x and y column, if this
384 is not unique return 0 */
385 int sudoku_get(Sudoku
* sud
, int x
, int y
, bool* original
)
387 Bitset val
= sud
->table
[x
][y
];
392 *original
= val
& ORIGINAL_BIT
;
394 for (i
= 1; i
<= SIZE
; i
++){
395 if (BIT_TEST(val
, i
)){
405 /* returns true if this is a valid problem, this is necessary because the input
406 problem might be degenerate which breaks the solver algorithm. */
407 static bool is_valid(const Sudoku
* sud
)
411 for (y
= 0; y
< SIZE
; y
++){
412 for (x
= 0; x
< SIZE
; x
++){
413 if ((sud
->table
[x
][y
] & ALL_BITS
) == 0){
421 /* scan the table for the most constrained item, giving all it's options, sets
422 the best x and y coordinates, the number of options and the options for
423 that coordinate and returns true if the puzzle is finished */
424 static bool scan(const Sudoku
* sud
, int* rX
, int* rY
, int *num
, int* options
)
427 int bestCount
= SIZE
+1;
429 bool allMarked
= true;
431 for (y
= 0; y
< SIZE
; y
++){
432 for (x
= 0; x
< SIZE
; x
++){
433 Bitset val
= sud
->table
[x
][y
];
437 if (val
& MARK_BIT
) {
442 for (i
= 1; i
<= SIZE
; i
++){
443 if (BIT_TEST(val
, i
)){
447 if (count
< bestCount
){
452 /* can't possibly be beaten */
459 /* now copy into options */
461 val
= sud
->table
[*rX
][*rY
];
462 for (i
= 1, j
= 0; i
<= SIZE
; i
++){
463 if (BIT_TEST(val
, i
)){
470 static bool solve(Sudoku
* sud
, Stats
* stats
, const Options
* options
);
472 /* try a particular option and return true if that gives a solution or false
473 if it doesn't, restores board on backtracking */
474 static bool spawn_option(Sudoku
* sud
, Stats
* stats
, const Options
* options
,
475 int x
, int y
, int num
)
479 rb
->memcpy(©
,sud
,sizeof(Sudoku
));
480 sudoku_set(©
, x
, y
, num
, false);
481 stats
->numTries
+= 1;
482 if (solve(©
, stats
, options
)){
483 if (!options
->allSolutions
&& stats
->solutionFound
){
484 rb
->memcpy(sud
,©
,sizeof(Sudoku
));
493 /* solve a sudoku problem, returns true if there is a solution and false
494 otherwise. stats is used to track statisticss */
495 static bool solve(Sudoku
* sud
, Stats
* stats
, const Options
* options
)
503 if (scan(sud
, &x
, &y
, &num
, places
)){
504 /* a solution was found! */
505 if (options
->uniquenessCheck
&& stats
->solutionFound
){
506 /*printf("\n\t... But the solution is not unique!\n"); */
509 stats
->solutionFound
= true;
510 if (options
->allSolutions
|| options
->uniquenessCheck
){
511 /*printf("\n\tSolution after %d iterations\n", stats->numTries); */
512 /*sudoku_print(sud); */
520 /* can't be satisfied */
523 /* try all the places (except the last one) */
524 for (i
= 0; i
< num
-1; i
++){
525 if (spawn_option(sud
, stats
, options
, x
, y
, places
[i
])){
526 /* solution found! */
527 if (!options
->allSolutions
&& stats
->solutionFound
){
532 /* take the last place ourself */
533 stats
->numTries
+= 1;
534 sudoku_set(sud
, x
, y
, places
[num
-1], false);
538 /******** END OF IMPORTED CODE */
541 /* A wrapper function between the Sudoku plugin and the above solver code */
542 void sudoku_solve(struct sudoku_state_t
* state
)
551 /* Initialise the parameters */
553 rb
->memset(&stats
,0,sizeof(stats
));
554 options
.allSolutions
=false;
555 options
.uniquenessCheck
=false;
557 /* Convert Rockbox format into format for solver */
560 if (state
->startboard
[r
][c
]!='0') {
561 sudoku_set(&sud
, c
, r
, state
->startboard
[r
][c
]-'0', true);
566 /* need to check for degenerate input problems ... */
568 ret
= solve(&sud
, &stats
, &options
);
574 /* Populate the board with the solution. */
577 state
->currentboard
[r
][c
]='0'+
578 sudoku_get(&sud
, c
, r
, &original
);
582 rb
->splash(HZ
*2, "Solve failed");
588 /* Copies the current to the saved board */
589 static void save_state(struct sudoku_state_t
*state
)
591 rb
->memcpy(state
->savedboard
, state
->currentboard
,
592 sizeof(state
->savedboard
));
595 /* Copies the saved to the current board */
596 static void restore_state(struct sudoku_state_t
*state
)
598 rb
->memcpy(state
->currentboard
, state
->savedboard
,
599 sizeof(state
->savedboard
));
602 void default_state(struct sudoku_state_t
* state
)
606 rb
->strncpy(state
->filename
,GAME_FILE
,MAX_PATH
);
609 state
->startboard
[r
][c
]=default_game
[r
][c
];
610 state
->currentboard
[r
][c
]=default_game
[r
][c
];
611 #ifdef SUDOKU_BUTTON_POSSIBLE
612 state
->possiblevals
[r
][c
]=0;
617 /* initialize the saved board so reload function works */
625 void clear_state(struct sudoku_state_t
* state
)
629 rb
->strncpy(state
->filename
,GAME_FILE
,MAX_PATH
);
632 state
->startboard
[r
][c
]='0';
633 state
->currentboard
[r
][c
]='0';
634 #ifdef SUDOKU_BUTTON_POSSIBLE
635 state
->possiblevals
[r
][c
]=0;
645 /* Check the status of the board, assuming a change at the cursor location */
646 bool check_status(struct sudoku_state_t
* state
)
653 /* First, check the column */
654 for (cell
=0;cell
<9;cell
++) {
658 cell
=state
->currentboard
[r
][state
->x
];
660 if (check
[cell
-'1']==1) {
667 /* Second, check the row */
668 for (cell
=0;cell
<9;cell
++) {
672 cell
=state
->currentboard
[state
->y
][c
];
674 if (check
[cell
-'1']==1) {
681 /* Finally, check the 3x3 sub-grid */
682 for (cell
=0;cell
<9;cell
++) {
687 for (r
=r1
;r
<r1
+3;r
++) {
688 for (c
=c1
;c
<c1
+3;c
++) {
689 cell
=state
->currentboard
[r
][c
];
691 if (check
[cell
-'1']==1) {
699 /* We passed all the checks :) */
704 /* Load game - only ".ss" is officially supported, but any sensible
705 text representation (one line per row) may load.
707 bool load_sudoku(struct sudoku_state_t
* state
, char* filename
)
714 char buf
[300]; /* A buffer to read a sudoku board from */
716 fd
=rb
->open(filename
, O_RDONLY
);
718 LOGF("Invalid sudoku file: %s\n",filename
);
722 rb
->strncpy(state
->filename
,filename
,MAX_PATH
);
723 n
=rb
->read(fd
,buf
,300);
732 while ((i
< n
) && (r
< 9)) {
752 if (c
>= SIZE
|| r
>= SIZE
){
753 LOGF("ERROR: sudoku problem is the wrong size (%d,%d)\n",
760 if (((buf
[i
]>='A') && (buf
[i
]<='I')) ||
761 ((buf
[i
]>='0') && (buf
[i
]<='9'))) {
763 if (r
>= SIZE
|| c
>= SIZE
){
764 LOGF("ERROR: sudoku problem is the wrong size "
768 if ((buf
[i
]>='0') && (buf
[i
]<='9')) {
769 state
->startboard
[r
][c
]=buf
[i
];
770 state
->currentboard
[r
][c
]=buf
[i
];
772 state
->currentboard
[r
][c
]='1'+(buf
[i
]-'A');
776 /* Ignore any other characters */
782 /* Check that the board is valid - we need to check every row/column
783 individually, so we check the diagonal from top-left to bottom-right */
784 for (state
->x
= 0; state
->x
< 9; state
->x
++) {
786 if (check_status(state
)) return false;
791 /* Save a copy of the saved state - so we can reload without using the
797 bool save_sudoku(struct sudoku_state_t
* state
)
805 rb
->splash(0, "Saving...");
806 rb
->memcpy(line
,"...|...|...\r\n",13);
807 rb
->memcpy(sep
,"-----------\r\n",13);
809 if (state
->filename
[0]==0) {
813 fd
=rb
->open(state
->filename
, O_WRONLY
|O_CREAT
);
818 if (state
->startboard
[r
][c
]!='0') {
819 line
[i
]=state
->startboard
[r
][c
];
820 } else if (state
->currentboard
[r
][c
]!='0') {
821 line
[i
]='A'+(state
->currentboard
[r
][c
]-'1');
826 if ((c
==2) || (c
==5)) {
830 rb
->write(fd
,line
,sizeof(line
));
831 if ((r
==2) || (r
==5)) {
832 rb
->write(fd
,sep
,sizeof(sep
));
835 /* Add a blank line at end */
836 rb
->write(fd
,"\r\n",2);
838 rb
->reload_directory();
839 /* Save a copy of the saved state - so we can reload without
848 void clear_board(struct sudoku_state_t
* state
)
854 state
->currentboard
[r
][c
]=state
->startboard
[r
][c
];
861 void update_cell(struct sudoku_state_t
* state
, int r
, int c
)
863 /* We have four types of cell:
864 1) User-entered number
869 if ((r
==state
->y
) && (c
==state
->x
)) {
870 rb
->lcd_bitmap_part(sudoku_inverse
,NUMBER_TYPE
,
871 BITMAP_HEIGHT
*(state
->currentboard
[r
][c
]-'0'),
873 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],CELL_WIDTH
,
876 if (state
->startboard
[r
][c
]!='0') {
877 rb
->lcd_bitmap_part(sudoku_start
,NUMBER_TYPE
,
878 BITMAP_HEIGHT
*(state
->startboard
[r
][c
]-'0'),
880 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],
881 CELL_WIDTH
,CELL_HEIGHT
);
883 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,
884 BITMAP_HEIGHT
*(state
->currentboard
[r
][c
]-'0'),
886 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],
887 CELL_WIDTH
,CELL_HEIGHT
);
891 rb
->lcd_update_rect(cellxpos
[c
],cellypos
[r
],CELL_WIDTH
,CELL_HEIGHT
);
895 void display_board(struct sudoku_state_t
* state
)
898 #ifdef SUDOKU_BUTTON_POSSIBLE
902 /* Clear the display buffer */
903 rb
->lcd_clear_display();
905 /* Draw the gridlines - differently for different targets */
908 /* Small targets - draw dotted/single lines */
912 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[r
]-1);
913 rb
->lcd_vline(XOFS
+cellxpos
[r
]-1,YOFS
,YOFS
+BOARD_HEIGHT
-1);
916 for (c
=XOFS
;c
<XOFS
+BOARD_WIDTH
;c
+=2) {
917 rb
->lcd_drawpixel(c
,YOFS
+cellypos
[r
]-1);
919 for (c
=YOFS
;c
<YOFS
+BOARD_HEIGHT
;c
+=2) {
920 rb
->lcd_drawpixel(XOFS
+cellxpos
[r
]-1,c
);
924 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[8]+CELL_HEIGHT
);
925 rb
->lcd_vline(XOFS
+cellxpos
[8]+CELL_WIDTH
,YOFS
,YOFS
+BOARD_HEIGHT
-1);
927 /* Large targets - draw single/double lines */
929 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[r
]-1);
930 rb
->lcd_vline(XOFS
+cellxpos
[r
]-1,YOFS
,YOFS
+BOARD_HEIGHT
-1);
932 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[r
]-2);
933 rb
->lcd_vline(XOFS
+cellxpos
[r
]-2,YOFS
,YOFS
+BOARD_HEIGHT
-1);
936 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[8]+CELL_HEIGHT
);
937 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFS
+cellypos
[8]+CELL_HEIGHT
+1);
938 rb
->lcd_vline(XOFS
+cellxpos
[8]+CELL_WIDTH
,YOFS
,YOFS
+BOARD_HEIGHT
-1);
939 rb
->lcd_vline(XOFS
+cellxpos
[8]+CELL_WIDTH
+1,YOFS
,YOFS
+BOARD_HEIGHT
-1);
942 #ifdef SUDOKU_BUTTON_POSSIBLE
943 #ifdef VERTICAL_LAYOUT
944 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFSSCRATCHPAD
);
945 rb
->lcd_hline(XOFS
,XOFS
+BOARD_WIDTH
-1,YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
948 /* Small targets - draw dotted/single lines */
951 rb
->lcd_vline(XOFS
+cellxpos
[r
]-1,YOFSSCRATCHPAD
,
952 YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
955 for (c
=YOFSSCRATCHPAD
;c
<YOFSSCRATCHPAD
+CELL_HEIGHT
+1;c
+=2) {
956 rb
->lcd_drawpixel(XOFS
+cellxpos
[r
]-1,c
);
960 /* Large targets - draw single/double lines */
961 rb
->lcd_vline(XOFS
+cellxpos
[r
]-1,YOFSSCRATCHPAD
,
962 YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
964 rb
->lcd_vline(XOFS
+cellxpos
[r
]-2,YOFSSCRATCHPAD
,
965 YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
967 if ((r
>0) && state
->possiblevals
[state
->y
][state
->x
]&BIT_N(r
))
968 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,BITMAP_HEIGHT
*r
,
969 BITMAP_STRIDE
,XOFS
+cellxpos
[r
-1],
970 YOFSSCRATCHPAD
+1,CELL_WIDTH
,CELL_HEIGHT
);
972 rb
->lcd_vline(XOFS
+cellxpos
[8]+CELL_WIDTH
,YOFSSCRATCHPAD
,
973 YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
975 rb
->lcd_vline(XOFS
+cellxpos
[8]+CELL_WIDTH
+1,YOFSSCRATCHPAD
,
976 YOFSSCRATCHPAD
+CELL_HEIGHT
+1);
978 if (state
->possiblevals
[state
->y
][state
->x
]&BIT_N(r
))
979 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,BITMAP_HEIGHT
*r
,
980 BITMAP_STRIDE
,XOFS
+cellxpos
[8],YOFSSCRATCHPAD
+1,
981 CELL_WIDTH
,CELL_HEIGHT
);
982 #else /* Horizontal layout */
983 rb
->lcd_vline(XOFSSCRATCHPAD
,YOFS
,YOFS
+BOARD_HEIGHT
-1);
984 rb
->lcd_vline(XOFSSCRATCHPAD
+CELL_WIDTH
+1,YOFS
,YOFS
+BOARD_HEIGHT
-1);
987 /* Small targets - draw dotted/single lines */
990 rb
->lcd_hline(XOFSSCRATCHPAD
,XOFSSCRATCHPAD
+CELL_WIDTH
+1,
994 for (c
=XOFSSCRATCHPAD
;c
<XOFSSCRATCHPAD
+CELL_WIDTH
+1;c
+=2) {
995 rb
->lcd_drawpixel(c
,YOFS
+cellypos
[r
]-1);
999 /* Large targets - draw single/double lines */
1000 rb
->lcd_hline(XOFSSCRATCHPAD
,XOFSSCRATCHPAD
+CELL_WIDTH
+1,
1001 YOFS
+cellypos
[r
]-1);
1003 rb
->lcd_hline(XOFSSCRATCHPAD
,XOFSSCRATCHPAD
+CELL_WIDTH
+1,
1004 YOFS
+cellypos
[r
]-2);
1006 if ((r
>0) && state
->possiblevals
[state
->y
][state
->x
]&BIT_N(r
))
1007 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,BITMAP_HEIGHT
*r
,
1008 BITMAP_STRIDE
,XOFSSCRATCHPAD
+1,
1009 YOFS
+cellypos
[r
-1],CELL_WIDTH
,CELL_HEIGHT
);
1011 rb
->lcd_hline(XOFSSCRATCHPAD
,XOFSSCRATCHPAD
+CELL_WIDTH
+1,
1012 YOFS
+cellypos
[8]+CELL_HEIGHT
);
1014 rb
->lcd_hline(XOFSSCRATCHPAD
,XOFSSCRATCHPAD
+CELL_WIDTH
+1,
1015 YOFS
+cellypos
[8]+CELL_HEIGHT
+1);
1017 if (state
->possiblevals
[state
->y
][state
->x
]&BIT_N(r
))
1018 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,BITMAP_HEIGHT
*r
,
1019 BITMAP_STRIDE
,XOFSSCRATCHPAD
+1,YOFS
+cellypos
[8],
1020 CELL_WIDTH
,CELL_HEIGHT
);
1022 #endif /* SUDOKU_BUTTON_POSSIBLE */
1024 /* Draw the numbers */
1027 /* We have four types of cell:
1028 1) User-entered number
1033 if ((r
==state
->y
) && (c
==state
->x
)) {
1034 rb
->lcd_bitmap_part(sudoku_inverse
,NUMBER_TYPE
,
1035 BITMAP_HEIGHT
*(state
->currentboard
[r
][c
]-
1038 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],
1039 CELL_WIDTH
,CELL_HEIGHT
);
1041 if (state
->startboard
[r
][c
]!='0') {
1042 rb
->lcd_bitmap_part(sudoku_start
,NUMBER_TYPE
,
1043 BITMAP_HEIGHT
*(state
->startboard
[r
][c
]-
1046 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],
1047 CELL_WIDTH
,CELL_HEIGHT
);
1049 rb
->lcd_bitmap_part(sudoku_normal
,NUMBER_TYPE
,
1051 (state
->currentboard
[r
][c
]-'0'),
1053 XOFS
+cellxpos
[c
],YOFS
+cellypos
[r
],
1054 CELL_WIDTH
,CELL_HEIGHT
);
1057 #ifdef SUDOKU_BUTTON_POSSIBLE
1058 /* Draw the possible number markings on the board */
1059 if(sudcfg
.show_markings
&& state
->startboard
[r
][c
]=='0'
1060 && state
->currentboard
[r
][c
]=='0') {
1062 if(state
->possiblevals
[r
][c
]&(2<<i
)) {
1064 /* draw markings in dark grey */
1065 rb
->lcd_set_foreground(LCD_DARKGRAY
);
1067 rb
->lcd_fillrect(XOFS
+cellxpos
[c
]+MARK_OFFS
1068 +(i
%3)*(MARK_SIZE
+MARK_SPACE
),
1069 YOFS
+cellypos
[r
]+MARK_OFFS
1070 +(i
/3)*(MARK_SIZE
+MARK_SPACE
),
1074 rb
->lcd_set_foreground(LCD_BLACK
);
1079 #endif /* SUDOKU_BUTTON_POSSIBLE */
1084 /* update the screen */
1088 bool sudoku_generate(struct sudoku_state_t
* state
)
1093 struct sudoku_state_t new_state
;
1095 clear_state(&new_state
);
1096 display_board(&new_state
);
1097 rb
->splash(0, "Generating...");
1099 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1100 rb
->cpu_boost(true);
1103 res
= sudoku_generate_board(&new_state
,&difficulty
);
1105 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1106 rb
->cpu_boost(false);
1110 rb
->memcpy(state
,&new_state
,sizeof(new_state
));
1111 rb
->snprintf(str
,sizeof(str
),"Difficulty: %s",difficulty
);
1112 display_board(state
);
1113 rb
->splash(HZ
*3, str
);
1114 rb
->strncpy(state
->filename
,GAME_FILE
,MAX_PATH
);
1116 display_board(&new_state
);
1117 rb
->splash(HZ
*2, "Aborted");
1119 /* initialize the saved board so reload function works */
1124 #ifdef HAVE_LCD_COLOR
1125 static bool numdisplay_setting(void)
1127 static const struct opt_items names
[] = {
1132 return rb
->set_option("Number Display", &sudcfg
.number_display
, INT
, names
,
1133 sizeof(names
) / sizeof(names
[0]), NULL
);
1137 #ifdef SUDOKU_BUTTON_POSSIBLE
1138 static bool showmarkings_setting(void)
1140 static const struct opt_items names
[] = {
1145 return rb
->set_option("Show Markings", &sudcfg
.show_markings
, INT
, names
,
1146 sizeof(names
) / sizeof(names
[0]), NULL
);
1151 SM_AUDIO_PLAYBACK
= 0,
1152 #ifdef HAVE_LCD_COLOR
1155 #ifdef SUDOKU_BUTTON_POSSIBLE
1167 int sudoku_menu(struct sudoku_state_t
* state
)
1171 MENUITEM_STRINGLIST(menu
, "Sudoku Menu", NULL
,
1173 #ifdef HAVE_LCD_COLOR
1176 #ifdef SUDOKU_BUTTON_POSSIBLE
1179 "Save", "Reload", "Clear", "Solve",
1180 "Generate", "New", "Quit");
1182 result
= rb
->do_menu(&menu
, NULL
, NULL
, false);
1185 case SM_AUDIO_PLAYBACK
:
1186 playback_control(NULL
);
1189 #ifdef HAVE_LCD_COLOR
1190 case SM_NUMBER_DISPLAY
:
1191 numdisplay_setting();
1195 #ifdef SUDOKU_BUTTON_POSSIBLE
1196 case SM_SHOW_MARKINGS
:
1197 showmarkings_setting();
1205 restore_state(state
);
1213 sudoku_solve(state
);
1217 sudoku_generate(state
);
1236 /* Menu used when user is in edit mode - i.e. creating a new game manually */
1237 int sudoku_edit_menu(struct sudoku_state_t
* state
)
1241 MENUITEM_STRINGLIST(menu
, "Edit Menu", NULL
,
1244 result
= rb
->do_menu(&menu
, NULL
, NULL
, false);
1247 case 0: /* Save new game */
1248 rb
->kbd_input(state
->filename
,MAX_PATH
);
1249 if (save_sudoku(state
)) {
1252 rb
->splash(HZ
*2, "Save failed");
1266 void move_cursor(struct sudoku_state_t
* state
, int newx
, int newy
)
1270 /* Check that the character at the cursor position is legal */
1271 if (check_status(state
)) {
1272 rb
->splash(HZ
*2, "Illegal move!");
1273 /* Ignore any button presses during the splash */
1274 rb
->button_clear_queue();
1284 /* Redraw current and old cells */
1285 update_cell(state
,oldx
,oldy
);
1286 update_cell(state
,newx
,newy
);
1289 /* plugin entry point */
1290 enum plugin_status
plugin_start(const void* parameter
)
1294 int lastbutton
= BUTTON_NONE
;
1298 struct sudoku_state_t state
;
1300 #if defined(HAVE_LCD_COLOR) || defined(SUDOKU_BUTTON_POSSIBLE)
1301 configfile_load(cfg_filename
, disk_config
,
1302 sizeof(disk_config
) / sizeof(disk_config
[0]),
1303 CFGFILE_MINVERSION
);
1304 rb
->memcpy(&sudcfg
, &sudcfg_disk
, sizeof(sudcfg
)); /* copy to running config */
1308 rb
->lcd_set_backdrop(NULL
);
1309 rb
->lcd_set_foreground(LCD_BLACK
);
1310 rb
->lcd_set_background(LCD_WHITE
);
1313 clear_state(&state
);
1315 if (parameter
==NULL
) {
1316 /* We have been started as a plugin - try default sudoku.ss */
1317 if (!load_sudoku(&state
,GAME_FILE
)) {
1318 /* No previous game saved, use the default */
1319 default_state(&state
);
1322 if (!load_sudoku(&state
,(char*)parameter
)) {
1323 rb
->splash(HZ
*2, "Load error");
1324 return(PLUGIN_ERROR
);
1329 display_board(&state
);
1331 /* The main game loop */
1335 button
= rb
->button_get(true);
1338 #ifdef SUDOKU_BUTTON_QUIT
1340 case SUDOKU_BUTTON_QUIT
:
1341 if (check_status(&state
)) {
1342 rb
->splash(HZ
*2, "Illegal move!");
1343 /* Ignore any button presses during the splash */
1344 rb
->button_clear_queue();
1346 save_sudoku(&state
);
1352 /* Increment digit */
1353 #ifdef SUDOKU_BUTTON_ALTTOGGLE
1354 case SUDOKU_BUTTON_ALTTOGGLE
| BUTTON_REPEAT
:
1356 case SUDOKU_BUTTON_TOGGLE
| BUTTON_REPEAT
:
1357 /* Slow down the repeat speed to 1/3 second */
1358 if ((*rb
->current_tick
-ticks
) < (HZ
/3)) {
1362 #ifdef SUDOKU_BUTTON_ALTTOGGLE
1363 case SUDOKU_BUTTON_ALTTOGGLE
:
1365 case SUDOKU_BUTTON_TOGGLE
:
1366 #ifdef SUDOKU_BUTTON_TOGGLE_PRE
1367 if ((button
== SUDOKU_BUTTON_TOGGLE
)
1368 && (lastbutton
!= SUDOKU_BUTTON_TOGGLE_PRE
))
1371 /* Increment digit */
1372 ticks
=*rb
->current_tick
;
1373 if (state
.editmode
) {
1374 if (state
.startboard
[state
.y
][state
.x
]=='9') {
1375 state
.startboard
[state
.y
][state
.x
]='0';
1376 state
.currentboard
[state
.y
][state
.x
]='0';
1378 state
.startboard
[state
.y
][state
.x
]++;
1379 state
.currentboard
[state
.y
][state
.x
]++;
1382 if (state
.startboard
[state
.y
][state
.x
]=='0') {
1383 if (state
.currentboard
[state
.y
][state
.x
]=='9') {
1384 state
.currentboard
[state
.y
][state
.x
]='0';
1386 state
.currentboard
[state
.y
][state
.x
]++;
1390 update_cell(&state
,state
.y
,state
.x
);
1393 #ifdef SUDOKU_BUTTON_TOGGLEBACK
1394 case SUDOKU_BUTTON_TOGGLEBACK
| BUTTON_REPEAT
:
1395 /* Slow down the repeat speed to 1/3 second */
1396 if ((*rb
->current_tick
-ticks
) < (HZ
/3)) {
1400 case SUDOKU_BUTTON_TOGGLEBACK
:
1401 /* Decrement digit */
1402 ticks
=*rb
->current_tick
;
1403 if (state
.editmode
) {
1404 if (state
.startboard
[state
.y
][state
.x
]=='0') {
1405 state
.startboard
[state
.y
][state
.x
]='9';
1406 state
.currentboard
[state
.y
][state
.x
]='9';
1408 state
.startboard
[state
.y
][state
.x
]--;
1409 state
.currentboard
[state
.y
][state
.x
]--;
1412 if (state
.startboard
[state
.y
][state
.x
]=='0') {
1413 if (state
.currentboard
[state
.y
][state
.x
]=='0') {
1414 state
.currentboard
[state
.y
][state
.x
]='9';
1416 state
.currentboard
[state
.y
][state
.x
]--;
1420 update_cell(&state
,state
.y
,state
.x
);
1424 /* move cursor left */
1425 case SUDOKU_BUTTON_LEFT
:
1426 case (SUDOKU_BUTTON_LEFT
| BUTTON_REPEAT
):
1427 if ( (state
.x
==0&&invertdir
==0) || (state
.y
==0&&invertdir
==1) ) {
1428 #ifndef SUDOKU_BUTTON_UP
1429 if ( (state
.y
==0&&invertdir
==0) || (state
.x
==0&&invertdir
==1)) {
1430 move_cursor(&state
,8,8);
1433 move_cursor(&state
,8,state
.y
-1);
1435 move_cursor(&state
,state
.x
-1,8);
1439 move_cursor(&state
,8,state
.y
);
1443 move_cursor(&state
,state
.x
-1,state
.y
);
1445 move_cursor(&state
,state
.x
,state
.y
-1);
1450 /* move cursor right */
1451 case SUDOKU_BUTTON_RIGHT
:
1452 case (SUDOKU_BUTTON_RIGHT
| BUTTON_REPEAT
):
1453 if ( (state
.x
==8&&invertdir
==0) || (state
.y
==8&&invertdir
==1) ) {
1454 #ifndef SUDOKU_BUTTON_DOWN
1455 if ( (state
.y
==8&&invertdir
==0) || (state
.x
==8&&invertdir
==1) ) {
1456 move_cursor(&state
,0,0);
1459 move_cursor(&state
,0,state
.y
+1);
1461 move_cursor(&state
,state
.x
+1,0);
1465 move_cursor(&state
,0,state
.y
);
1469 move_cursor(&state
,state
.x
+1,state
.y
);
1471 move_cursor(&state
,state
.x
,state
.y
+1);
1476 #ifdef SUDOKU_BUTTON_UP
1477 /* move cursor up */
1478 case SUDOKU_BUTTON_UP
:
1479 case (SUDOKU_BUTTON_UP
| BUTTON_REPEAT
):
1481 move_cursor(&state
,state
.x
,8);
1483 move_cursor(&state
,state
.x
,state
.y
-1);
1488 #ifdef SUDOKU_BUTTON_DOWN
1489 /* move cursor down */
1490 case SUDOKU_BUTTON_DOWN
:
1491 case (SUDOKU_BUTTON_DOWN
| BUTTON_REPEAT
):
1493 move_cursor(&state
,state
.x
,0);
1495 move_cursor(&state
,state
.x
,state
.y
+1);
1500 case SUDOKU_BUTTON_MENU
:
1501 #ifdef SUDOKU_BUTTON_MENU_PRE
1502 if (lastbutton
!= SUDOKU_BUTTON_MENU_PRE
)
1505 /* Don't let the user leave a game in a bad state */
1506 if (check_status(&state
)) {
1507 rb
->splash(HZ
*2, "Illegal move!");
1508 /* Ignore any button presses during the splash */
1509 rb
->button_clear_queue();
1511 if (state
.editmode
) {
1512 res
= sudoku_edit_menu(&state
);
1513 if (res
== MENU_ATTACHED_USB
) {
1514 rc
= PLUGIN_USB_CONNECTED
;
1516 } else if (res
== 1) { /* Quit */
1520 res
= sudoku_menu(&state
);
1521 if (res
== MENU_ATTACHED_USB
) {
1522 rc
= PLUGIN_USB_CONNECTED
;
1524 } else if (res
== SM_QUIT
) {
1530 #ifdef SUDOKU_BUTTON_POSSIBLE
1531 case SUDOKU_BUTTON_POSSIBLE
:
1532 /* Toggle current number in the possiblevals structure */
1533 if (state
.currentboard
[state
.y
][state
.x
]!='0') {
1534 state
.possiblevals
[state
.y
][state
.x
]^=
1535 BIT_N(state
.currentboard
[state
.y
][state
.x
] - '0');
1540 #ifdef SUDOKU_BUTTON_CHANGEDIR
1541 case SUDOKU_BUTTON_CHANGEDIR
:
1542 /* Change scroll wheel direction */
1543 invertdir
=!invertdir
;
1547 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
) {
1548 /* Quit if USB has been connected */
1549 rc
= PLUGIN_USB_CONNECTED
;
1554 if (button
!= BUTTON_NONE
)
1555 lastbutton
= button
;
1557 display_board(&state
);
1559 #if defined(HAVE_LCD_COLOR) || defined(SUDOKU_BUTTON_POSSIBLE)
1560 if (rb
->memcmp(&sudcfg
, &sudcfg_disk
, sizeof(sudcfg
))) /* save settings if changed */
1562 rb
->memcpy(&sudcfg_disk
, &sudcfg
, sizeof(sudcfg
));
1563 configfile_save(cfg_filename
, disk_config
,
1564 sizeof(disk_config
) / sizeof(disk_config
[0]),