Make open() posix compliant api-wise. A few calls (those with O_CREAT) need the addit...
[kugel-rb.git] / apps / plugins / sudoku / sudoku.c
blob4a7fbd93a8e10d51030e126825b90159f5e5dfbd
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
22 /***
23 Sudoku by Dave Chapman
25 User instructions
26 -----------------
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
32 further options:
34 Save
35 Reload
36 Clear
37 Solve
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 ...|...|... ...|...|...
61 #include "plugin.h"
62 #include "lib/configfile.h"
64 #ifdef HAVE_LCD_BITMAP
66 #include <lib/playback_control.h>
67 #include "sudoku.h"
68 #include "generator.h"
70 /* The bitmaps */
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 STRIDE(SCREEN_MAIN, BMPWIDTH_sudoku_normal, BMPHEIGHT_sudoku_normal)
78 #if (LCD_DEPTH>2)
79 #define BITMAP_WIDTH (BMPWIDTH_sudoku_normal/2)
80 #else
81 #define BITMAP_WIDTH BMPWIDTH_sudoku_normal
82 #endif
84 PLUGIN_HEADER
86 /* Default game - used to initialise sudoku.ss if it doesn't exist. */
87 static const char default_game[9][9] =
89 { '0','1','0', '3','0','7', '0','0','4' },
90 { '0','0','0', '0','6','0', '1','0','2' },
91 { '0','0','0', '0','8','0', '5','6','0' },
93 { '0','6','0', '0','0','0', '0','2','9' },
94 { '0','0','0', '5','0','3', '0','0','0' },
95 { '7','9','0', '0','0','0', '0','3','0' },
97 { '0','8','5', '0','3','0', '0','0','0' },
98 { '1','0','2', '0','7','0', '0','0','0' },
99 { '0','0','0', '4','0','8', '0','5','0' },
102 #if LCD_HEIGHT <= LCD_WIDTH /* Horizontal layout, scratchpad at the left */
104 #if (LCD_HEIGHT==64) && (LCD_WIDTH==112 || LCD_WIDTH==128)
105 /* Archos Recorders and Ondios - 112x64, 9 cells @ 8x6 with 10 border lines */
106 #define SMALL_BOARD
107 #define MARK_OFFS 1 /* Pixels between border and mark */
108 #define MARK_SPACE 1 /* Pixels between two marks */
109 #define MARK_SIZE 1 /* Mark width and height */
111 #elif ((LCD_HEIGHT==80) && (LCD_WIDTH==132))
112 /* C200, 9 cells @ 8x8 with 8 border lines */
113 #define SMALL_BOARD
114 #define MARK_OFFS 1 /* Pixels between border and mark */
115 #define MARK_SPACE 1 /* Pixels between two marks */
116 #define MARK_SIZE 1 /* Mark width and height */
118 #elif ((LCD_HEIGHT==96) && (LCD_WIDTH==128))
119 /* iAudio M3, 9 cells @ 9x9 with 14 border lines */
120 #define MARK_OFFS 1 /* Pixels between border and mark */
121 #define MARK_SPACE 2 /* Pixels between two marks */
122 #define MARK_SIZE 1 /* Mark width and height */
124 #elif (LCD_HEIGHT==110) && (LCD_WIDTH==138) \
125 || (LCD_HEIGHT==128) && (LCD_WIDTH==128)
126 /* iPod Mini - 138x110, 9 cells @ 10x10 with 14 border lines */
127 /* iriver H10 5-6GB - 128x128, 9 cells @ 10x10 with 14 border lines */
128 #define MARK_OFFS 1 /* Pixels between border and mark */
129 #define MARK_SPACE 1 /* Pixels between two marks */
130 #define MARK_SIZE 2 /* Mark width and height */
132 #elif ((LCD_HEIGHT==128) && (LCD_WIDTH==160)) \
133 || ((LCD_HEIGHT==132) && (LCD_WIDTH==176))
134 /* iAudio X5, Iriver H1x0, iPod G3, G4 - 160x128; */
135 /* iPod Nano - 176x132, 9 cells @ 12x12 with 14 border lines */
136 #define MARK_OFFS 1 /* Pixels between border and mark */
137 #define MARK_SPACE 2 /* Pixels between two marks */
138 #define MARK_SIZE 2 /* Mark width and height */
140 #elif ((LCD_HEIGHT==176) && (LCD_WIDTH==220))
141 /* Iriver h300, iPod Color/Photo - 220x176, 9 cells @ 16x16 with 14 border lines */
142 #define MARK_OFFS 1 /* Pixels between border and mark */
143 #define MARK_SPACE 1 /* Pixels between two marks */
144 #define MARK_SIZE 4 /* Mark width and height */
146 #elif (LCD_HEIGHT==240) && (LCD_WIDTH==320)
147 /* iPod Video - 320x240, 9 cells @ 24x24 with 14 border lines */
148 #define MARK_OFFS 1 /* Pixels between border and mark */
149 #define MARK_SPACE 2 /* Pixels between two marks */
150 #define MARK_SIZE 6 /* Mark width and height */
152 #elif (LCD_HEIGHT==480) && (LCD_WIDTH==640)
153 /* M:Robe 500 - 640x480, 9 cells @ 48x48 with 14 border lines */
154 #define MARK_OFFS 1 /* Pixels between border and mark */
155 #define MARK_SPACE 2 /* Pixels between two marks */
156 #define MARK_SIZE 6 /* Mark width and height */
158 #else
159 #error SUDOKU: Unsupported LCD size
160 #endif
162 #else /* Vertical layout, scratchpad at the bottom */
163 #define VERTICAL_LAYOUT
165 #if ((LCD_HEIGHT==220) && (LCD_WIDTH==176))
166 /* e200, 9 cells @ 16x16 with 14 border lines */
167 #define MARK_OFFS 1 /* Pixels between border and mark */
168 #define MARK_SPACE 1 /* Pixels between two marks */
169 #define MARK_SIZE 4 /* Mark width and height */
171 #elif (LCD_HEIGHT>=320) && (LCD_WIDTH>=240)
172 /* Gigabeat - 240x320, 9 cells @ 24x24 with 14 border lines */
173 #define MARK_OFFS 1 /* Pixels between border and mark */
174 #define MARK_SPACE 2 /* Pixels between two marks */
175 #define MARK_SIZE 6 /* Mark width and height */
177 #elif ((LCD_HEIGHT==160) && (LCD_WIDTH==128))
178 /* Philips GoGear SA9200 - 128x160, 9 cells @ 10x10 with 14 border tiles */
179 #define MARK_OFFS 1 /* Pixels between border and mark */
180 #define MARK_SPACE 1 /* Pixels between two marks */
181 #define MARK_SIZE 2 /* Mark width and height */
183 #else
184 #error SUDOKU: Unsupported LCD size
185 #endif
187 #endif /* Layout */
189 #define CELL_WIDTH BITMAP_WIDTH
190 #define CELL_HEIGHT BITMAP_HEIGHT
192 #ifdef SUDOKU_BUTTON_CHANGEDIR
193 int invertdir=0;
194 #else
195 #define invertdir 0
196 #endif
198 #define CFGFILE_VERSION 0 /* Current config file version */
199 #define CFGFILE_MINVERSION 0 /* Minimum config file version to accept */
201 #if defined(HAVE_LCD_COLOR) || defined(SUDOKU_BUTTON_POSSIBLE)
202 /* settings */
203 struct sudoku_config {
204 #ifdef HAVE_LCD_COLOR
205 int number_display;
206 #endif
207 #ifdef SUDOKU_BUTTON_POSSIBLE
208 int show_markings;
209 #endif
212 struct sudoku_config sudcfg_disk = {
213 #ifdef HAVE_LCD_COLOR
215 #endif
216 #ifdef SUDOKU_BUTTON_POSSIBLE
218 #endif
220 struct sudoku_config sudcfg;
222 static const char cfg_filename[] = "sudoku.cfg";
223 #ifdef HAVE_LCD_COLOR
224 static char *number_str[2] = { "black", "coloured" };
225 #endif
226 #ifdef SUDOKU_BUTTON_POSSIBLE
227 static char *mark_str[2] = { "hide", "show" };
228 #endif
230 struct configdata disk_config[] = {
231 #ifdef HAVE_LCD_COLOR
232 { TYPE_ENUM, 0, 2, { .int_p = &sudcfg_disk.number_display }, "numbers",
233 number_str },
234 #endif
235 #ifdef SUDOKU_BUTTON_POSSIBLE
236 { TYPE_ENUM, 0, 2, { .int_p = &sudcfg_disk.show_markings }, "markings",
237 mark_str },
238 #endif
240 #endif
241 #ifdef HAVE_LCD_COLOR
242 #define NUMBER_TYPE (sudcfg.number_display*CELL_WIDTH)
243 #else
244 #define NUMBER_TYPE 0
245 #endif
247 /* Size dependent build-time calculations */
248 #ifdef SMALL_BOARD
249 #define BOARD_WIDTH (CELL_WIDTH*9+10)
250 #define BOARD_HEIGHT (CELL_HEIGHT*9+10)
251 static unsigned int cellxpos[9]={
252 1, (CELL_WIDTH+2), (2*CELL_WIDTH+3),
253 (3*CELL_WIDTH+4), (4*CELL_WIDTH+5), (5*CELL_WIDTH+6),
254 (6*CELL_WIDTH+7), (7*CELL_WIDTH+8), (8*CELL_WIDTH+9)
256 static unsigned int cellypos[9]={
257 1, (CELL_HEIGHT+2), (2*CELL_HEIGHT+3),
258 (3*CELL_HEIGHT+4), (4*CELL_HEIGHT+5), (5*CELL_HEIGHT+6),
259 (6*CELL_HEIGHT+7), (7*CELL_HEIGHT+8), (8*CELL_HEIGHT+9)
261 #else /* !SMALL_BOARD */
262 #define BOARD_WIDTH (CELL_WIDTH*9+10+4)
263 #define BOARD_HEIGHT (CELL_HEIGHT*9+10+4)
264 static unsigned int cellxpos[9]={
265 2, (CELL_WIDTH +3), (2*CELL_WIDTH +4),
266 (3*CELL_WIDTH +6), (4*CELL_WIDTH +7), (5*CELL_WIDTH +8),
267 (6*CELL_WIDTH+10), (7*CELL_WIDTH+11), (8*CELL_WIDTH+12)
269 static unsigned int cellypos[9]={
270 2, (CELL_HEIGHT +3), (2*CELL_HEIGHT +4),
271 (3*CELL_HEIGHT +6), (4*CELL_HEIGHT +7), (5*CELL_HEIGHT +8),
272 (6*CELL_HEIGHT+10), (7*CELL_HEIGHT+11), (8*CELL_HEIGHT+12)
274 #endif
276 #ifdef VERTICAL_LAYOUT
277 #define XOFS ((LCD_WIDTH-BOARD_WIDTH)/2)
278 #define YOFS ((LCD_HEIGHT-(BOARD_HEIGHT+CELL_HEIGHT*2+2))/2)
279 #define YOFSSCRATCHPAD (YOFS+BOARD_HEIGHT+CELL_WIDTH)
280 #else
281 #define XOFSSCRATCHPAD ((LCD_WIDTH-(BOARD_WIDTH+CELL_WIDTH*2+2))/2)
282 #define XOFS (XOFSSCRATCHPAD+CELL_WIDTH*2+2)
283 #define YOFS ((LCD_HEIGHT-BOARD_HEIGHT)/2)
284 #endif
286 #define BLOCK 3
287 #define SIZE (BLOCK*BLOCK)
289 #if 0
290 /****** Solver routine by Tom Shackell <shackell@cs.york.ac.uk>
292 Downloaded from:
294 http://www-users.cs.york.ac.uk/~shackell/sudoku/Sudoku.html
296 Released under GPLv2
300 typedef unsigned int Bitset;
302 #define true 1
303 #define false 0
305 typedef struct _Sudoku {
306 Bitset table[SIZE][SIZE];
307 }Sudoku;
309 typedef struct _Stats {
310 int numTries;
311 int backTracks;
312 int numEmpty;
313 bool solutionFound;
314 }Stats;
316 typedef struct _Options {
317 bool allSolutions;
318 bool uniquenessCheck;
319 }Options;
321 void sudoku_init(Sudoku* sud);
322 void sudoku_set(Sudoku* sud, int x, int y, int num, bool original);
323 int sudoku_get(Sudoku* sud, int x, int y, bool* original);
325 #define BIT(n) ((Bitset)BIT_N(n))
326 #define BIT_TEST(v,n) ((((Bitset)v) & BIT(n)) != 0)
327 #define BIT_CLEAR(v,n) (v) &= ~BIT(n)
328 #define MARK_BIT BIT(0)
329 #define ORIGINAL_BIT BIT(SIZE+1)
331 #define ALL_BITS (BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9))
333 /* initialize a sudoku problem, should be called before using set or get */
334 void sudoku_init(Sudoku* sud)
336 int y, x;
337 for (y = 0; y < SIZE; y++){
338 for (x = 0; x < SIZE; x++){
339 sud->table[x][y] = ALL_BITS;
344 /* set the number at a particular x and y column */
345 void sudoku_set(Sudoku* sud, int x, int y, int num, bool original)
347 int i, j;
348 int bx, by;
349 Bitset orig;
351 /* clear the row and columns */
352 for (i = 0; i < SIZE; i++){
353 BIT_CLEAR(sud->table[i][y], num);
354 BIT_CLEAR(sud->table[x][i], num);
356 /* clear the block */
357 bx = x - (x % BLOCK);
358 by = y - (y % BLOCK);
359 for (i = 0; i < BLOCK; i++){
360 for (j = 0; j < BLOCK; j++){
361 BIT_CLEAR(sud->table[bx+j][by+i], num);
364 /* mark the table */
365 orig = original ? ORIGINAL_BIT : 0;
366 sud->table[x][y] = BIT(num) | MARK_BIT | orig;
369 /* get the number at a particular x and y column, if this
370 is not unique return 0 */
371 int sudoku_get(Sudoku* sud, int x, int y, bool* original)
373 Bitset val = sud->table[x][y];
374 int result = 0;
375 int i;
377 if (original) {
378 *original = val & ORIGINAL_BIT;
380 for (i = 1; i <= SIZE; i++){
381 if (BIT_TEST(val, i)){
382 if (result != 0){
383 return 0;
385 result = i;
388 return result;
391 /* returns true if this is a valid problem, this is necessary because the input
392 problem might be degenerate which breaks the solver algorithm. */
393 static bool is_valid(const Sudoku* sud)
395 int x, y;
397 for (y = 0; y < SIZE; y++){
398 for (x = 0; x < SIZE; x++){
399 if ((sud->table[x][y] & ALL_BITS) == 0){
400 return false;
404 return true;
407 /* scan the table for the most constrained item, giving all it's options, sets
408 the best x and y coordinates, the number of options and the options for
409 that coordinate and returns true if the puzzle is finished */
410 static bool scan(const Sudoku* sud, int* rX, int* rY, int *num, int* options)
412 int x, y, i, j;
413 int bestCount = SIZE+1;
414 Bitset val;
415 bool allMarked = true;
417 for (y = 0; y < SIZE; y++){
418 for (x = 0; x < SIZE; x++){
419 Bitset val = sud->table[x][y];
420 int i;
421 int count = 0;
423 if (val & MARK_BIT) {
424 /* already set */
425 continue;
427 allMarked = false;
428 for (i = 1; i <= SIZE; i++){
429 if (BIT_TEST(val, i)){
430 count++;
433 if (count < bestCount){
434 bestCount = count;
435 *rX = x;
436 *rY = y;
437 if (count == 0){
438 /* can't possibly be beaten */
439 *num = 0;
440 return false;
445 /* now copy into options */
446 *num = bestCount;
447 val = sud->table[*rX][*rY];
448 for (i = 1, j = 0; i <= SIZE; i++){
449 if (BIT_TEST(val, i)){
450 options[j++] = i;
453 return allMarked;
456 static bool solve(Sudoku* sud, Stats* stats, const Options* options);
458 /* try a particular option and return true if that gives a solution or false
459 if it doesn't, restores board on backtracking */
460 static bool spawn_option(Sudoku* sud, Stats* stats, const Options* options,
461 int x, int y, int num)
463 Sudoku copy;
465 rb->memcpy(&copy,sud,sizeof(Sudoku));
466 sudoku_set(&copy, x, y, num, false);
467 stats->numTries += 1;
468 if (solve(&copy, stats, options)){
469 if (!options->allSolutions && stats->solutionFound){
470 rb->memcpy(sud,&copy,sizeof(Sudoku));
472 return true;
473 }else{
474 stats->backTracks++;
476 return false;
479 /* solve a sudoku problem, returns true if there is a solution and false
480 otherwise. stats is used to track statisticss */
481 static bool solve(Sudoku* sud, Stats* stats, const Options* options)
483 while (true){
484 int x = 0;
485 int y = 0;
486 int i, num;
487 int places[SIZE];
489 if (scan(sud, &x, &y, &num, places)){
490 /* a solution was found! */
491 if (options->uniquenessCheck && stats->solutionFound){
492 /*printf("\n\t... But the solution is not unique!\n"); */
493 return true;
495 stats->solutionFound = true;
496 if (options->allSolutions || options->uniquenessCheck){
497 /*printf("\n\tSolution after %d iterations\n", stats->numTries); */
498 /*sudoku_print(sud); */
499 return false;
501 else{
502 return true;
505 if (num == 0){
506 /* can't be satisfied */
507 return false;
509 /* try all the places (except the last one) */
510 for (i = 0; i < num-1; i++){
511 if (spawn_option(sud, stats, options, x, y, places[i])){
512 /* solution found! */
513 if (!options->allSolutions && stats->solutionFound){
514 return true;
518 /* take the last place ourself */
519 stats->numTries += 1;
520 sudoku_set(sud, x, y, places[num-1], false);
524 /******** END OF IMPORTED CODE */
527 /* A wrapper function between the Sudoku plugin and the above solver code */
528 void sudoku_solve(struct sudoku_state_t* state)
530 bool ret;
531 Stats stats;
532 Options options;
533 Sudoku sud;
534 bool original;
535 int r,c;
537 /* Initialise the parameters */
538 sudoku_init(&sud);
539 rb->memset(&stats,0,sizeof(stats));
540 options.allSolutions=false;
541 options.uniquenessCheck=false;
543 /* Convert Rockbox format into format for solver */
544 for (r=0;r<9;r++) {
545 for (c=0;c<9;c++) {
546 if (state->startboard[r][c]!='0') {
547 sudoku_set(&sud, c, r, state->startboard[r][c]-'0', true);
552 /* need to check for degenerate input problems ... */
553 if (is_valid(&sud)){
554 ret = solve(&sud, &stats, &options);
555 } else {
556 ret = false;
559 if (ret) {
560 /* Populate the board with the solution. */
561 for (r=0;r<9;r++) {
562 for (c=0;c<9;c++) {
563 state->currentboard[r][c]='0'+
564 sudoku_get(&sud, c, r, &original);
567 } else {
568 rb->splash(HZ*2, "Solve failed");
571 return;
573 #endif /* 0 */
575 void sudoku_solve(struct sudoku_state_t* state)
577 bool ret = sudoku_solve_board(state);
579 if (!ret) {
580 rb->splash(HZ*2, "Solve failed");
583 return;
586 /* Copies the current to the saved board */
587 static void save_state(struct sudoku_state_t *state)
589 rb->memcpy(state->savedboard, state->currentboard,
590 sizeof(state->savedboard));
593 /* Copies the saved to the current board */
594 static void restore_state(struct sudoku_state_t *state)
596 rb->memcpy(state->currentboard, state->savedboard,
597 sizeof(state->savedboard));
600 void default_state(struct sudoku_state_t* state)
602 int r,c;
604 rb->strlcpy(state->filename,GAME_FILE,MAX_PATH);
605 for (r=0;r<9;r++) {
606 for (c=0;c<9;c++) {
607 state->startboard[r][c]=default_game[r][c];
608 state->currentboard[r][c]=default_game[r][c];
609 #ifdef SUDOKU_BUTTON_POSSIBLE
610 state->possiblevals[r][c]=0;
611 #endif
615 /* initialize the saved board so reload function works */
616 save_state(state);
618 state->x=0;
619 state->y=0;
620 state->editmode=0;
623 void clear_state(struct sudoku_state_t* state)
625 int r,c;
627 rb->strlcpy(state->filename,GAME_FILE,MAX_PATH);
628 for (r=0;r<9;r++) {
629 for (c=0;c<9;c++) {
630 state->startboard[r][c]='0';
631 state->currentboard[r][c]='0';
632 #ifdef SUDOKU_BUTTON_POSSIBLE
633 state->possiblevals[r][c]=0;
634 #endif
638 state->x=0;
639 state->y=0;
640 state->editmode=0;
643 /* Check the status of the board, assuming a change at the cursor location */
644 bool check_status(struct sudoku_state_t* state)
646 int check[9];
647 int r,c;
648 int r1,c1;
649 int cell;
651 /* First, check the column */
652 for (cell=0;cell<9;cell++) {
653 check[cell]=0;
655 for (r=0;r<9;r++) {
656 cell=state->currentboard[r][state->x];
657 if (cell!='0') {
658 if (check[cell-'1']==1) {
659 return true;
661 check[cell-'1']=1;
665 /* Second, check the row */
666 for (cell=0;cell<9;cell++) {
667 check[cell]=0;
669 for (c=0;c<9;c++) {
670 cell=state->currentboard[state->y][c];
671 if (cell!='0') {
672 if (check[cell-'1']==1) {
673 return true;
675 check[cell-'1']=1;
679 /* Finally, check the 3x3 sub-grid */
680 for (cell=0;cell<9;cell++) {
681 check[cell]=0;
683 r1=(state->y/3)*3;
684 c1=(state->x/3)*3;
685 for (r=r1;r<r1+3;r++) {
686 for (c=c1;c<c1+3;c++) {
687 cell=state->currentboard[r][c];
688 if (cell!='0') {
689 if (check[cell-'1']==1) {
690 return true;
692 check[cell-'1']=1;
697 /* We passed all the checks :) */
699 return false;
702 /* Load game - only ".ss" is officially supported, but any sensible
703 text representation (one line per row) may load.
705 bool load_sudoku(struct sudoku_state_t* state, char* filename)
707 int fd;
708 size_t n;
709 int r = 0, c = 0;
710 unsigned int i;
711 int valid=0;
712 char buf[300]; /* A buffer to read a sudoku board from */
714 fd=rb->open(filename, O_RDONLY);
715 if (fd < 0) {
716 LOGF("Invalid sudoku file: %s\n",filename);
717 return(false);
720 rb->strlcpy(state->filename,filename,MAX_PATH);
721 n=rb->read(fd,buf,300);
722 if (n <= 0) {
723 return(false);
725 rb->close(fd);
727 r=0;
728 c=0;
729 i=0;
730 while ((i < n) && (r < 9)) {
731 switch (buf[i]){
732 case ' ': case '\t':
733 if (c > 0)
734 valid=1;
735 break;
736 case '|':
737 case '*':
738 case '-':
739 case '\r':
740 break;
741 case '\n':
742 if (valid) {
743 r++;
744 valid=0;
746 c = 0;
747 break;
748 case '_': case '.':
749 valid=1;
750 if (c >= SIZE || r >= SIZE){
751 LOGF("ERROR: sudoku problem is the wrong size (%d,%d)\n",
752 c, r);
753 return(false);
755 c++;
756 break;
757 default:
758 if (((buf[i]>='A') && (buf[i]<='I')) ||
759 ((buf[i]>='0') && (buf[i]<='9'))) {
760 valid=1;
761 if (r >= SIZE || c >= SIZE){
762 LOGF("ERROR: sudoku problem is the wrong size "
763 "(%d,%d)\n", c, r);
764 return(false);
766 if ((buf[i]>='0') && (buf[i]<='9')) {
767 state->startboard[r][c]=buf[i];
768 state->currentboard[r][c]=buf[i];
769 } else {
770 state->currentboard[r][c]='1'+(buf[i]-'A');
772 c++;
774 /* Ignore any other characters */
775 break;
777 i++;
780 /* Check that the board is valid - we need to check every row/column
781 and block individually */
782 for (state->y = 0; state->y < 9; state->y++) {
783 state->x = (state->y%3)*3 + (state->y/3);
784 if (check_status(state)) return false;
786 state->x = 0;
787 state->y = 0;
789 /* Save a copy of the saved state - so we can reload without using the
790 disk */
791 save_state(state);
792 return(true);
795 bool save_sudoku(struct sudoku_state_t* state)
797 int fd;
798 int r,c;
799 int i;
800 char line[13];
801 char sep[13];
803 rb->splash(0, "Saving...");
804 rb->memcpy(line,"...|...|...\r\n",13);
805 rb->memcpy(sep,"-----------\r\n",13);
807 if (state->filename[0]==0) {
808 return false;
811 fd=rb->open(state->filename, O_WRONLY|O_CREAT, 0666);
812 if (fd >= 0) {
813 for (r=0;r<9;r++) {
814 i=0;
815 for (c=0;c<9;c++) {
816 if (state->startboard[r][c]!='0') {
817 line[i]=state->startboard[r][c];
818 } else if (state->currentboard[r][c]!='0') {
819 line[i]='A'+(state->currentboard[r][c]-'1');
820 } else {
821 line[i]='.';
823 i++;
824 if ((c==2) || (c==5)) {
825 i++;
828 rb->write(fd,line,sizeof(line));
829 if ((r==2) || (r==5)) {
830 rb->write(fd,sep,sizeof(sep));
833 /* Add a blank line at end */
834 rb->write(fd,"\r\n",2);
835 rb->close(fd);
836 rb->reload_directory();
837 /* Save a copy of the saved state - so we can reload without
838 using the disk */
839 save_state(state);
840 return true;
841 } else {
842 return false;
846 void clear_board(struct sudoku_state_t* state)
848 int r,c;
850 for (r=0;r<9;r++) {
851 for (c=0;c<9;c++) {
852 state->currentboard[r][c]=state->startboard[r][c];
855 state->x=0;
856 state->y=0;
859 void update_cell(struct sudoku_state_t* state, int r, int c)
861 /* We have four types of cell:
862 1) User-entered number
863 2) Starting number
864 3) Cursor in cell
867 if ((r==state->y) && (c==state->x)) {
868 rb->lcd_bitmap_part(sudoku_inverse,NUMBER_TYPE,
869 BITMAP_HEIGHT*(state->currentboard[r][c]-'0'),
870 BITMAP_STRIDE,
871 XOFS+cellxpos[c],YOFS+cellypos[r],CELL_WIDTH,
872 CELL_HEIGHT);
873 } else {
874 if (state->startboard[r][c]!='0') {
875 rb->lcd_bitmap_part(sudoku_start,NUMBER_TYPE,
876 BITMAP_HEIGHT*(state->startboard[r][c]-'0'),
877 BITMAP_STRIDE,
878 XOFS+cellxpos[c],YOFS+cellypos[r],
879 CELL_WIDTH,CELL_HEIGHT);
880 } else {
881 rb->lcd_bitmap_part(sudoku_normal,NUMBER_TYPE,
882 BITMAP_HEIGHT*(state->currentboard[r][c]-'0'),
883 BITMAP_STRIDE,
884 XOFS+cellxpos[c],YOFS+cellypos[r],
885 CELL_WIDTH,CELL_HEIGHT);
889 rb->lcd_update_rect(cellxpos[c],cellypos[r],CELL_WIDTH,CELL_HEIGHT);
893 void display_board(struct sudoku_state_t* state)
895 int r,c;
896 #ifdef SUDOKU_BUTTON_POSSIBLE
897 int i;
898 #endif
900 /* Clear the display buffer */
901 rb->lcd_clear_display();
903 /* Draw the gridlines - differently for different targets */
905 #ifdef SMALL_BOARD
906 /* Small targets - draw dotted/single lines */
907 for (r=0;r<9;r++) {
908 if ((r % 3)==0) {
909 /* Solid Line */
910 rb->lcd_hline(XOFS,XOFS+BOARD_WIDTH-1,YOFS+cellypos[r]-1);
911 rb->lcd_vline(XOFS+cellxpos[r]-1,YOFS,YOFS+BOARD_HEIGHT-1);
912 } else {
913 /* Dotted line */
914 for (c=XOFS;c<XOFS+BOARD_WIDTH;c+=2) {
915 rb->lcd_drawpixel(c,YOFS+cellypos[r]-1);
917 for (c=YOFS;c<YOFS+BOARD_HEIGHT;c+=2) {
918 rb->lcd_drawpixel(XOFS+cellxpos[r]-1,c);
922 rb->lcd_hline(XOFS,XOFS+BOARD_WIDTH-1,YOFS+cellypos[8]+CELL_HEIGHT);
923 rb->lcd_vline(XOFS+cellxpos[8]+CELL_WIDTH,YOFS,YOFS+BOARD_HEIGHT-1);
924 #else
925 /* Large targets - draw single/double lines */
926 for (r=0;r<9;r++) {
927 rb->lcd_hline(XOFS,XOFS+BOARD_WIDTH-1,YOFS+cellypos[r]-1);
928 rb->lcd_vline(XOFS+cellxpos[r]-1,YOFS,YOFS+BOARD_HEIGHT-1);
929 if ((r % 3)==0) {
930 rb->lcd_hline(XOFS,XOFS+BOARD_WIDTH-1,YOFS+cellypos[r]-2);
931 rb->lcd_vline(XOFS+cellxpos[r]-2,YOFS,YOFS+BOARD_HEIGHT-1);
934 rb->lcd_hline(XOFS,XOFS+BOARD_WIDTH-1,YOFS+cellypos[8]+CELL_HEIGHT);
935 rb->lcd_hline(XOFS,XOFS+BOARD_WIDTH-1,YOFS+cellypos[8]+CELL_HEIGHT+1);
936 rb->lcd_vline(XOFS+cellxpos[8]+CELL_WIDTH,YOFS,YOFS+BOARD_HEIGHT-1);
937 rb->lcd_vline(XOFS+cellxpos[8]+CELL_WIDTH+1,YOFS,YOFS+BOARD_HEIGHT-1);
938 #endif
940 #ifdef SUDOKU_BUTTON_POSSIBLE
941 #ifdef VERTICAL_LAYOUT
942 rb->lcd_hline(XOFS,XOFS+BOARD_WIDTH-1,YOFSSCRATCHPAD);
943 rb->lcd_hline(XOFS,XOFS+BOARD_WIDTH-1,YOFSSCRATCHPAD+CELL_HEIGHT+1);
944 for (r=0;r<9;r++) {
945 #ifdef SMALL_BOARD
946 /* Small targets - draw dotted/single lines */
947 if ((r % 3)==0) {
948 /* Solid Line */
949 rb->lcd_vline(XOFS+cellxpos[r]-1,YOFSSCRATCHPAD,
950 YOFSSCRATCHPAD+CELL_HEIGHT+1);
951 } else {
952 /* Dotted line */
953 for (c=YOFSSCRATCHPAD;c<YOFSSCRATCHPAD+CELL_HEIGHT+1;c+=2) {
954 rb->lcd_drawpixel(XOFS+cellxpos[r]-1,c);
957 #else
958 /* Large targets - draw single/double lines */
959 rb->lcd_vline(XOFS+cellxpos[r]-1,YOFSSCRATCHPAD,
960 YOFSSCRATCHPAD+CELL_HEIGHT+1);
961 if ((r % 3)==0)
962 rb->lcd_vline(XOFS+cellxpos[r]-2,YOFSSCRATCHPAD,
963 YOFSSCRATCHPAD+CELL_HEIGHT+1);
964 #endif
965 if ((r>0) && state->possiblevals[state->y][state->x]&BIT_N(r))
966 rb->lcd_bitmap_part(sudoku_normal,NUMBER_TYPE,BITMAP_HEIGHT*r,
967 BITMAP_STRIDE,XOFS+cellxpos[r-1],
968 YOFSSCRATCHPAD+1,CELL_WIDTH,CELL_HEIGHT);
970 rb->lcd_vline(XOFS+cellxpos[8]+CELL_WIDTH,YOFSSCRATCHPAD,
971 YOFSSCRATCHPAD+CELL_HEIGHT+1);
972 #ifndef SMALL_BOARD
973 rb->lcd_vline(XOFS+cellxpos[8]+CELL_WIDTH+1,YOFSSCRATCHPAD,
974 YOFSSCRATCHPAD+CELL_HEIGHT+1);
975 #endif
976 if (state->possiblevals[state->y][state->x]&BIT_N(r))
977 rb->lcd_bitmap_part(sudoku_normal,NUMBER_TYPE,BITMAP_HEIGHT*r,
978 BITMAP_STRIDE,XOFS+cellxpos[8],YOFSSCRATCHPAD+1,
979 CELL_WIDTH,CELL_HEIGHT);
980 #else /* Horizontal layout */
981 rb->lcd_vline(XOFSSCRATCHPAD,YOFS,YOFS+BOARD_HEIGHT-1);
982 rb->lcd_vline(XOFSSCRATCHPAD+CELL_WIDTH+1,YOFS,YOFS+BOARD_HEIGHT-1);
983 for (r=0;r<9;r++) {
984 #ifdef SMALL_BOARD
985 /* Small targets - draw dotted/single lines */
986 if ((r % 3)==0) {
987 /* Solid Line */
988 rb->lcd_hline(XOFSSCRATCHPAD,XOFSSCRATCHPAD+CELL_WIDTH+1,
989 YOFS+cellypos[r]-1);
990 } else {
991 /* Dotted line */
992 for (c=XOFSSCRATCHPAD;c<XOFSSCRATCHPAD+CELL_WIDTH+1;c+=2) {
993 rb->lcd_drawpixel(c,YOFS+cellypos[r]-1);
996 #else
997 /* Large targets - draw single/double lines */
998 rb->lcd_hline(XOFSSCRATCHPAD,XOFSSCRATCHPAD+CELL_WIDTH+1,
999 YOFS+cellypos[r]-1);
1000 if ((r % 3)==0)
1001 rb->lcd_hline(XOFSSCRATCHPAD,XOFSSCRATCHPAD+CELL_WIDTH+1,
1002 YOFS+cellypos[r]-2);
1003 #endif
1004 if ((r>0) && state->possiblevals[state->y][state->x]&BIT_N(r))
1005 rb->lcd_bitmap_part(sudoku_normal,NUMBER_TYPE,BITMAP_HEIGHT*r,
1006 BITMAP_STRIDE,XOFSSCRATCHPAD+1,
1007 YOFS+cellypos[r-1],CELL_WIDTH,CELL_HEIGHT);
1009 rb->lcd_hline(XOFSSCRATCHPAD,XOFSSCRATCHPAD+CELL_WIDTH+1,
1010 YOFS+cellypos[8]+CELL_HEIGHT);
1011 #ifndef SMALL_BOARD
1012 rb->lcd_hline(XOFSSCRATCHPAD,XOFSSCRATCHPAD+CELL_WIDTH+1,
1013 YOFS+cellypos[8]+CELL_HEIGHT+1);
1014 #endif
1015 if (state->possiblevals[state->y][state->x]&BIT_N(r))
1016 rb->lcd_bitmap_part(sudoku_normal,NUMBER_TYPE,BITMAP_HEIGHT*r,
1017 BITMAP_STRIDE,XOFSSCRATCHPAD+1,YOFS+cellypos[8],
1018 CELL_WIDTH,CELL_HEIGHT);
1019 #endif /* Layout */
1020 #endif /* SUDOKU_BUTTON_POSSIBLE */
1022 /* Draw the numbers */
1023 for (r=0;r<9;r++) {
1024 for (c=0;c<9;c++) {
1025 /* We have four types of cell:
1026 1) User-entered number
1027 2) Starting number
1028 3) Cursor in cell
1031 if ((r==state->y) && (c==state->x)) {
1032 rb->lcd_bitmap_part(sudoku_inverse,NUMBER_TYPE,
1033 BITMAP_HEIGHT*(state->currentboard[r][c]-
1034 '0'),
1035 BITMAP_STRIDE,
1036 XOFS+cellxpos[c],YOFS+cellypos[r],
1037 CELL_WIDTH,CELL_HEIGHT);
1038 } else {
1039 if (state->startboard[r][c]!='0') {
1040 rb->lcd_bitmap_part(sudoku_start,NUMBER_TYPE,
1041 BITMAP_HEIGHT*(state->startboard[r][c]-
1042 '0'),
1043 BITMAP_STRIDE,
1044 XOFS+cellxpos[c],YOFS+cellypos[r],
1045 CELL_WIDTH,CELL_HEIGHT);
1046 } else {
1047 rb->lcd_bitmap_part(sudoku_normal,NUMBER_TYPE,
1048 BITMAP_HEIGHT*
1049 (state->currentboard[r][c]-'0'),
1050 BITMAP_STRIDE,
1051 XOFS+cellxpos[c],YOFS+cellypos[r],
1052 CELL_WIDTH,CELL_HEIGHT);
1055 #ifdef SUDOKU_BUTTON_POSSIBLE
1056 /* Draw the possible number markings on the board */
1057 if(sudcfg.show_markings && state->startboard[r][c]=='0'
1058 && state->currentboard[r][c]=='0') {
1059 for(i=0;i<9;i++) {
1060 if(state->possiblevals[r][c]&(2<<i)) {
1061 #if LCD_DEPTH > 1
1062 /* draw markings in dark grey */
1063 rb->lcd_set_foreground(LCD_DARKGRAY);
1064 #endif
1065 rb->lcd_fillrect(XOFS+cellxpos[c]+MARK_OFFS
1066 +(i%3)*(MARK_SIZE+MARK_SPACE),
1067 YOFS+cellypos[r]+MARK_OFFS
1068 +(i/3)*(MARK_SIZE+MARK_SPACE),
1069 MARK_SIZE,
1070 MARK_SIZE);
1071 #if LCD_DEPTH > 1
1072 rb->lcd_set_foreground(LCD_BLACK);
1073 #endif
1077 #endif /* SUDOKU_BUTTON_POSSIBLE */
1082 /* update the screen */
1083 rb->lcd_update();
1086 bool sudoku_generate(struct sudoku_state_t* state)
1088 char* difficulty;
1089 char str[80];
1090 bool res;
1091 struct sudoku_state_t new_state;
1093 clear_state(&new_state);
1094 display_board(&new_state);
1095 rb->splash(0, "Generating...");
1097 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1098 rb->cpu_boost(true);
1099 #endif
1101 res = sudoku_generate_board(&new_state,&difficulty);
1103 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1104 rb->cpu_boost(false);
1105 #endif
1107 if (res) {
1108 rb->memcpy(state,&new_state,sizeof(new_state));
1109 rb->snprintf(str,sizeof(str),"Difficulty: %s",difficulty);
1110 display_board(state);
1111 rb->splash(HZ*3, str);
1112 rb->strlcpy(state->filename,GAME_FILE,MAX_PATH);
1113 } else {
1114 display_board(&new_state);
1115 rb->splash(HZ*2, "Aborted");
1117 /* initialize the saved board so reload function works */
1118 save_state(state);
1119 return res;
1122 #ifdef HAVE_LCD_COLOR
1123 static bool numdisplay_setting(void)
1125 static const struct opt_items names[] = {
1126 {"Black", -1},
1127 {"Coloured", -1},
1130 return rb->set_option("Number Display", &sudcfg.number_display, INT, names,
1131 sizeof(names) / sizeof(names[0]), NULL);
1133 #endif
1135 #ifdef SUDOKU_BUTTON_POSSIBLE
1136 static bool showmarkings_setting(void)
1138 static const struct opt_items names[] = {
1139 {"Hide", -1},
1140 {"Show", -1},
1143 return rb->set_option("Show Markings", &sudcfg.show_markings, INT, names,
1144 sizeof(names) / sizeof(names[0]), NULL);
1146 #endif
1148 enum {
1149 SM_AUDIO_PLAYBACK = 0,
1150 #ifdef HAVE_LCD_COLOR
1151 SM_NUMBER_DISPLAY,
1152 #endif
1153 #ifdef SUDOKU_BUTTON_POSSIBLE
1154 SM_SHOW_MARKINGS,
1155 #endif
1156 SM_SAVE,
1157 SM_RELOAD,
1158 SM_CLEAR,
1159 SM_SOLVE,
1160 SM_GENERATE,
1161 SM_NEW,
1162 SM_QUIT,
1165 int sudoku_menu(struct sudoku_state_t* state)
1167 int result;
1169 MENUITEM_STRINGLIST(menu, "Sudoku Menu", NULL,
1170 "Audio Playback",
1171 #ifdef HAVE_LCD_COLOR
1172 "Number Display",
1173 #endif
1174 #ifdef SUDOKU_BUTTON_POSSIBLE
1175 "Show Markings",
1176 #endif
1177 "Save", "Reload", "Clear", "Solve",
1178 "Generate", "New", "Quit");
1180 result = rb->do_menu(&menu, NULL, NULL, false);
1182 switch (result) {
1183 case SM_AUDIO_PLAYBACK:
1184 playback_control(NULL);
1185 break;
1187 #ifdef HAVE_LCD_COLOR
1188 case SM_NUMBER_DISPLAY:
1189 numdisplay_setting();
1190 break;
1191 #endif
1193 #ifdef SUDOKU_BUTTON_POSSIBLE
1194 case SM_SHOW_MARKINGS:
1195 showmarkings_setting();
1196 break;
1197 #endif
1198 case SM_SAVE:
1199 save_sudoku(state);
1200 break;
1202 case SM_RELOAD:
1203 restore_state(state);
1204 break;
1206 case SM_CLEAR:
1207 clear_board(state);
1208 break;
1210 case SM_SOLVE:
1211 sudoku_solve(state);
1212 break;
1214 case SM_GENERATE:
1215 sudoku_generate(state);
1216 break;
1218 case SM_NEW:
1219 clear_state(state);
1220 state->editmode=1;
1221 break;
1223 case SM_QUIT:
1224 save_sudoku(state);
1225 break;
1227 default:
1228 break;
1231 return result;
1234 /* Menu used when user is in edit mode - i.e. creating a new game manually */
1235 int sudoku_edit_menu(struct sudoku_state_t* state)
1237 int result;
1239 MENUITEM_STRINGLIST(menu, "Edit Menu", NULL,
1240 "Save as", "Quit");
1242 result = rb->do_menu(&menu, NULL, NULL, false);
1244 switch (result) {
1245 case 0: /* Save new game */
1246 rb->kbd_input(state->filename,MAX_PATH);
1247 if (save_sudoku(state)) {
1248 state->editmode=0;
1249 } else {
1250 rb->splash(HZ*2, "Save failed");
1252 break;
1254 case 1: /* Quit */
1255 break;
1257 default:
1258 break;
1261 return result;
1264 void move_cursor(struct sudoku_state_t* state, int newx, int newy)
1266 int oldx, oldy;
1268 /* Check that the character at the cursor position is legal */
1269 if (check_status(state)) {
1270 rb->splash(HZ*2, "Illegal move!");
1271 /* Ignore any button presses during the splash */
1272 rb->button_clear_queue();
1273 return;
1276 /* Move Cursor */
1277 oldx=state->x;
1278 oldy=state->y;
1279 state->x=newx;
1280 state->y=newy;
1282 /* Redraw current and old cells */
1283 update_cell(state,oldx,oldy);
1284 update_cell(state,newx,newy);
1287 /* plugin entry point */
1288 enum plugin_status plugin_start(const void* parameter)
1290 bool exit;
1291 int button;
1292 int lastbutton = BUTTON_NONE;
1293 int res;
1294 int rc = PLUGIN_OK;
1295 long ticks;
1296 struct sudoku_state_t state;
1298 #if defined(HAVE_LCD_COLOR) || defined(SUDOKU_BUTTON_POSSIBLE)
1299 configfile_load(cfg_filename, disk_config,
1300 sizeof(disk_config) / sizeof(disk_config[0]),
1301 CFGFILE_MINVERSION);
1302 rb->memcpy(&sudcfg, &sudcfg_disk, sizeof(sudcfg)); /* copy to running config */
1303 #endif
1305 #if LCD_DEPTH > 1
1306 rb->lcd_set_backdrop(NULL);
1307 rb->lcd_set_foreground(LCD_BLACK);
1308 rb->lcd_set_background(LCD_WHITE);
1309 #endif
1311 clear_state(&state);
1313 if (parameter==NULL) {
1314 /* We have been started as a plugin - try default sudoku.ss */
1315 if (!load_sudoku(&state,GAME_FILE)) {
1316 /* No previous game saved, use the default */
1317 default_state(&state);
1319 } else {
1320 if (!load_sudoku(&state,(char*)parameter)) {
1321 rb->splash(HZ*2, "Load error");
1322 return(PLUGIN_ERROR);
1327 display_board(&state);
1329 /* The main game loop */
1330 exit=false;
1331 ticks=0;
1332 while(!exit) {
1333 button = rb->button_get(true);
1335 switch(button){
1336 #ifdef SUDOKU_BUTTON_QUIT
1337 /* Exit game */
1338 case SUDOKU_BUTTON_QUIT:
1339 if (check_status(&state)) {
1340 rb->splash(HZ*2, "Illegal move!");
1341 /* Ignore any button presses during the splash */
1342 rb->button_clear_queue();
1343 } else {
1344 save_sudoku(&state);
1345 exit=true;
1347 break;
1348 #endif
1350 /* Increment digit */
1351 #ifdef SUDOKU_BUTTON_ALTTOGGLE
1352 case SUDOKU_BUTTON_ALTTOGGLE | BUTTON_REPEAT:
1353 #endif
1354 case SUDOKU_BUTTON_TOGGLE | BUTTON_REPEAT:
1355 /* Slow down the repeat speed to 1/3 second */
1356 if ((*rb->current_tick-ticks) < (HZ/3)) {
1357 break;
1360 #ifdef SUDOKU_BUTTON_ALTTOGGLE
1361 case SUDOKU_BUTTON_ALTTOGGLE:
1362 #endif
1363 case SUDOKU_BUTTON_TOGGLE:
1364 #ifdef SUDOKU_BUTTON_TOGGLE_PRE
1365 if ((button == SUDOKU_BUTTON_TOGGLE)
1366 && (lastbutton != SUDOKU_BUTTON_TOGGLE_PRE))
1367 break;
1368 #endif
1369 /* Increment digit */
1370 ticks=*rb->current_tick;
1371 if (state.editmode) {
1372 if (state.startboard[state.y][state.x]=='9') {
1373 state.startboard[state.y][state.x]='0';
1374 state.currentboard[state.y][state.x]='0';
1375 } else {
1376 state.startboard[state.y][state.x]++;
1377 state.currentboard[state.y][state.x]++;
1379 } else {
1380 if (state.startboard[state.y][state.x]=='0') {
1381 if (state.currentboard[state.y][state.x]=='9') {
1382 state.currentboard[state.y][state.x]='0';
1383 } else {
1384 state.currentboard[state.y][state.x]++;
1388 update_cell(&state,state.y,state.x);
1389 break;
1391 #ifdef SUDOKU_BUTTON_TOGGLEBACK
1392 case SUDOKU_BUTTON_TOGGLEBACK | BUTTON_REPEAT:
1393 /* Slow down the repeat speed to 1/3 second */
1394 if ((*rb->current_tick-ticks) < (HZ/3)) {
1395 break;
1398 case SUDOKU_BUTTON_TOGGLEBACK:
1399 /* Decrement digit */
1400 ticks=*rb->current_tick;
1401 if (state.editmode) {
1402 if (state.startboard[state.y][state.x]=='0') {
1403 state.startboard[state.y][state.x]='9';
1404 state.currentboard[state.y][state.x]='9';
1405 } else {
1406 state.startboard[state.y][state.x]--;
1407 state.currentboard[state.y][state.x]--;
1409 } else {
1410 if (state.startboard[state.y][state.x]=='0') {
1411 if (state.currentboard[state.y][state.x]=='0') {
1412 state.currentboard[state.y][state.x]='9';
1413 } else {
1414 state.currentboard[state.y][state.x]--;
1418 update_cell(&state,state.y,state.x);
1419 break;
1420 #endif
1422 /* move cursor left */
1423 case SUDOKU_BUTTON_LEFT:
1424 case (SUDOKU_BUTTON_LEFT | BUTTON_REPEAT):
1425 if ( (state.x==0&&invertdir==0) || (state.y==0&&invertdir==1) ) {
1426 #ifndef SUDOKU_BUTTON_UP
1427 if ( (state.y==0&&invertdir==0) || (state.x==0&&invertdir==1)) {
1428 move_cursor(&state,8,8);
1429 } else {
1430 if (invertdir==0) {
1431 move_cursor(&state,8,state.y-1);
1432 } else {
1433 move_cursor(&state,state.x-1,8);
1436 #else
1437 move_cursor(&state,8,state.y);
1438 #endif
1439 } else {
1440 if (invertdir==0) {
1441 move_cursor(&state,state.x-1,state.y);
1442 } else {
1443 move_cursor(&state,state.x,state.y-1);
1446 break;
1448 /* move cursor right */
1449 case SUDOKU_BUTTON_RIGHT:
1450 case (SUDOKU_BUTTON_RIGHT | BUTTON_REPEAT):
1451 if ( (state.x==8&&invertdir==0) || (state.y==8&&invertdir==1) ) {
1452 #ifndef SUDOKU_BUTTON_DOWN
1453 if ( (state.y==8&&invertdir==0) || (state.x==8&&invertdir==1) ) {
1454 move_cursor(&state,0,0);
1455 } else {
1456 if (invertdir==0) {
1457 move_cursor(&state,0,state.y+1);
1458 } else {
1459 move_cursor(&state,state.x+1,0);
1462 #else
1463 move_cursor(&state,0,state.y);
1464 #endif
1465 } else {
1466 if (invertdir==0) {
1467 move_cursor(&state,state.x+1,state.y);
1468 } else {
1469 move_cursor(&state,state.x,state.y+1);
1472 break;
1474 #ifdef SUDOKU_BUTTON_UP
1475 /* move cursor up */
1476 case SUDOKU_BUTTON_UP:
1477 case (SUDOKU_BUTTON_UP | BUTTON_REPEAT):
1478 if (state.y==0) {
1479 move_cursor(&state,state.x,8);
1480 } else {
1481 move_cursor(&state,state.x,state.y-1);
1483 break;
1484 #endif
1486 #ifdef SUDOKU_BUTTON_DOWN
1487 /* move cursor down */
1488 case SUDOKU_BUTTON_DOWN:
1489 case (SUDOKU_BUTTON_DOWN | BUTTON_REPEAT):
1490 if (state.y==8) {
1491 move_cursor(&state,state.x,0);
1492 } else {
1493 move_cursor(&state,state.x,state.y+1);
1495 break;
1496 #endif
1498 case SUDOKU_BUTTON_MENU:
1499 #ifdef SUDOKU_BUTTON_MENU_PRE
1500 if (lastbutton != SUDOKU_BUTTON_MENU_PRE)
1501 break;
1502 #endif
1503 /* Don't let the user leave a game in a bad state */
1504 if (check_status(&state)) {
1505 rb->splash(HZ*2, "Illegal move!");
1506 /* Ignore any button presses during the splash */
1507 rb->button_clear_queue();
1508 } else {
1509 if (state.editmode) {
1510 res = sudoku_edit_menu(&state);
1511 if (res == MENU_ATTACHED_USB) {
1512 rc = PLUGIN_USB_CONNECTED;
1513 exit = true;
1514 } else if (res == 1) { /* Quit */
1515 exit = true;
1517 } else {
1518 res = sudoku_menu(&state);
1519 if (res == MENU_ATTACHED_USB) {
1520 rc = PLUGIN_USB_CONNECTED;
1521 exit = true;
1522 } else if (res == SM_QUIT) {
1523 exit = true;
1527 break;
1528 #ifdef SUDOKU_BUTTON_POSSIBLE
1529 case SUDOKU_BUTTON_POSSIBLE:
1530 /* Toggle current number in the possiblevals structure */
1531 if (state.currentboard[state.y][state.x]!='0') {
1532 state.possiblevals[state.y][state.x]^=
1533 BIT_N(state.currentboard[state.y][state.x] - '0');
1535 break;
1536 #endif
1538 #ifdef SUDOKU_BUTTON_CHANGEDIR
1539 case SUDOKU_BUTTON_CHANGEDIR:
1540 /* Change scroll wheel direction */
1541 invertdir=!invertdir;
1542 break;
1543 #endif
1544 default:
1545 if (rb->default_event_handler(button) == SYS_USB_CONNECTED) {
1546 /* Quit if USB has been connected */
1547 rc = PLUGIN_USB_CONNECTED;
1548 exit = true;
1550 break;
1552 if (button != BUTTON_NONE)
1553 lastbutton = button;
1555 display_board(&state);
1557 #if defined(HAVE_LCD_COLOR) || defined(SUDOKU_BUTTON_POSSIBLE)
1558 if (rb->memcmp(&sudcfg, &sudcfg_disk, sizeof(sudcfg))) /* save settings if changed */
1560 rb->memcpy(&sudcfg_disk, &sudcfg, sizeof(sudcfg));
1561 configfile_save(cfg_filename, disk_config,
1562 sizeof(disk_config) / sizeof(disk_config[0]),
1563 CFGFILE_VERSION);
1565 #endif
1566 return rc;
1569 #endif