mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-04-11 16:37:45 -04:00
Purge "Menu" or "Rockbox" from plugin menu titles and match application names from manual. Exception: "Main Menu" is left unchanged as the title for the Main Menu Configuration plugin, since it appears in Rockbox's Settings menu and therefore should match the name of the setting. E.g.: "Rockbox Goban" => "Goban" "Image Viewer Menu" => "Image Viewer" "Viewer Menu" => "Text Viewer" "Menu" => "Chess Clock" "Do What?" => "Text Editor" "Mpegplayer Menu" => "MPEG Player" "Multiboot Settings" => "Multiboot" "Disktidy" => "Disk Tidy" ... Change-Id: Ie6d3be7557f31a36309489037ad8b2b27b06706e
1317 lines
41 KiB
C
1317 lines
41 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2005 Dave Chapman
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/***
|
|
Sudoku by Dave Chapman
|
|
|
|
User instructions
|
|
-----------------
|
|
|
|
Use the arrow keys to move cursor, and press SELECT/ON/F2 to increment
|
|
the number under the cursor.
|
|
|
|
At any time during the game, press On to bring up the game menu with
|
|
further options:
|
|
|
|
Save
|
|
Reload
|
|
Clear
|
|
Solve
|
|
|
|
Sudoku is implemented as a "viewer" for a ".ss" file, as generated by
|
|
Simple Sudoku and other applications - http://angusj.com/sudoku/
|
|
|
|
In-progress game positions are saved in the original .ss file, with
|
|
A-I used to indicate numbers entered by the user.
|
|
|
|
Example ".ss" file, and one with a saved state:
|
|
|
|
...|...|... ...|...|...
|
|
2..|8.4|9.1 2.C|8.4|9.1
|
|
...|1.6|32. E..|1.6|32.
|
|
----------- -----------
|
|
...|..5|.4. ...|..5|.4.
|
|
8..|423|..6 8..|423|..6
|
|
.3.|9..|... .3D|9..|A..
|
|
----------- -----------
|
|
.63|7.9|... .63|7.9|...
|
|
4.9|5.2|..8 4.9|5.2|.C8
|
|
...|...|... ...|...|...
|
|
|
|
*/
|
|
|
|
#include "plugin.h"
|
|
#include "lib/configfile.h"
|
|
|
|
#include <lib/playback_control.h>
|
|
#include "sudoku.h"
|
|
#include "generator.h"
|
|
|
|
/* The bitmaps */
|
|
#include "pluginbitmaps/sudoku_normal.h"
|
|
#include "pluginbitmaps/sudoku_inverse.h"
|
|
#include "pluginbitmaps/sudoku_start.h"
|
|
|
|
#define BITMAP_HEIGHT (BMPHEIGHT_sudoku_normal/10)
|
|
#define BITMAP_STRIDE STRIDE(SCREEN_MAIN, BMPWIDTH_sudoku_normal, BMPHEIGHT_sudoku_normal)
|
|
|
|
#if (LCD_DEPTH>2)
|
|
#define BITMAP_WIDTH (BMPWIDTH_sudoku_normal/2)
|
|
#else
|
|
#define BITMAP_WIDTH BMPWIDTH_sudoku_normal
|
|
#endif
|
|
|
|
|
|
|
|
/* 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 <= LCD_WIDTH /* Horizontal layout, scratchpad at the left */
|
|
|
|
#if ((LCD_HEIGHT==64 && (LCD_WIDTH==112 || LCD_WIDTH==128)) || (LCD_HEIGHT == 96 && LCD_WIDTH == 96))
|
|
/* Archos Recorders and Ondios - 112x64, 9 cells @ 8x6 with 10 border lines */
|
|
#define SMALL_BOARD
|
|
#define MARK_OFFS 1 /* Pixels between border and mark */
|
|
#define MARK_SPACE 1 /* Pixels between two marks */
|
|
#define MARK_SIZE 1 /* Mark width and height */
|
|
|
|
#elif ((LCD_HEIGHT==80) && (LCD_WIDTH==132))
|
|
/* C200, 9 cells @ 8x8 with 8 border lines */
|
|
#define SMALL_BOARD
|
|
#define MARK_OFFS 1 /* Pixels between border and mark */
|
|
#define MARK_SPACE 1 /* Pixels between two marks */
|
|
#define MARK_SIZE 1 /* Mark width and height */
|
|
|
|
#elif ((LCD_HEIGHT==96) && (LCD_WIDTH==128))
|
|
/* iAudio M3, Samsung YH820, 9 cells @ 9x9 with 14 border lines */
|
|
#define MARK_OFFS 1 /* Pixels between border and mark */
|
|
#define MARK_SPACE 2 /* Pixels between two marks */
|
|
#define MARK_SIZE 1 /* Mark width and height */
|
|
|
|
#elif (LCD_HEIGHT==110) && (LCD_WIDTH==138) \
|
|
|| (LCD_HEIGHT==128) && (LCD_WIDTH==128)
|
|
/* iPod Mini - 138x110, 9 cells @ 10x10 with 14 border lines */
|
|
/* iriver H10 5-6GB - 128x128, 9 cells @ 10x10 with 14 border lines */
|
|
#define MARK_OFFS 1 /* Pixels between border and mark */
|
|
#define MARK_SPACE 1 /* Pixels between two marks */
|
|
#define MARK_SIZE 2 /* Mark width and height */
|
|
|
|
#elif ((LCD_HEIGHT==128) && (LCD_WIDTH==160)) \
|
|
|| ((LCD_HEIGHT==132) && (LCD_WIDTH==176))
|
|
/* iAudio X5, Iriver H1x0, iPod G3, G4 - 160x128; */
|
|
/* iPod Nano - 176x132, 9 cells @ 12x12 with 14 border lines */
|
|
#define MARK_OFFS 1 /* Pixels between border and mark */
|
|
#define MARK_SPACE 2 /* Pixels between two marks */
|
|
#define MARK_SIZE 2 /* Mark width and height */
|
|
|
|
#elif ((LCD_HEIGHT==176) && (LCD_WIDTH==220))
|
|
/* Iriver h300, iPod Color/Photo - 220x176, 9 cells @ 16x16 with 14 border lines */
|
|
#define MARK_OFFS 1 /* Pixels between border and mark */
|
|
#define MARK_SPACE 1 /* Pixels between two marks */
|
|
#define MARK_SIZE 4 /* Mark width and height */
|
|
|
|
#elif (LCD_HEIGHT==240) && (LCD_WIDTH==240)
|
|
/* Anbernic RG Nano - 240x240, 9 cells @ 20x20 with 14 border lines */
|
|
#define MARK_OFFS 1 /* Pixels between border and mark */
|
|
#define MARK_SPACE 1 /* Pixels between two marks */
|
|
#define MARK_SIZE 4 /* Mark width and height */
|
|
|
|
#elif (LCD_HEIGHT==240) && (LCD_WIDTH==320)
|
|
/* iPod Video - 320x240, 9 cells @ 24x24 with 14 border lines */
|
|
#define MARK_OFFS 1 /* Pixels between border and mark */
|
|
#define MARK_SPACE 2 /* Pixels between two marks */
|
|
#define MARK_SIZE 6 /* Mark width and height */
|
|
|
|
#elif (LCD_HEIGHT==480) && (LCD_WIDTH==640)
|
|
/* M:Robe 500 - 640x480, 9 cells @ 48x48 with 14 border lines */
|
|
#define MARK_OFFS 1 /* Pixels between border and mark */
|
|
#define MARK_SPACE 2 /* Pixels between two marks */
|
|
#define MARK_SIZE 6 /* Mark width and height */
|
|
|
|
#else
|
|
#error SUDOKU: Unsupported LCD size
|
|
#endif
|
|
|
|
#else /* Vertical layout, scratchpad at the bottom */
|
|
#define VERTICAL_LAYOUT
|
|
|
|
#if ((LCD_HEIGHT==220) && (LCD_WIDTH==176))
|
|
/* e200, 9 cells @ 16x16 with 14 border lines */
|
|
#define MARK_OFFS 1 /* Pixels between border and mark */
|
|
#define MARK_SPACE 1 /* Pixels between two marks */
|
|
#define MARK_SIZE 4 /* Mark width and height */
|
|
|
|
#elif (LCD_HEIGHT>=320) && (LCD_WIDTH>=240)
|
|
/* Gigabeat - 240x320, 9 cells @ 24x24 with 14 border lines */
|
|
#define MARK_OFFS 1 /* Pixels between border and mark */
|
|
#define MARK_SPACE 2 /* Pixels between two marks */
|
|
#define MARK_SIZE 6 /* Mark width and height */
|
|
|
|
#elif ((LCD_HEIGHT==160) && (LCD_WIDTH==128))
|
|
/* Philips GoGear SA9200 - 128x160, 9 cells @ 10x10 with 14 border tiles */
|
|
#define MARK_OFFS 1 /* Pixels between border and mark */
|
|
#define MARK_SPACE 1 /* Pixels between two marks */
|
|
#define MARK_SIZE 2 /* Mark width and height */
|
|
|
|
#else
|
|
#error SUDOKU: Unsupported LCD size
|
|
#endif
|
|
|
|
#endif /* Layout */
|
|
|
|
#define CELL_WIDTH BITMAP_WIDTH
|
|
#define CELL_HEIGHT BITMAP_HEIGHT
|
|
|
|
#ifdef SUDOKU_BUTTON_CHANGEDIR
|
|
int invertdir=0;
|
|
#else
|
|
#define invertdir 0
|
|
#endif
|
|
|
|
#define CFGFILE_VERSION 0 /* Current config file version */
|
|
#define CFGFILE_MINVERSION 0 /* Minimum config file version to accept */
|
|
|
|
#if defined(HAVE_LCD_COLOR) || defined(SUDOKU_BUTTON_POSSIBLE)
|
|
/* settings */
|
|
struct sudoku_config {
|
|
#ifdef HAVE_LCD_COLOR
|
|
int number_display;
|
|
#endif
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
int show_markings;
|
|
#endif
|
|
};
|
|
|
|
struct sudoku_config sudcfg_disk = {
|
|
#ifdef HAVE_LCD_COLOR
|
|
0,
|
|
#endif
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
1,
|
|
#endif
|
|
};
|
|
struct sudoku_config sudcfg;
|
|
|
|
static const char cfg_filename[] = "sudoku.cfg";
|
|
#ifdef HAVE_LCD_COLOR
|
|
static char *number_str[2] = { "black", "coloured" };
|
|
#endif
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
static char *mark_str[2] = { "hide", "show" };
|
|
#endif
|
|
|
|
struct configdata disk_config[] = {
|
|
#ifdef HAVE_LCD_COLOR
|
|
{ TYPE_ENUM, 0, 2, { .int_p = &sudcfg_disk.number_display }, "numbers",
|
|
number_str },
|
|
#endif
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
{ TYPE_ENUM, 0, 2, { .int_p = &sudcfg_disk.show_markings }, "markings",
|
|
mark_str },
|
|
#endif
|
|
};
|
|
#endif
|
|
#ifdef HAVE_LCD_COLOR
|
|
#define NUMBER_TYPE (sudcfg.number_display*CELL_WIDTH)
|
|
#else
|
|
#define NUMBER_TYPE 0
|
|
#endif
|
|
|
|
/* Size dependent build-time calculations */
|
|
#ifdef SMALL_BOARD
|
|
#define BOARD_WIDTH (CELL_WIDTH*9+10)
|
|
#define BOARD_HEIGHT (CELL_HEIGHT*9+10)
|
|
static unsigned int cellxpos[9]={
|
|
1, (CELL_WIDTH+2), (2*CELL_WIDTH+3),
|
|
(3*CELL_WIDTH+4), (4*CELL_WIDTH+5), (5*CELL_WIDTH+6),
|
|
(6*CELL_WIDTH+7), (7*CELL_WIDTH+8), (8*CELL_WIDTH+9)
|
|
};
|
|
static unsigned int cellypos[9]={
|
|
1, (CELL_HEIGHT+2), (2*CELL_HEIGHT+3),
|
|
(3*CELL_HEIGHT+4), (4*CELL_HEIGHT+5), (5*CELL_HEIGHT+6),
|
|
(6*CELL_HEIGHT+7), (7*CELL_HEIGHT+8), (8*CELL_HEIGHT+9)
|
|
};
|
|
#else /* !SMALL_BOARD */
|
|
#define BOARD_WIDTH (CELL_WIDTH*9+10+4)
|
|
#define BOARD_HEIGHT (CELL_HEIGHT*9+10+4)
|
|
static unsigned int cellxpos[9]={
|
|
2, (CELL_WIDTH +3), (2*CELL_WIDTH +4),
|
|
(3*CELL_WIDTH +6), (4*CELL_WIDTH +7), (5*CELL_WIDTH +8),
|
|
(6*CELL_WIDTH+10), (7*CELL_WIDTH+11), (8*CELL_WIDTH+12)
|
|
};
|
|
static unsigned int cellypos[9]={
|
|
2, (CELL_HEIGHT +3), (2*CELL_HEIGHT +4),
|
|
(3*CELL_HEIGHT +6), (4*CELL_HEIGHT +7), (5*CELL_HEIGHT +8),
|
|
(6*CELL_HEIGHT+10), (7*CELL_HEIGHT+11), (8*CELL_HEIGHT+12)
|
|
};
|
|
#endif
|
|
|
|
#ifdef VERTICAL_LAYOUT
|
|
#define XOFS ((LCD_WIDTH-BOARD_WIDTH)/2)
|
|
#define YOFS ((LCD_HEIGHT-(BOARD_HEIGHT+CELL_HEIGHT*2+2))/2)
|
|
#define YOFSSCRATCHPAD (YOFS+BOARD_HEIGHT+CELL_WIDTH)
|
|
#else
|
|
#define XOFSSCRATCHPAD ((LCD_WIDTH-(BOARD_WIDTH+CELL_WIDTH*2+2))/2)
|
|
#define XOFS (XOFSSCRATCHPAD+CELL_WIDTH*2+2)
|
|
#define YOFS ((LCD_HEIGHT-BOARD_HEIGHT)/2)
|
|
#endif
|
|
|
|
#define BLOCK 3
|
|
#define SIZE (BLOCK*BLOCK)
|
|
|
|
static void sudoku_solve(struct sudoku_state_t* state)
|
|
{
|
|
bool ret = sudoku_solve_board(state);
|
|
|
|
if (!ret) {
|
|
rb->splash(HZ*2, "Solve failed");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* Copies the current to the saved board */
|
|
static void save_state(struct sudoku_state_t *state)
|
|
{
|
|
rb->memcpy(state->savedboard, state->currentboard,
|
|
sizeof(state->savedboard));
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
rb->memcpy(state->savedpossible, state->possiblevals,
|
|
sizeof(state->savedpossible));
|
|
#endif
|
|
}
|
|
|
|
/* Copies the saved to the current board */
|
|
static void restore_state(struct sudoku_state_t *state)
|
|
{
|
|
rb->memcpy(state->currentboard, state->savedboard,
|
|
sizeof(state->savedboard));
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
rb->memcpy(state->possiblevals, state->savedpossible,
|
|
sizeof(state->possiblevals));
|
|
#endif
|
|
}
|
|
|
|
static void default_state(struct sudoku_state_t* state)
|
|
{
|
|
int r,c;
|
|
|
|
rb->strlcpy(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
|
|
}
|
|
}
|
|
|
|
/* initialize the saved board so reload function works */
|
|
save_state(state);
|
|
|
|
state->x=0;
|
|
state->y=0;
|
|
state->editmode=0;
|
|
}
|
|
|
|
static void clear_state(struct sudoku_state_t* state)
|
|
{
|
|
int r,c;
|
|
|
|
rb->strlcpy(state->filename,GAME_FILE,MAX_PATH);
|
|
for (r=0;r<9;r++) {
|
|
for (c=0;c<9;c++) {
|
|
state->startboard[r][c]='0';
|
|
state->currentboard[r][c]='0';
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
state->possiblevals[r][c]=0;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
state->x=0;
|
|
state->y=0;
|
|
state->editmode=0;
|
|
}
|
|
|
|
/* Check the status of the board, assuming a change at the cursor location */
|
|
static 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
|
|
text representation (one line per row) may load.
|
|
*/
|
|
static bool load_sudoku(struct sudoku_state_t* state, char* filename)
|
|
{
|
|
int fd;
|
|
size_t n;
|
|
int r = 0, c = 0, d = 0;
|
|
unsigned int i;
|
|
int valid=0;
|
|
char buf[500]; /* A buffer to read a sudoku board from */
|
|
|
|
fd=rb->open(filename, O_RDONLY);
|
|
if (fd < 0) {
|
|
LOGF("Invalid sudoku file: %s\n",filename);
|
|
return(false);
|
|
}
|
|
|
|
rb->strlcpy(state->filename,filename,MAX_PATH);
|
|
n=rb->read(fd,buf,500);
|
|
if (n <= 0) {
|
|
return(false);
|
|
}
|
|
rb->close(fd);
|
|
r=0;
|
|
c=0;
|
|
i=0;
|
|
d=0;
|
|
while ((i < n) && (r < 9)) {
|
|
switch (buf[i]){
|
|
case ' ': case '\t':
|
|
if (c > 0)
|
|
valid=1;
|
|
break;
|
|
case '|':
|
|
case '*':
|
|
case '-':
|
|
case '\r':
|
|
break;
|
|
case '\n':
|
|
if (valid) {
|
|
r++;
|
|
valid=0;
|
|
}
|
|
c = 0;
|
|
d = 0;
|
|
break;
|
|
case '_': case '.':
|
|
valid=1;
|
|
if (c >= SIZE || r >= SIZE){
|
|
LOGF("ERROR: sudoku problem is the wrong size (%d,%d)\n",
|
|
c, r);
|
|
return(false);
|
|
}
|
|
c++;
|
|
break;
|
|
default:
|
|
if (((buf[i]>='A') && (buf[i]<='I')) ||
|
|
((buf[i]>='0') && (buf[i]<='9'))) {
|
|
valid=1;
|
|
if (r >= SIZE || c >= SIZE){
|
|
LOGF("ERROR: sudoku problem is the wrong size "
|
|
"(%d,%d)\n", c, r);
|
|
return(false);
|
|
}
|
|
if ((buf[i]>='0') && (buf[i]<='9')) {
|
|
state->startboard[r][c]=buf[i];
|
|
state->currentboard[r][c]=buf[i];
|
|
} else {
|
|
state->currentboard[r][c]='1'+(buf[i]-'A');
|
|
}
|
|
c++;
|
|
}
|
|
if((buf[i]>='a' && buf[i] <= 'z') && i < (n-1)
|
|
&& (buf[i+1] >= 'a' && buf[i+1] <= 'z')) {
|
|
state->possiblevals[r][d]
|
|
= (((buf[i]-'a') * 26 + buf[i+1]-'a')<<1);
|
|
i++;
|
|
d++;
|
|
}
|
|
/* Ignore any other characters */
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
/* Check that the board is valid - we need to check every row/column
|
|
and block individually */
|
|
for (state->y = 0; state->y < 9; state->y++) {
|
|
state->x = (state->y%3)*3 + (state->y/3);
|
|
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
|
|
disk */
|
|
save_state(state);
|
|
return(true);
|
|
}
|
|
|
|
static bool save_sudoku(struct sudoku_state_t* state)
|
|
{
|
|
int fd;
|
|
int r,c;
|
|
int i;
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
int x;
|
|
char line[41] __NONSTRING ="...|...|... ; \r\n";
|
|
#else
|
|
char line[13] __NONSTRING = "...|...|...\r\n";
|
|
#endif
|
|
char sep[13]__NONSTRING = "-----------\r\n";
|
|
|
|
rb->splash(0, "Saving...");
|
|
|
|
if (state->filename[0]==0) {
|
|
return false;
|
|
}
|
|
|
|
fd=rb->open(state->filename, O_WRONLY|O_CREAT, 0666);
|
|
if (fd >= 0) {
|
|
for (r=0;r<9;r++) {
|
|
i=0;
|
|
for (c=0;c<9;c++) {
|
|
if (state->startboard[r][c]!='0') {
|
|
line[i]=state->startboard[r][c];
|
|
} else if (state->currentboard[r][c]!='0') {
|
|
line[i]='A'+(state->currentboard[r][c]-'1');
|
|
} else {
|
|
line[i]='.';
|
|
}
|
|
i++;
|
|
if ((c==2) || (c==5)) {
|
|
i++;
|
|
}
|
|
}
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
i+=2;
|
|
for(c=0; c<9; c++) {
|
|
x = ((state->possiblevals[r][c]>>1)/26);
|
|
line[i++] = x + 'a';
|
|
x = ((state->possiblevals[r][c]>>1)%26);
|
|
line[i++] = x + 'a';
|
|
}
|
|
#endif
|
|
rb->write(fd,line,sizeof(line));
|
|
if ((r==2) || (r==5)) {
|
|
rb->write(fd,sep,sizeof(sep));
|
|
}
|
|
}
|
|
/* Add a blank line at end */
|
|
rb->write(fd,"\r\n",2);
|
|
rb->close(fd);
|
|
rb->reload_directory();
|
|
/* Save a copy of the saved state - so we can reload without
|
|
using the disk */
|
|
save_state(state);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static void clear_board(struct sudoku_state_t* state)
|
|
{
|
|
int r,c;
|
|
|
|
for (r=0;r<9;r++) {
|
|
for (c=0;c<9;c++) {
|
|
state->currentboard[r][c]=state->startboard[r][c];
|
|
}
|
|
}
|
|
state->x=0;
|
|
state->y=0;
|
|
}
|
|
|
|
static void update_cell(struct sudoku_state_t* state, int r, int c)
|
|
{
|
|
/* We have four types of cell:
|
|
1) User-entered number
|
|
2) Starting number
|
|
3) Cursor in cell
|
|
*/
|
|
|
|
if ((r==state->y) && (c==state->x)) {
|
|
rb->lcd_bitmap_part(sudoku_inverse,NUMBER_TYPE,
|
|
BITMAP_HEIGHT*(state->currentboard[r][c]-'0'),
|
|
BITMAP_STRIDE,
|
|
XOFS+cellxpos[c],YOFS+cellypos[r],CELL_WIDTH,
|
|
CELL_HEIGHT);
|
|
} else {
|
|
if (state->startboard[r][c]!='0') {
|
|
rb->lcd_bitmap_part(sudoku_start,NUMBER_TYPE,
|
|
BITMAP_HEIGHT*(state->startboard[r][c]-'0'),
|
|
BITMAP_STRIDE,
|
|
XOFS+cellxpos[c],YOFS+cellypos[r],
|
|
CELL_WIDTH,CELL_HEIGHT);
|
|
} else {
|
|
rb->lcd_bitmap_part(sudoku_normal,NUMBER_TYPE,
|
|
BITMAP_HEIGHT*(state->currentboard[r][c]-'0'),
|
|
BITMAP_STRIDE,
|
|
XOFS+cellxpos[c],YOFS+cellypos[r],
|
|
CELL_WIDTH,CELL_HEIGHT);
|
|
}
|
|
}
|
|
|
|
rb->lcd_update_rect(cellxpos[c],cellypos[r],CELL_WIDTH,CELL_HEIGHT);
|
|
}
|
|
|
|
|
|
static void display_board(struct sudoku_state_t* state)
|
|
{
|
|
int r,c;
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
int i;
|
|
#endif
|
|
|
|
/* Clear the display buffer */
|
|
rb->lcd_clear_display();
|
|
|
|
/* Draw the gridlines - differently for different targets */
|
|
|
|
#ifdef SMALL_BOARD
|
|
/* Small targets - draw dotted/single lines */
|
|
for (r=0;r<9;r++) {
|
|
if ((r % 3)==0) {
|
|
/* Solid Line */
|
|
rb->lcd_hline(XOFS,XOFS+BOARD_WIDTH-1,YOFS+cellypos[r]-1);
|
|
rb->lcd_vline(XOFS+cellxpos[r]-1,YOFS,YOFS+BOARD_HEIGHT-1);
|
|
} else {
|
|
/* Dotted line */
|
|
for (c=XOFS;c<XOFS+BOARD_WIDTH;c+=2) {
|
|
rb->lcd_drawpixel(c,YOFS+cellypos[r]-1);
|
|
}
|
|
for (c=YOFS;c<YOFS+BOARD_HEIGHT;c+=2) {
|
|
rb->lcd_drawpixel(XOFS+cellxpos[r]-1,c);
|
|
}
|
|
}
|
|
}
|
|
rb->lcd_hline(XOFS,XOFS+BOARD_WIDTH-1,YOFS+cellypos[8]+CELL_HEIGHT);
|
|
rb->lcd_vline(XOFS+cellxpos[8]+CELL_WIDTH,YOFS,YOFS+BOARD_HEIGHT-1);
|
|
#else
|
|
/* Large targets - draw single/double lines */
|
|
for (r=0;r<9;r++) {
|
|
rb->lcd_hline(XOFS,XOFS+BOARD_WIDTH-1,YOFS+cellypos[r]-1);
|
|
rb->lcd_vline(XOFS+cellxpos[r]-1,YOFS,YOFS+BOARD_HEIGHT-1);
|
|
if ((r % 3)==0) {
|
|
rb->lcd_hline(XOFS,XOFS+BOARD_WIDTH-1,YOFS+cellypos[r]-2);
|
|
rb->lcd_vline(XOFS+cellxpos[r]-2,YOFS,YOFS+BOARD_HEIGHT-1);
|
|
}
|
|
}
|
|
rb->lcd_hline(XOFS,XOFS+BOARD_WIDTH-1,YOFS+cellypos[8]+CELL_HEIGHT);
|
|
rb->lcd_hline(XOFS,XOFS+BOARD_WIDTH-1,YOFS+cellypos[8]+CELL_HEIGHT+1);
|
|
rb->lcd_vline(XOFS+cellxpos[8]+CELL_WIDTH,YOFS,YOFS+BOARD_HEIGHT-1);
|
|
rb->lcd_vline(XOFS+cellxpos[8]+CELL_WIDTH+1,YOFS,YOFS+BOARD_HEIGHT-1);
|
|
#endif
|
|
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
#ifdef VERTICAL_LAYOUT
|
|
rb->lcd_hline(XOFS,XOFS+BOARD_WIDTH-1,YOFSSCRATCHPAD);
|
|
rb->lcd_hline(XOFS,XOFS+BOARD_WIDTH-1,YOFSSCRATCHPAD+CELL_HEIGHT+1);
|
|
for (r=0;r<9;r++) {
|
|
#ifdef SMALL_BOARD
|
|
/* Small targets - draw dotted/single lines */
|
|
if ((r % 3)==0) {
|
|
/* Solid Line */
|
|
rb->lcd_vline(XOFS+cellxpos[r]-1,YOFSSCRATCHPAD,
|
|
YOFSSCRATCHPAD+CELL_HEIGHT+1);
|
|
} else {
|
|
/* Dotted line */
|
|
for (c=YOFSSCRATCHPAD;c<YOFSSCRATCHPAD+CELL_HEIGHT+1;c+=2) {
|
|
rb->lcd_drawpixel(XOFS+cellxpos[r]-1,c);
|
|
}
|
|
}
|
|
#else
|
|
/* Large targets - draw single/double lines */
|
|
rb->lcd_vline(XOFS+cellxpos[r]-1,YOFSSCRATCHPAD,
|
|
YOFSSCRATCHPAD+CELL_HEIGHT+1);
|
|
if ((r % 3)==0)
|
|
rb->lcd_vline(XOFS+cellxpos[r]-2,YOFSSCRATCHPAD,
|
|
YOFSSCRATCHPAD+CELL_HEIGHT+1);
|
|
#endif
|
|
if ((r>0) && state->possiblevals[state->y][state->x]&BIT_N(r))
|
|
rb->lcd_bitmap_part(sudoku_normal,NUMBER_TYPE,BITMAP_HEIGHT*r,
|
|
BITMAP_STRIDE,XOFS+cellxpos[r-1],
|
|
YOFSSCRATCHPAD+1,CELL_WIDTH,CELL_HEIGHT);
|
|
}
|
|
rb->lcd_vline(XOFS+cellxpos[8]+CELL_WIDTH,YOFSSCRATCHPAD,
|
|
YOFSSCRATCHPAD+CELL_HEIGHT+1);
|
|
#ifndef SMALL_BOARD
|
|
rb->lcd_vline(XOFS+cellxpos[8]+CELL_WIDTH+1,YOFSSCRATCHPAD,
|
|
YOFSSCRATCHPAD+CELL_HEIGHT+1);
|
|
#endif
|
|
if (state->possiblevals[state->y][state->x]&BIT_N(r))
|
|
rb->lcd_bitmap_part(sudoku_normal,NUMBER_TYPE,BITMAP_HEIGHT*r,
|
|
BITMAP_STRIDE,XOFS+cellxpos[8],YOFSSCRATCHPAD+1,
|
|
CELL_WIDTH,CELL_HEIGHT);
|
|
#else /* Horizontal layout */
|
|
rb->lcd_vline(XOFSSCRATCHPAD,YOFS,YOFS+BOARD_HEIGHT-1);
|
|
rb->lcd_vline(XOFSSCRATCHPAD+CELL_WIDTH+1,YOFS,YOFS+BOARD_HEIGHT-1);
|
|
for (r=0;r<9;r++) {
|
|
#ifdef SMALL_BOARD
|
|
/* Small targets - draw dotted/single lines */
|
|
if ((r % 3)==0) {
|
|
/* Solid Line */
|
|
rb->lcd_hline(XOFSSCRATCHPAD,XOFSSCRATCHPAD+CELL_WIDTH+1,
|
|
YOFS+cellypos[r]-1);
|
|
} else {
|
|
/* Dotted line */
|
|
for (c=XOFSSCRATCHPAD;c<XOFSSCRATCHPAD+CELL_WIDTH+1;c+=2) {
|
|
rb->lcd_drawpixel(c,YOFS+cellypos[r]-1);
|
|
}
|
|
}
|
|
#else
|
|
/* Large targets - draw single/double lines */
|
|
rb->lcd_hline(XOFSSCRATCHPAD,XOFSSCRATCHPAD+CELL_WIDTH+1,
|
|
YOFS+cellypos[r]-1);
|
|
if ((r % 3)==0)
|
|
rb->lcd_hline(XOFSSCRATCHPAD,XOFSSCRATCHPAD+CELL_WIDTH+1,
|
|
YOFS+cellypos[r]-2);
|
|
#endif
|
|
if ((r>0) && state->possiblevals[state->y][state->x]&BIT_N(r))
|
|
rb->lcd_bitmap_part(sudoku_normal,NUMBER_TYPE,BITMAP_HEIGHT*r,
|
|
BITMAP_STRIDE,XOFSSCRATCHPAD+1,
|
|
YOFS+cellypos[r-1],CELL_WIDTH,CELL_HEIGHT);
|
|
}
|
|
rb->lcd_hline(XOFSSCRATCHPAD,XOFSSCRATCHPAD+CELL_WIDTH+1,
|
|
YOFS+cellypos[8]+CELL_HEIGHT);
|
|
#ifndef SMALL_BOARD
|
|
rb->lcd_hline(XOFSSCRATCHPAD,XOFSSCRATCHPAD+CELL_WIDTH+1,
|
|
YOFS+cellypos[8]+CELL_HEIGHT+1);
|
|
#endif
|
|
if (state->possiblevals[state->y][state->x]&BIT_N(r))
|
|
rb->lcd_bitmap_part(sudoku_normal,NUMBER_TYPE,BITMAP_HEIGHT*r,
|
|
BITMAP_STRIDE,XOFSSCRATCHPAD+1,YOFS+cellypos[8],
|
|
CELL_WIDTH,CELL_HEIGHT);
|
|
#endif /* Layout */
|
|
#endif /* SUDOKU_BUTTON_POSSIBLE */
|
|
|
|
/* Draw the numbers */
|
|
for (r=0;r<9;r++) {
|
|
for (c=0;c<9;c++) {
|
|
/* We have four types of cell:
|
|
1) User-entered number
|
|
2) Starting number
|
|
3) Cursor in cell
|
|
*/
|
|
|
|
if ((r==state->y) && (c==state->x)) {
|
|
rb->lcd_bitmap_part(sudoku_inverse,NUMBER_TYPE,
|
|
BITMAP_HEIGHT*(state->currentboard[r][c]-
|
|
'0'),
|
|
BITMAP_STRIDE,
|
|
XOFS+cellxpos[c],YOFS+cellypos[r],
|
|
CELL_WIDTH,CELL_HEIGHT);
|
|
} else {
|
|
if (state->startboard[r][c]!='0') {
|
|
rb->lcd_bitmap_part(sudoku_start,NUMBER_TYPE,
|
|
BITMAP_HEIGHT*(state->startboard[r][c]-
|
|
'0'),
|
|
BITMAP_STRIDE,
|
|
XOFS+cellxpos[c],YOFS+cellypos[r],
|
|
CELL_WIDTH,CELL_HEIGHT);
|
|
} else {
|
|
rb->lcd_bitmap_part(sudoku_normal,NUMBER_TYPE,
|
|
BITMAP_HEIGHT*
|
|
(state->currentboard[r][c]-'0'),
|
|
BITMAP_STRIDE,
|
|
XOFS+cellxpos[c],YOFS+cellypos[r],
|
|
CELL_WIDTH,CELL_HEIGHT);
|
|
}
|
|
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
/* Draw the possible number markings on the board */
|
|
if(sudcfg.show_markings && state->startboard[r][c]=='0'
|
|
&& state->currentboard[r][c]=='0') {
|
|
for(i=0;i<9;i++) {
|
|
if(state->possiblevals[r][c]&(2<<i)) {
|
|
#if LCD_DEPTH > 1
|
|
/* draw markings in dark grey */
|
|
rb->lcd_set_foreground(LCD_DARKGRAY);
|
|
#endif
|
|
rb->lcd_fillrect(XOFS+cellxpos[c]+MARK_OFFS
|
|
+(i%3)*(MARK_SIZE+MARK_SPACE),
|
|
YOFS+cellypos[r]+MARK_OFFS
|
|
+(i/3)*(MARK_SIZE+MARK_SPACE),
|
|
MARK_SIZE,
|
|
MARK_SIZE);
|
|
#if LCD_DEPTH > 1
|
|
rb->lcd_set_foreground(LCD_BLACK);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif /* SUDOKU_BUTTON_POSSIBLE */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* update the screen */
|
|
rb->lcd_update();
|
|
}
|
|
|
|
static bool sudoku_generate(struct sudoku_state_t* state)
|
|
{
|
|
char* difficulty;
|
|
char str[80];
|
|
bool res;
|
|
struct sudoku_state_t new_state;
|
|
|
|
clear_state(&new_state);
|
|
display_board(&new_state);
|
|
rb->splash(0, "Generating...");
|
|
|
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
|
rb->cpu_boost(true);
|
|
#endif
|
|
|
|
res = sudoku_generate_board(&new_state,&difficulty);
|
|
|
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
|
rb->cpu_boost(false);
|
|
#endif
|
|
|
|
if (res) {
|
|
rb->memcpy(state,&new_state,sizeof(new_state));
|
|
rb->snprintf(str,sizeof(str),"Difficulty: %s",difficulty);
|
|
display_board(state);
|
|
rb->splash(HZ*3, str);
|
|
rb->strlcpy(state->filename,GAME_FILE,MAX_PATH);
|
|
} else {
|
|
display_board(&new_state);
|
|
rb->splash(HZ*2, "Aborted");
|
|
}
|
|
/* initialize the saved board so reload function works */
|
|
save_state(state);
|
|
return res;
|
|
}
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
static bool numdisplay_setting(void)
|
|
{
|
|
static const struct opt_items names[] = {
|
|
{"Black", -1},
|
|
{"Coloured", -1},
|
|
};
|
|
|
|
return rb->set_option("Number Display", &sudcfg.number_display, RB_INT, names,
|
|
sizeof(names) / sizeof(names[0]), NULL);
|
|
}
|
|
#endif
|
|
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
static bool showmarkings_setting(void)
|
|
{
|
|
static const struct opt_items names[] = {
|
|
{"Hide", -1},
|
|
{"Show", -1},
|
|
};
|
|
|
|
return rb->set_option("Show Markings", &sudcfg.show_markings, RB_INT, names,
|
|
sizeof(names) / sizeof(names[0]), NULL);
|
|
}
|
|
#endif
|
|
|
|
enum {
|
|
SM_AUDIO_PLAYBACK = 0,
|
|
#ifdef HAVE_LCD_COLOR
|
|
SM_NUMBER_DISPLAY,
|
|
#endif
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
SM_SHOW_MARKINGS,
|
|
#endif
|
|
SM_SAVE,
|
|
SM_RELOAD,
|
|
SM_CLEAR,
|
|
SM_SOLVE,
|
|
SM_GENERATE,
|
|
SM_NEW,
|
|
SM_QUIT,
|
|
};
|
|
|
|
static int sudoku_menu(struct sudoku_state_t* state)
|
|
{
|
|
int result;
|
|
|
|
MENUITEM_STRINGLIST(menu, "Sudoku", NULL,
|
|
"Audio Playback",
|
|
#ifdef HAVE_LCD_COLOR
|
|
"Number Display",
|
|
#endif
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
"Show Markings",
|
|
#endif
|
|
"Save", "Reload", "Clear", "Solve",
|
|
"Generate", "New", "Quit");
|
|
|
|
result = rb->do_menu(&menu, NULL, NULL, false);
|
|
|
|
switch (result) {
|
|
case SM_AUDIO_PLAYBACK:
|
|
playback_control(NULL);
|
|
break;
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
case SM_NUMBER_DISPLAY:
|
|
numdisplay_setting();
|
|
break;
|
|
#endif
|
|
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
case SM_SHOW_MARKINGS:
|
|
showmarkings_setting();
|
|
break;
|
|
#endif
|
|
case SM_SAVE:
|
|
save_sudoku(state);
|
|
break;
|
|
|
|
case SM_RELOAD:
|
|
restore_state(state);
|
|
break;
|
|
|
|
case SM_CLEAR:
|
|
clear_board(state);
|
|
break;
|
|
|
|
case SM_SOLVE:
|
|
sudoku_solve(state);
|
|
break;
|
|
|
|
case SM_GENERATE:
|
|
sudoku_generate(state);
|
|
break;
|
|
|
|
case SM_NEW:
|
|
clear_state(state);
|
|
state->editmode=1;
|
|
break;
|
|
|
|
case SM_QUIT:
|
|
save_sudoku(state);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Menu used when user is in edit mode - i.e. creating a new game manually */
|
|
static int sudoku_edit_menu(struct sudoku_state_t* state)
|
|
{
|
|
int result;
|
|
|
|
MENUITEM_STRINGLIST(menu, "Edit", NULL,
|
|
"Save as", "Quit");
|
|
|
|
result = rb->do_menu(&menu, NULL, NULL, false);
|
|
|
|
switch (result) {
|
|
case 0: /* Save new game */
|
|
rb->kbd_input(state->filename,MAX_PATH, NULL);
|
|
if (save_sudoku(state)) {
|
|
state->editmode=0;
|
|
} else {
|
|
rb->splash(HZ*2, "Save failed");
|
|
}
|
|
break;
|
|
|
|
case 1: /* Quit */
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void move_cursor(struct sudoku_state_t* state, int newx, int newy)
|
|
{
|
|
int oldx, oldy;
|
|
|
|
/* Check that the character at the cursor position is legal */
|
|
if (check_status(state)) {
|
|
rb->splash(HZ*2, "Illegal move!");
|
|
/* Ignore any button presses during the splash */
|
|
rb->button_clear_queue();
|
|
return;
|
|
}
|
|
|
|
/* Move Cursor */
|
|
oldx=state->x;
|
|
oldy=state->y;
|
|
state->x=newx;
|
|
state->y=newy;
|
|
|
|
/* Redraw current and old cells */
|
|
update_cell(state,oldx,oldy);
|
|
update_cell(state,newx,newy);
|
|
}
|
|
|
|
/* plugin entry point */
|
|
enum plugin_status plugin_start(const void* parameter)
|
|
{
|
|
bool exit;
|
|
int button;
|
|
#if defined(SUDOKU_BUTTON_TOGGLE_PRE) || defined(SUDOKU_BUTTON_MENU_PRE)
|
|
int lastbutton = BUTTON_NONE;
|
|
#endif
|
|
int res;
|
|
int rc = PLUGIN_OK;
|
|
long ticks;
|
|
struct sudoku_state_t state;
|
|
|
|
#if defined(HAVE_LCD_COLOR) || defined(SUDOKU_BUTTON_POSSIBLE)
|
|
configfile_load(cfg_filename, disk_config,
|
|
sizeof(disk_config) / sizeof(disk_config[0]),
|
|
CFGFILE_MINVERSION);
|
|
rb->memcpy(&sudcfg, &sudcfg_disk, sizeof(sudcfg)); /* copy to running config */
|
|
#endif
|
|
|
|
#if LCD_DEPTH > 1
|
|
rb->lcd_set_backdrop(NULL);
|
|
rb->lcd_set_foreground(LCD_BLACK);
|
|
rb->lcd_set_background(LCD_WHITE);
|
|
#endif
|
|
|
|
clear_state(&state);
|
|
|
|
if (parameter==NULL) {
|
|
/* We have been started as a plugin - try default sudoku.ss */
|
|
if (!load_sudoku(&state,GAME_FILE)) {
|
|
/* No previous game saved, use the default */
|
|
default_state(&state);
|
|
}
|
|
} else {
|
|
if (!load_sudoku(&state,(char*)parameter)) {
|
|
rb->splash(HZ*2, "Load error");
|
|
return(PLUGIN_ERROR);
|
|
}
|
|
}
|
|
|
|
|
|
display_board(&state);
|
|
|
|
/* The main game loop */
|
|
exit=false;
|
|
ticks=0;
|
|
while(!exit) {
|
|
button = rb->button_get(true);
|
|
|
|
switch(button){
|
|
#ifdef SUDOKU_BUTTON_QUIT
|
|
/* Exit game */
|
|
case SUDOKU_BUTTON_QUIT:
|
|
if (check_status(&state)) {
|
|
rb->splash(HZ*2, "Illegal move!");
|
|
/* Ignore any button presses during the splash */
|
|
rb->button_clear_queue();
|
|
} else {
|
|
save_sudoku(&state);
|
|
exit=true;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
/* Increment digit */
|
|
#ifdef SUDOKU_BUTTON_ALTTOGGLE
|
|
case SUDOKU_BUTTON_ALTTOGGLE | BUTTON_REPEAT:
|
|
#endif
|
|
case SUDOKU_BUTTON_TOGGLE | BUTTON_REPEAT:
|
|
/* Slow down the repeat speed to 1/3 second */
|
|
if ((*rb->current_tick-ticks) < (HZ/3)) {
|
|
break;
|
|
}
|
|
|
|
#ifdef SUDOKU_BUTTON_ALTTOGGLE
|
|
case SUDOKU_BUTTON_ALTTOGGLE:
|
|
#endif
|
|
case SUDOKU_BUTTON_TOGGLE:
|
|
#ifdef SUDOKU_BUTTON_TOGGLE_PRE
|
|
if ((button == SUDOKU_BUTTON_TOGGLE)
|
|
&& (lastbutton != SUDOKU_BUTTON_TOGGLE_PRE))
|
|
break;
|
|
#endif
|
|
/* Increment digit */
|
|
ticks=*rb->current_tick;
|
|
if (state.editmode) {
|
|
if (state.startboard[state.y][state.x]=='9') {
|
|
state.startboard[state.y][state.x]='0';
|
|
state.currentboard[state.y][state.x]='0';
|
|
} else {
|
|
state.startboard[state.y][state.x]++;
|
|
state.currentboard[state.y][state.x]++;
|
|
}
|
|
} else {
|
|
if (state.startboard[state.y][state.x]=='0') {
|
|
if (state.currentboard[state.y][state.x]=='9') {
|
|
state.currentboard[state.y][state.x]='0';
|
|
} else {
|
|
state.currentboard[state.y][state.x]++;
|
|
}
|
|
}
|
|
}
|
|
update_cell(&state,state.y,state.x);
|
|
break;
|
|
|
|
#ifdef SUDOKU_BUTTON_TOGGLEBACK
|
|
case SUDOKU_BUTTON_TOGGLEBACK | BUTTON_REPEAT:
|
|
/* Slow down the repeat speed to 1/3 second */
|
|
if ((*rb->current_tick-ticks) < (HZ/3)) {
|
|
break;
|
|
}
|
|
|
|
case SUDOKU_BUTTON_TOGGLEBACK:
|
|
/* Decrement digit */
|
|
ticks=*rb->current_tick;
|
|
if (state.editmode) {
|
|
if (state.startboard[state.y][state.x]=='0') {
|
|
state.startboard[state.y][state.x]='9';
|
|
state.currentboard[state.y][state.x]='9';
|
|
} else {
|
|
state.startboard[state.y][state.x]--;
|
|
state.currentboard[state.y][state.x]--;
|
|
}
|
|
} else {
|
|
if (state.startboard[state.y][state.x]=='0') {
|
|
if (state.currentboard[state.y][state.x]=='0') {
|
|
state.currentboard[state.y][state.x]='9';
|
|
} else {
|
|
state.currentboard[state.y][state.x]--;
|
|
}
|
|
}
|
|
}
|
|
update_cell(&state,state.y,state.x);
|
|
break;
|
|
#endif
|
|
|
|
/* move cursor left */
|
|
case SUDOKU_BUTTON_LEFT:
|
|
case (SUDOKU_BUTTON_LEFT | BUTTON_REPEAT):
|
|
if ( (state.x==0&&invertdir==0) || (state.y==0&&invertdir==1) ) {
|
|
#ifndef SUDOKU_BUTTON_UP
|
|
if ( (state.y==0&&invertdir==0) || (state.x==0&&invertdir==1)) {
|
|
move_cursor(&state,8,8);
|
|
} else {
|
|
if (invertdir==0) {
|
|
move_cursor(&state,8,state.y-1);
|
|
} else {
|
|
move_cursor(&state,state.x-1,8);
|
|
}
|
|
}
|
|
#else
|
|
move_cursor(&state,8,state.y);
|
|
#endif
|
|
} else {
|
|
if (invertdir==0) {
|
|
move_cursor(&state,state.x-1,state.y);
|
|
} else {
|
|
move_cursor(&state,state.x,state.y-1);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* move cursor right */
|
|
case SUDOKU_BUTTON_RIGHT:
|
|
case (SUDOKU_BUTTON_RIGHT | BUTTON_REPEAT):
|
|
if ( (state.x==8&&invertdir==0) || (state.y==8&&invertdir==1) ) {
|
|
#ifndef SUDOKU_BUTTON_DOWN
|
|
if ( (state.y==8&&invertdir==0) || (state.x==8&&invertdir==1) ) {
|
|
move_cursor(&state,0,0);
|
|
} else {
|
|
if (invertdir==0) {
|
|
move_cursor(&state,0,state.y+1);
|
|
} else {
|
|
move_cursor(&state,state.x+1,0);
|
|
}
|
|
}
|
|
#else
|
|
move_cursor(&state,0,state.y);
|
|
#endif
|
|
} else {
|
|
if (invertdir==0) {
|
|
move_cursor(&state,state.x+1,state.y);
|
|
} else {
|
|
move_cursor(&state,state.x,state.y+1);
|
|
}
|
|
}
|
|
break;
|
|
|
|
#ifdef SUDOKU_BUTTON_UP
|
|
/* move cursor up */
|
|
case SUDOKU_BUTTON_UP:
|
|
case (SUDOKU_BUTTON_UP | BUTTON_REPEAT):
|
|
if (state.y==0) {
|
|
move_cursor(&state,state.x,8);
|
|
} else {
|
|
move_cursor(&state,state.x,state.y-1);
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef SUDOKU_BUTTON_DOWN
|
|
/* move cursor down */
|
|
case SUDOKU_BUTTON_DOWN:
|
|
case (SUDOKU_BUTTON_DOWN | BUTTON_REPEAT):
|
|
if (state.y==8) {
|
|
move_cursor(&state,state.x,0);
|
|
} else {
|
|
move_cursor(&state,state.x,state.y+1);
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case SUDOKU_BUTTON_MENU:
|
|
#ifdef SUDOKU_BUTTON_MENU_PRE
|
|
if (lastbutton != SUDOKU_BUTTON_MENU_PRE)
|
|
break;
|
|
#endif
|
|
/* Don't let the user leave a game in a bad state */
|
|
if (check_status(&state)) {
|
|
rb->splash(HZ*2, "Illegal move!");
|
|
/* Ignore any button presses during the splash */
|
|
rb->button_clear_queue();
|
|
} else {
|
|
if (state.editmode) {
|
|
res = sudoku_edit_menu(&state);
|
|
if (res == MENU_ATTACHED_USB) {
|
|
rc = PLUGIN_USB_CONNECTED;
|
|
exit = true;
|
|
} else if (res == 1) { /* Quit */
|
|
exit = true;
|
|
}
|
|
} else {
|
|
res = sudoku_menu(&state);
|
|
if (res == MENU_ATTACHED_USB) {
|
|
rc = PLUGIN_USB_CONNECTED;
|
|
exit = true;
|
|
} else if (res == SM_QUIT) {
|
|
exit = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
#ifdef SUDOKU_BUTTON_POSSIBLE
|
|
case SUDOKU_BUTTON_POSSIBLE:
|
|
/* Toggle current number in the possiblevals structure */
|
|
if (state.currentboard[state.y][state.x]!='0') {
|
|
state.possiblevals[state.y][state.x]^=
|
|
BIT_N(state.currentboard[state.y][state.x] - '0');
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef SUDOKU_BUTTON_CHANGEDIR
|
|
case SUDOKU_BUTTON_CHANGEDIR:
|
|
/* Change scroll wheel direction */
|
|
invertdir=!invertdir;
|
|
break;
|
|
#endif
|
|
default:
|
|
if (rb->default_event_handler(button) == SYS_USB_CONNECTED) {
|
|
/* Quit if USB has been connected */
|
|
rc = PLUGIN_USB_CONNECTED;
|
|
exit = true;
|
|
}
|
|
break;
|
|
}
|
|
#if defined(SUDOKU_BUTTON_TOGGLE_PRE) || defined(SUDOKU_BUTTON_MENU_PRE)
|
|
if (button != BUTTON_NONE)
|
|
lastbutton = button;
|
|
#endif
|
|
|
|
display_board(&state);
|
|
}
|
|
#if defined(HAVE_LCD_COLOR) || defined(SUDOKU_BUTTON_POSSIBLE)
|
|
if (rb->memcmp(&sudcfg, &sudcfg_disk, sizeof(sudcfg))) /* save settings if changed */
|
|
{
|
|
rb->memcpy(&sudcfg_disk, &sudcfg, sizeof(sudcfg));
|
|
configfile_save(cfg_filename, disk_config,
|
|
sizeof(disk_config) / sizeof(disk_config[0]),
|
|
CFGFILE_VERSION);
|
|
}
|
|
#endif
|
|
return rc;
|
|
}
|