diff options
Diffstat (limited to 'apps/plugins/sudoku/sudoku.c')
-rw-r--r-- | apps/plugins/sudoku/sudoku.c | 254 |
1 files changed, 177 insertions, 77 deletions
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[]; | |||
77 | extern const fb_data sudoku_start[]; | 77 | extern const fb_data sudoku_start[]; |
78 | extern const fb_data sudoku_inverse[]; | 78 | extern const fb_data sudoku_inverse[]; |
79 | 79 | ||
80 | /* Default game - used to initialise sudoku.ss if it doesn't exist. */ | ||
81 | static 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 | ||
459 | void 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 | ||
444 | void clear_state(struct sudoku_state_t* state) | 479 | void 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 */ | ||
500 | bool 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 */ | 880 | bool sudoku_generate(struct sudoku_state_t* state) |
777 | bool 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 | |||
835 | void 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 | ||
860 | int sudoku_menu_cb(int key, int m) | 914 | int 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 */ | ||
1004 | int 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 | |||
949 | void move_cursor(struct sudoku_state_t* state, int newx, int newy) | 1041 | void 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)) { |