1
0
Fork 0
forked from len0rd/rockbox

Various clean-ups in preparation for 3.0: 1) Add a built-in default game which is used for the case when Sudoku is started as a plugin and no sudoku.ss exists - this prevents a time-consuming game generation; 2) Allow the user to abort a game generation by pressing any button; 3) Save the current state when user exits via the Quit button (previously, Sudoku only saved when quitting from the main menu); 4) Add a new "edit menu" for when the user is manually entering a new game, rather than displaying all options.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9944 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Dave Chapman 2006-05-15 20:08:37 +00:00
parent 254751f9fc
commit 506bac9cf7
2 changed files with 203 additions and 79 deletions

View file

@ -1056,9 +1056,14 @@ select_template( void )
init_template( i ); init_template( i );
} }
static bool check_buttons(void)
{
int button = rb->button_get(false);
return (button && (!(button & BUTTON_REL)) && (!(button & BUTTON_REPEAT)));
}
static static
void bool
generate( void ) generate( void )
{ {
static int digits[ 9 ]; static int digits[ 9 ];
@ -1066,6 +1071,10 @@ generate( void )
int i; int i;
start: start:
/* Allow the user to abort generation by pressing any button */
if (check_buttons())
return false;
for( i = 0 ; i < 9 ; ++i ) for( i = 0 ; i < 9 ; ++i )
digits[ i ] = i + 1; digits[ i ] = i + 1;
@ -1081,11 +1090,19 @@ start:
for( i = 0 ; i < len_tmplt ; ++i ) for( i = 0 ; i < len_tmplt ; ++i )
fill( tmplt[ i ], digits[ i % 9 ] ); fill( tmplt[ i ], digits[ i % 9 ] );
/* Allow the user to abort generation by pressing any button */
if (check_buttons())
return false;
rb->yield(); rb->yield();
if( 0 != solve( ) || idx_history < 81 ) if( 0 != solve( ) || idx_history < 81 )
goto start; goto start;
/* Allow the user to abort generation by pressing any button */
if (check_buttons())
return false;
rb->yield(); rb->yield();
for( i = 0 ; i < len_tmplt ; ++i ) for( i = 0 ; i < len_tmplt ; ++i )
@ -1105,6 +1122,8 @@ start:
goto start; goto start;
clear_moves( ); clear_moves( );
return true;
} }
bool sudoku_generate_board(struct sudoku_state_t* state, char** difficulty) bool 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)
rb->srand(*rb->current_tick); rb->srand(*rb->current_tick);
generate(); rb->button_clear_queue();
if (!generate()) {
/* User has aborted with a button press */
return false;
}
i=0; i=0;
for (r=0;r<9;r++) { for (r=0;r<9;r++) {

View file

@ -77,6 +77,22 @@ extern const fb_data sudoku_normal[];
extern const fb_data sudoku_start[]; extern const fb_data sudoku_start[];
extern const fb_data sudoku_inverse[]; extern const fb_data sudoku_inverse[];
/* Default game - used to initialise sudoku.ss if it doesn't exist. */
static const char default_game[9][9] =
{
{ '0','1','0', '3','0','7', '0','0','4' },
{ '0','0','0', '0','6','0', '1','0','2' },
{ '0','0','0', '0','8','0', '5','6','0' },
{ '0','6','0', '0','0','0', '0','2','9' },
{ '0','0','0', '5','0','3', '0','0','0' },
{ '7','9','0', '0','0','0', '0','3','0' },
{ '0','8','5', '0','3','0', '0','0','0' },
{ '1','0','2', '0','7','0', '0','0','0' },
{ '0','0','0', '4','0','8', '0','5','0' },
};
#if ((LCD_HEIGHT==128) && (LCD_WIDTH==160)) || \ #if ((LCD_HEIGHT==128) && (LCD_WIDTH==160)) || \
((LCD_HEIGHT==132) && (LCD_WIDTH==176)) ((LCD_HEIGHT==132) && (LCD_WIDTH==176))
/* For iriver H1x0 - 160x128, 9 cells @ 12x12 with 14 border lines*/ /* For iriver H1x0 - 160x128, 9 cells @ 12x12 with 14 border lines*/
@ -440,12 +456,31 @@ void sudoku_solve(struct sudoku_state_t* state)
return; return;
} }
void default_state(struct sudoku_state_t* state)
{
int r,c;
rb->strncpy(state->filename,GAME_FILE,MAX_PATH);
for (r=0;r<9;r++) {
for (c=0;c<9;c++) {
state->startboard[r][c]=default_game[r][c];
state->currentboard[r][c]=default_game[r][c];
#ifdef SUDOKU_BUTTON_POSSIBLE
state->possiblevals[r][c]=0;
#endif
}
}
state->x=0;
state->y=0;
state->editmode=0;
}
void clear_state(struct sudoku_state_t* state) void clear_state(struct sudoku_state_t* state)
{ {
int r,c; int r,c;
state->filename[0]=0; rb->strncpy(state->filename,GAME_FILE,MAX_PATH);
for (r=0;r<9;r++) { for (r=0;r<9;r++) {
for (c=0;c<9;c++) { for (c=0;c<9;c++) {
state->startboard[r][c]='0'; state->startboard[r][c]='0';
@ -461,6 +496,65 @@ void clear_state(struct sudoku_state_t* state)
state->editmode=0; state->editmode=0;
} }
/* Check the status of the board, assuming a change at the cursor location */
bool check_status(struct sudoku_state_t* state)
{
int check[9];
int r,c;
int r1,c1;
int cell;
/* First, check the column */
for (cell=0;cell<9;cell++) {
check[cell]=0;
}
for (r=0;r<9;r++) {
cell=state->currentboard[r][state->x];
if (cell!='0') {
if (check[cell-'1']==1) {
return true;
}
check[cell-'1']=1;
}
}
/* Second, check the row */
for (cell=0;cell<9;cell++) {
check[cell]=0;
}
for (c=0;c<9;c++) {
cell=state->currentboard[state->y][c];
if (cell!='0') {
if (check[cell-'1']==1) {
return true;
}
check[cell-'1']=1;
}
}
/* Finally, check the 3x3 sub-grid */
for (cell=0;cell<9;cell++) {
check[cell]=0;
}
r1=(state->y/3)*3;
c1=(state->x/3)*3;
for (r=r1;r<r1+3;r++) {
for (c=c1;c<c1+3;c++) {
cell=state->currentboard[r][c];
if (cell!='0') {
if (check[cell-'1']==1) {
return true;
}
check[cell-'1']=1;
}
}
}
/* We passed all the checks :) */
return false;
}
/* Load game - only ".ss" is officially supported, but any sensible /* Load game - only ".ss" is officially supported, but any sensible
text representation (one line per row) may load. text representation (one line per row) may load.
*/ */
@ -539,6 +633,15 @@ bool load_sudoku(struct sudoku_state_t* state, char* filename)
i++; i++;
} }
/* Check that the board is valid - we need to check every row/column
individually, so we check the diagonal from top-left to bottom-right */
for (state->x = 0; state->x < 9; state->x++) {
state->y = state->x;
if (check_status(state)) return false;
}
state->x = 0;
state->y = 0;
/* Save a copy of the saved state - so we can reload without using the /* Save a copy of the saved state - so we can reload without using the
disk */ disk */
rb->memcpy(state->savedboard,state->currentboard,81); rb->memcpy(state->savedboard,state->currentboard,81);
@ -553,6 +656,7 @@ bool save_sudoku(struct sudoku_state_t* state)
char line[13]; char line[13];
char sep[13]; char sep[13];
rb->splash(0, true, "Saving...");
rb->memcpy(line,"...|...|...\r\n",13); rb->memcpy(line,"...|...|...\r\n",13);
rb->memcpy(sep,"-----------\r\n",13); rb->memcpy(sep,"-----------\r\n",13);
@ -773,88 +877,38 @@ void display_board(struct sudoku_state_t* state)
rb->lcd_update(); rb->lcd_update();
} }
/* Check the status of the board, assuming a change at the cursor location */ bool sudoku_generate(struct sudoku_state_t* state)
bool check_status(struct sudoku_state_t* state)
{
int check[9];
int r,c;
int r1,c1;
int cell;
/* First, check the column */
for (cell=0;cell<9;cell++) {
check[cell]=0;
}
for (r=0;r<9;r++) {
cell=state->currentboard[r][state->x];
if (cell!='0') {
if (check[cell-'1']==1) {
return true;
}
check[cell-'1']=1;
}
}
/* Second, check the row */
for (cell=0;cell<9;cell++) {
check[cell]=0;
}
for (c=0;c<9;c++) {
cell=state->currentboard[state->y][c];
if (cell!='0') {
if (check[cell-'1']==1) {
return true;
}
check[cell-'1']=1;
}
}
/* Finally, check the 3x3 sub-grid */
for (cell=0;cell<9;cell++) {
check[cell]=0;
}
r1=(state->y/3)*3;
c1=(state->x/3)*3;
for (r=r1;r<r1+3;r++) {
for (c=c1;c<c1+3;c++) {
cell=state->currentboard[r][c];
if (cell!='0') {
if (check[cell-'1']==1) {
return true;
}
check[cell-'1']=1;
}
}
}
/* We passed all the checks :) */
return false;
}
void sudoku_generate(struct sudoku_state_t* state)
{ {
char* difficulty; char* difficulty;
char str[80]; char str[80];
bool res;
struct sudoku_state_t new_state;
clear_state(state); clear_state(&new_state);
display_board(state); display_board(&new_state);
rb->splash(0, true, "Generating..."); rb->splash(0, true, "Generating...");
#ifdef HAVE_ADJUSTABLE_CPU_FREQ #ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(true); rb->cpu_boost(true);
#endif #endif
sudoku_generate_board(state,&difficulty); res = sudoku_generate_board(&new_state,&difficulty);
#ifdef HAVE_ADJUSTABLE_CPU_FREQ #ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false); rb->cpu_boost(false);
#endif #endif
rb->snprintf(str,sizeof(str),"Difficulty: %s",difficulty); if (res) {
display_board(state); rb->memcpy(state,&new_state,sizeof(new_state));
rb->splash(3*HZ, true, str); rb->snprintf(str,sizeof(str),"Difficulty: %s",difficulty);
rb->strncpy(state->filename,GAME_FILE,MAX_PATH); display_board(state);
rb->splash(3*HZ, true, str);
rb->strncpy(state->filename,GAME_FILE,MAX_PATH);
} else {
display_board(&new_state);
rb->splash(2*HZ, true, "Aborted");
}
return res;
} }
int sudoku_menu_cb(int key, int m) int sudoku_menu_cb(int key, int m)
@ -946,6 +1000,44 @@ bool sudoku_menu(struct sudoku_state_t* state)
return (result==MENU_ATTACHED_USB); return (result==MENU_ATTACHED_USB);
} }
/* Menu used when user is in edit mode - i.e. creating a new game manually */
int sudoku_edit_menu(struct sudoku_state_t* state)
{
int m;
int result;
static const struct menu_item items[] = {
{ "Save as", NULL },
{ "Quit", NULL },
};
m = rb->menu_init(items, sizeof(items) / sizeof(*items),
sudoku_menu_cb, NULL, NULL, NULL);
result=rb->menu_show(m);
switch (result) {
case 0: /* Save new game */
rb->kbd_input(state->filename,MAX_PATH);
if (save_sudoku(state)) {
state->editmode=0;
} else {
rb->splash(HZ*2, true, "Save failed");
}
break;
case 1: /* Quit */
break;
default:
break;
}
rb->menu_exit(m);
return result;
}
void move_cursor(struct sudoku_state_t* state, int newx, int newy) void move_cursor(struct sudoku_state_t* state, int newx, int newy)
{ {
int oldx, oldy; int oldx, oldy;
@ -975,6 +1067,7 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
bool exit; bool exit;
int button; int button;
int lastbutton = BUTTON_NONE; int lastbutton = BUTTON_NONE;
int res;
long ticks; long ticks;
struct sudoku_state_t state; struct sudoku_state_t state;
@ -987,8 +1080,8 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
if (parameter==NULL) { if (parameter==NULL) {
/* We have been started as a plugin - try default sudoku.ss */ /* We have been started as a plugin - try default sudoku.ss */
if (!load_sudoku(&state,GAME_FILE)) { if (!load_sudoku(&state,GAME_FILE)) {
/* No previous game saved, generate one */ /* No previous game saved, use the default */
sudoku_generate(&state); default_state(&state);
} }
} else { } else {
if (!load_sudoku(&state,(char*)parameter)) { if (!load_sudoku(&state,(char*)parameter)) {
@ -1009,7 +1102,14 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
#ifdef SUDOKU_BUTTON_QUIT #ifdef SUDOKU_BUTTON_QUIT
/* Exit game */ /* Exit game */
case SUDOKU_BUTTON_QUIT: case SUDOKU_BUTTON_QUIT:
exit=1; if (check_status(&state)) {
rb->splash(HZ*2, true, "Illegal move!");
/* Ignore any button presses during the splash */
rb->button_clear_queue();
} else {
save_sudoku(&state);
exit=1;
}
break; break;
#endif #endif
@ -1047,7 +1147,7 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
if (state.currentboard[state.y][state.x]=='9') { if (state.currentboard[state.y][state.x]=='9') {
state.currentboard[state.y][state.x]='0'; state.currentboard[state.y][state.x]='0';
} else { } else {
state.currentboard[state.y][state.x]++; state.currentboard[state.y][state.x]++;
} }
} }
} }
@ -1157,11 +1257,11 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
rb->button_clear_queue(); rb->button_clear_queue();
} else { } else {
if (state.editmode) { if (state.editmode) {
rb->kbd_input(state.filename,MAX_PATH); res = sudoku_edit_menu(&state);
if (save_sudoku(&state)) { if (res == MENU_ATTACHED_USB) {
state.editmode=0; return PLUGIN_USB_CONNECTED;
} else { } else if (res == 1) { /* Quit */
rb->splash(HZ*2, true, "Save failed"); return PLUGIN_OK;
} }
} else { } else {
if (sudoku_menu(&state)) { if (sudoku_menu(&state)) {