summaryrefslogtreecommitdiff
path: root/apps/plugins/sudoku
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/sudoku')
-rw-r--r--apps/plugins/sudoku/generator.c28
-rw-r--r--apps/plugins/sudoku/sudoku.c254
2 files changed, 203 insertions, 79 deletions
diff --git a/apps/plugins/sudoku/generator.c b/apps/plugins/sudoku/generator.c
index 037798d761..eaef8c0f17 100644
--- a/apps/plugins/sudoku/generator.c
+++ b/apps/plugins/sudoku/generator.c
@@ -1056,9 +1056,14 @@ select_template( void )
1056 init_template( i ); 1056 init_template( i );
1057} 1057}
1058 1058
1059static bool check_buttons(void)
1060{
1061 int button = rb->button_get(false);
1062 return (button && (!(button & BUTTON_REL)) && (!(button & BUTTON_REPEAT)));
1063}
1059 1064
1060static 1065static
1061void 1066bool
1062generate( void ) 1067generate( void )
1063{ 1068{
1064 static int digits[ 9 ]; 1069 static int digits[ 9 ];
@@ -1066,6 +1071,10 @@ generate( void )
1066 int i; 1071 int i;
1067 1072
1068start: 1073start:
1074 /* Allow the user to abort generation by pressing any button */
1075 if (check_buttons())
1076 return false;
1077
1069 for( i = 0 ; i < 9 ; ++i ) 1078 for( i = 0 ; i < 9 ; ++i )
1070 digits[ i ] = i + 1; 1079 digits[ i ] = i + 1;
1071 1080
@@ -1081,11 +1090,19 @@ start:
1081 for( i = 0 ; i < len_tmplt ; ++i ) 1090 for( i = 0 ; i < len_tmplt ; ++i )
1082 fill( tmplt[ i ], digits[ i % 9 ] ); 1091 fill( tmplt[ i ], digits[ i % 9 ] );
1083 1092
1093 /* Allow the user to abort generation by pressing any button */
1094 if (check_buttons())
1095 return false;
1096
1084 rb->yield(); 1097 rb->yield();
1085 1098
1086 if( 0 != solve( ) || idx_history < 81 ) 1099 if( 0 != solve( ) || idx_history < 81 )
1087 goto start; 1100 goto start;
1088 1101
1102 /* Allow the user to abort generation by pressing any button */
1103 if (check_buttons())
1104 return false;
1105
1089 rb->yield(); 1106 rb->yield();
1090 1107
1091 for( i = 0 ; i < len_tmplt ; ++i ) 1108 for( i = 0 ; i < len_tmplt ; ++i )
@@ -1105,6 +1122,8 @@ start:
1105 goto start; 1122 goto start;
1106 1123
1107 clear_moves( ); 1124 clear_moves( );
1125
1126 return true;
1108} 1127}
1109 1128
1110bool sudoku_generate_board(struct sudoku_state_t* state, char** difficulty) 1129bool sudoku_generate_board(struct sudoku_state_t* state, char** difficulty)
@@ -1113,7 +1132,12 @@ bool sudoku_generate_board(struct sudoku_state_t* state, char** difficulty)
1113 1132
1114 rb->srand(*rb->current_tick); 1133 rb->srand(*rb->current_tick);
1115 1134
1116 generate(); 1135 rb->button_clear_queue();
1136
1137 if (!generate()) {
1138 /* User has aborted with a button press */
1139 return false;
1140 }
1117 1141
1118 i=0; 1142 i=0;
1119 for (r=0;r<9;r++) { 1143 for (r=0;r<9;r++) {
diff --git a/apps/plugins/sudoku/sudoku.c b/apps/plugins/sudoku/sudoku.c
index 4be10cc6c4..0c39c463ef 100644
--- a/apps/plugins/sudoku/sudoku.c
+++ b/apps/plugins/sudoku/sudoku.c
@@ -77,6 +77,22 @@ extern const fb_data sudoku_normal[];
77extern const fb_data sudoku_start[]; 77extern const fb_data sudoku_start[];
78extern const fb_data sudoku_inverse[]; 78extern const fb_data sudoku_inverse[];
79 79
80/* Default game - used to initialise sudoku.ss if it doesn't exist. */
81static const char default_game[9][9] =
82{
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' },
86
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' },
90
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' },
94};
95
80#if ((LCD_HEIGHT==128) && (LCD_WIDTH==160)) || \ 96#if ((LCD_HEIGHT==128) && (LCD_WIDTH==160)) || \
81 ((LCD_HEIGHT==132) && (LCD_WIDTH==176)) 97 ((LCD_HEIGHT==132) && (LCD_WIDTH==176))
82/* For iriver H1x0 - 160x128, 9 cells @ 12x12 with 14 border lines*/ 98/* For iriver H1x0 - 160x128, 9 cells @ 12x12 with 14 border lines*/
@@ -440,12 +456,31 @@ void sudoku_solve(struct sudoku_state_t* state)
440 return; 456 return;
441} 457}
442 458
459void default_state(struct sudoku_state_t* state)
460{
461 int r,c;
462
463 rb->strncpy(state->filename,GAME_FILE,MAX_PATH);
464 for (r=0;r<9;r++) {
465 for (c=0;c<9;c++) {
466 state->startboard[r][c]=default_game[r][c];
467 state->currentboard[r][c]=default_game[r][c];
468#ifdef SUDOKU_BUTTON_POSSIBLE
469 state->possiblevals[r][c]=0;
470#endif
471 }
472 }
473
474 state->x=0;
475 state->y=0;
476 state->editmode=0;
477}
443 478
444void clear_state(struct sudoku_state_t* state) 479void clear_state(struct sudoku_state_t* state)
445{ 480{
446 int r,c; 481 int r,c;
447 482
448 state->filename[0]=0; 483 rb->strncpy(state->filename,GAME_FILE,MAX_PATH);
449 for (r=0;r<9;r++) { 484 for (r=0;r<9;r++) {
450 for (c=0;c<9;c++) { 485 for (c=0;c<9;c++) {
451 state->startboard[r][c]='0'; 486 state->startboard[r][c]='0';
@@ -461,6 +496,65 @@ void clear_state(struct sudoku_state_t* state)
461 state->editmode=0; 496 state->editmode=0;
462} 497}
463 498
499/* Check the status of the board, assuming a change at the cursor location */
500bool check_status(struct sudoku_state_t* state)
501{
502 int check[9];
503 int r,c;
504 int r1,c1;
505 int cell;
506
507 /* First, check the column */
508 for (cell=0;cell<9;cell++) {
509 check[cell]=0;
510 }
511 for (r=0;r<9;r++) {
512 cell=state->currentboard[r][state->x];
513 if (cell!='0') {
514 if (check[cell-'1']==1) {
515 return true;
516 }
517 check[cell-'1']=1;
518 }
519 }
520
521 /* Second, check the row */
522 for (cell=0;cell<9;cell++) {
523 check[cell]=0;
524 }
525 for (c=0;c<9;c++) {
526 cell=state->currentboard[state->y][c];
527 if (cell!='0') {
528 if (check[cell-'1']==1) {
529 return true;
530 }
531 check[cell-'1']=1;
532 }
533 }
534
535 /* Finally, check the 3x3 sub-grid */
536 for (cell=0;cell<9;cell++) {
537 check[cell]=0;
538 }
539 r1=(state->y/3)*3;
540 c1=(state->x/3)*3;
541 for (r=r1;r<r1+3;r++) {
542 for (c=c1;c<c1+3;c++) {
543 cell=state->currentboard[r][c];
544 if (cell!='0') {
545 if (check[cell-'1']==1) {
546 return true;
547 }
548 check[cell-'1']=1;
549 }
550 }
551 }
552
553 /* We passed all the checks :) */
554
555 return false;
556}
557
464/* Load game - only ".ss" is officially supported, but any sensible 558/* Load game - only ".ss" is officially supported, but any sensible
465 text representation (one line per row) may load. 559 text representation (one line per row) may load.
466*/ 560*/
@@ -539,6 +633,15 @@ bool load_sudoku(struct sudoku_state_t* state, char* filename)
539 i++; 633 i++;
540 } 634 }
541 635
636 /* Check that the board is valid - we need to check every row/column
637 individually, so we check the diagonal from top-left to bottom-right */
638 for (state->x = 0; state->x < 9; state->x++) {
639 state->y = state->x;
640 if (check_status(state)) return false;
641 }
642 state->x = 0;
643 state->y = 0;
644
542 /* Save a copy of the saved state - so we can reload without using the 645 /* Save a copy of the saved state - so we can reload without using the
543 disk */ 646 disk */
544 rb->memcpy(state->savedboard,state->currentboard,81); 647 rb->memcpy(state->savedboard,state->currentboard,81);
@@ -553,6 +656,7 @@ bool save_sudoku(struct sudoku_state_t* state)
553 char line[13]; 656 char line[13];
554 char sep[13]; 657 char sep[13];
555 658
659 rb->splash(0, true, "Saving...");
556 rb->memcpy(line,"...|...|...\r\n",13); 660 rb->memcpy(line,"...|...|...\r\n",13);
557 rb->memcpy(sep,"-----------\r\n",13); 661 rb->memcpy(sep,"-----------\r\n",13);
558 662
@@ -773,88 +877,38 @@ void display_board(struct sudoku_state_t* state)
773 rb->lcd_update(); 877 rb->lcd_update();
774} 878}
775 879
776/* Check the status of the board, assuming a change at the cursor location */ 880bool sudoku_generate(struct sudoku_state_t* state)
777bool check_status(struct sudoku_state_t* state)
778{
779 int check[9];
780 int r,c;
781 int r1,c1;
782 int cell;
783
784 /* First, check the column */
785 for (cell=0;cell<9;cell++) {
786 check[cell]=0;
787 }
788 for (r=0;r<9;r++) {
789 cell=state->currentboard[r][state->x];
790 if (cell!='0') {
791 if (check[cell-'1']==1) {
792 return true;
793 }
794 check[cell-'1']=1;
795 }
796 }
797
798 /* Second, check the row */
799 for (cell=0;cell<9;cell++) {
800 check[cell]=0;
801 }
802 for (c=0;c<9;c++) {
803 cell=state->currentboard[state->y][c];
804 if (cell!='0') {
805 if (check[cell-'1']==1) {
806 return true;
807 }
808 check[cell-'1']=1;
809 }
810 }
811
812 /* Finally, check the 3x3 sub-grid */
813 for (cell=0;cell<9;cell++) {
814 check[cell]=0;
815 }
816 r1=(state->y/3)*3;
817 c1=(state->x/3)*3;
818 for (r=r1;r<r1+3;r++) {
819 for (c=c1;c<c1+3;c++) {
820 cell=state->currentboard[r][c];
821 if (cell!='0') {
822 if (check[cell-'1']==1) {
823 return true;
824 }
825 check[cell-'1']=1;
826 }
827 }
828 }
829
830 /* We passed all the checks :) */
831
832 return false;
833}
834
835void sudoku_generate(struct sudoku_state_t* state)
836{ 881{
837 char* difficulty; 882 char* difficulty;
838 char str[80]; 883 char str[80];
884 bool res;
885 struct sudoku_state_t new_state;
839 886
840 clear_state(state); 887 clear_state(&new_state);
841 display_board(state); 888 display_board(&new_state);
842 rb->splash(0, true, "Generating..."); 889 rb->splash(0, true, "Generating...");
843 890
844#ifdef HAVE_ADJUSTABLE_CPU_FREQ 891#ifdef HAVE_ADJUSTABLE_CPU_FREQ
845 rb->cpu_boost(true); 892 rb->cpu_boost(true);
846#endif 893#endif
847 894
848 sudoku_generate_board(state,&difficulty); 895 res = sudoku_generate_board(&new_state,&difficulty);
849 896
850#ifdef HAVE_ADJUSTABLE_CPU_FREQ 897#ifdef HAVE_ADJUSTABLE_CPU_FREQ
851 rb->cpu_boost(false); 898 rb->cpu_boost(false);
852#endif 899#endif
853 900
854 rb->snprintf(str,sizeof(str),"Difficulty: %s",difficulty); 901 if (res) {
855 display_board(state); 902 rb->memcpy(state,&new_state,sizeof(new_state));
856 rb->splash(3*HZ, true, str); 903 rb->snprintf(str,sizeof(str),"Difficulty: %s",difficulty);
857 rb->strncpy(state->filename,GAME_FILE,MAX_PATH); 904 display_board(state);
905 rb->splash(3*HZ, true, str);
906 rb->strncpy(state->filename,GAME_FILE,MAX_PATH);
907 } else {
908 display_board(&new_state);
909 rb->splash(2*HZ, true, "Aborted");
910 }
911 return res;
858} 912}
859 913
860int sudoku_menu_cb(int key, int m) 914int sudoku_menu_cb(int key, int m)
@@ -946,6 +1000,44 @@ bool sudoku_menu(struct sudoku_state_t* state)
946 return (result==MENU_ATTACHED_USB); 1000 return (result==MENU_ATTACHED_USB);
947} 1001}
948 1002
1003/* Menu used when user is in edit mode - i.e. creating a new game manually */
1004int sudoku_edit_menu(struct sudoku_state_t* state)
1005{
1006 int m;
1007 int result;
1008
1009 static const struct menu_item items[] = {
1010 { "Save as", NULL },
1011 { "Quit", NULL },
1012 };
1013
1014 m = rb->menu_init(items, sizeof(items) / sizeof(*items),
1015 sudoku_menu_cb, NULL, NULL, NULL);
1016
1017 result=rb->menu_show(m);
1018
1019 switch (result) {
1020 case 0: /* Save new game */
1021 rb->kbd_input(state->filename,MAX_PATH);
1022 if (save_sudoku(state)) {
1023 state->editmode=0;
1024 } else {
1025 rb->splash(HZ*2, true, "Save failed");
1026 }
1027 break;
1028
1029 case 1: /* Quit */
1030 break;
1031
1032 default:
1033 break;
1034 }
1035
1036 rb->menu_exit(m);
1037
1038 return result;
1039}
1040
949void move_cursor(struct sudoku_state_t* state, int newx, int newy) 1041void move_cursor(struct sudoku_state_t* state, int newx, int newy)
950{ 1042{
951 int oldx, oldy; 1043 int oldx, oldy;
@@ -975,6 +1067,7 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
975 bool exit; 1067 bool exit;
976 int button; 1068 int button;
977 int lastbutton = BUTTON_NONE; 1069 int lastbutton = BUTTON_NONE;
1070 int res;
978 long ticks; 1071 long ticks;
979 struct sudoku_state_t state; 1072 struct sudoku_state_t state;
980 1073
@@ -987,8 +1080,8 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
987 if (parameter==NULL) { 1080 if (parameter==NULL) {
988 /* We have been started as a plugin - try default sudoku.ss */ 1081 /* We have been started as a plugin - try default sudoku.ss */
989 if (!load_sudoku(&state,GAME_FILE)) { 1082 if (!load_sudoku(&state,GAME_FILE)) {
990 /* No previous game saved, generate one */ 1083 /* No previous game saved, use the default */
991 sudoku_generate(&state); 1084 default_state(&state);
992 } 1085 }
993 } else { 1086 } else {
994 if (!load_sudoku(&state,(char*)parameter)) { 1087 if (!load_sudoku(&state,(char*)parameter)) {
@@ -1009,7 +1102,14 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
1009#ifdef SUDOKU_BUTTON_QUIT 1102#ifdef SUDOKU_BUTTON_QUIT
1010 /* Exit game */ 1103 /* Exit game */
1011 case SUDOKU_BUTTON_QUIT: 1104 case SUDOKU_BUTTON_QUIT:
1012 exit=1; 1105 if (check_status(&state)) {
1106 rb->splash(HZ*2, true, "Illegal move!");
1107 /* Ignore any button presses during the splash */
1108 rb->button_clear_queue();
1109 } else {
1110 save_sudoku(&state);
1111 exit=1;
1112 }
1013 break; 1113 break;
1014#endif 1114#endif
1015 1115
@@ -1047,7 +1147,7 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
1047 if (state.currentboard[state.y][state.x]=='9') { 1147 if (state.currentboard[state.y][state.x]=='9') {
1048 state.currentboard[state.y][state.x]='0'; 1148 state.currentboard[state.y][state.x]='0';
1049 } else { 1149 } else {
1050 state.currentboard[state.y][state.x]++; 1150 state.currentboard[state.y][state.x]++;
1051 } 1151 }
1052 } 1152 }
1053 } 1153 }
@@ -1157,11 +1257,11 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
1157 rb->button_clear_queue(); 1257 rb->button_clear_queue();
1158 } else { 1258 } else {
1159 if (state.editmode) { 1259 if (state.editmode) {
1160 rb->kbd_input(state.filename,MAX_PATH); 1260 res = sudoku_edit_menu(&state);
1161 if (save_sudoku(&state)) { 1261 if (res == MENU_ATTACHED_USB) {
1162 state.editmode=0; 1262 return PLUGIN_USB_CONNECTED;
1163 } else { 1263 } else if (res == 1) { /* Quit */
1164 rb->splash(HZ*2, true, "Save failed"); 1264 return PLUGIN_OK;
1165 } 1265 }
1166 } else { 1266 } else {
1167 if (sudoku_menu(&state)) { 1267 if (sudoku_menu(&state)) {