*
* TODO:
*
+ * - reports from users are that `Trivial'-mode puzzles are still
+ * rather hard compared to newspapers' easy ones, so some better
+ * low-end difficulty grading would be nice
+ * + it's possible that really easy puzzles always have
+ * _several_ things you can do, so don't make you hunt too
+ * hard for the one deduction you can currently make
+ * + it's also possible that easy puzzles require fewer
+ * cross-eliminations: perhaps there's a higher incidence of
+ * things you can deduce by looking only at (say) rows,
+ * rather than things you have to check both rows and columns
+ * for
+ * + but really, what I need to do is find some really easy
+ * puzzles and _play_ them, to see what's actually easy about
+ * them
+ * + while I'm revamping this area, filling in the _last_
+ * number in a nearly-full row or column should certainly be
+ * permitted even at the lowest difficulty level.
+ * + also Owen noticed that `Basic' grids requiring numeric
+ * elimination are actually very hard, so I wonder if a
+ * difficulty gradation between that and positional-
+ * elimination-only might be in order
+ * + but it's not good to have _too_ many difficulty levels, or
+ * it'll take too long to randomly generate a given level.
+ *
* - it might still be nice to do some prioritisation on the
* removal of numbers from the grid
* + one possibility is to try to minimise the maximum number
* click, _or_ you highlight a square and then type. At most
* one thing is ever highlighted at a time, so there's no way
* to confuse the two.
- * + `pencil marks' might be useful for more subtle forms of
- * deduction, now we can create puzzles that require them.
+ * + then again, I don't actually like sudoku.com's interface;
+ * it's too much like a paint package whereas I prefer to
+ * think of Solo as a text editor.
+ * + another PDA-friendly possibility is a drag interface:
+ * _drag_ numbers from the palette into the grid squares.
+ * Thought experiments suggest I'd prefer that to the
+ * sudoku.com approach, but I haven't actually tried it.
*/
/*
#include "puzzles.h"
-#define max(x,y) ((x)>(y)?(x):(y))
-
/*
* To save space, I store digits internally as unsigned char. This
* imposes a hard limit of 255 on the order of the puzzle. Since
COL_CLUE,
COL_USER,
COL_HIGHLIGHT,
+ COL_PENCIL,
NCOLOURS
};
struct game_state {
int c, r;
digit *grid;
+ unsigned char *pencil; /* c*r*c*r elements */
unsigned char *immutable; /* marks which digits are clues */
int completed, cheated;
};
ret->c = ret->r = 3;
ret->symm = SYMM_ROT2; /* a plausible default */
- ret->diff = DIFF_SIMPLE; /* so is this */
+ ret->diff = DIFF_BLOCK; /* so is this */
return ret;
}
} presets[] = {
{ "2x2 Trivial", { 2, 2, SYMM_ROT2, DIFF_BLOCK } },
{ "2x3 Basic", { 2, 3, SYMM_ROT2, DIFF_SIMPLE } },
+ { "3x3 Trivial", { 3, 3, SYMM_ROT2, DIFF_BLOCK } },
{ "3x3 Basic", { 3, 3, SYMM_ROT2, DIFF_SIMPLE } },
{ "3x3 Intermediate", { 3, 3, SYMM_ROT2, DIFF_INTERSECT } },
{ "3x3 Advanced", { 3, 3, SYMM_ROT2, DIFF_SET } },
+ { "3x3 Unreasonable", { 3, 3, SYMM_ROT2, DIFF_RECURSIVE } },
+#ifndef SLOW_SYSTEM
{ "3x4 Basic", { 3, 4, SYMM_ROT2, DIFF_SIMPLE } },
{ "4x4 Basic", { 4, 4, SYMM_ROT2, DIFF_SIMPLE } },
+#endif
};
if (i < 0 || i >= lenof(presets))
return TRUE;
}
-static game_params *decode_params(char const *string)
+static void decode_params(game_params *ret, char const *string)
{
- game_params *ret = default_params();
-
ret->c = ret->r = atoi(string);
- ret->symm = SYMM_ROT2;
while (*string && isdigit((unsigned char)*string)) string++;
if (*string == 'x') {
string++;
string++, ret->diff = DIFF_INTERSECT;
else if (*string == 'a') /* advanced */
string++, ret->diff = DIFF_SET;
+ else if (*string == 'u') /* unreasonable */
+ string++, ret->diff = DIFF_RECURSIVE;
} else
string++; /* eat unknown character */
}
-
- return ret;
}
-static char *encode_params(game_params *params)
+static char *encode_params(game_params *params, int full)
{
char str[80];
- /*
- * Symmetry is a game generation preference and hence is left
- * out of the encoding. Users can add it back in as they see
- * fit.
- */
sprintf(str, "%dx%d", params->c, params->r);
+ if (full) {
+ switch (params->symm) {
+ case SYMM_REF4: strcat(str, "m4"); break;
+ case SYMM_ROT4: strcat(str, "r4"); break;
+ /* case SYMM_ROT2: strcat(str, "r2"); break; [default] */
+ case SYMM_NONE: strcat(str, "a"); break;
+ }
+ switch (params->diff) {
+ /* case DIFF_BLOCK: strcat(str, "dt"); break; [default] */
+ case DIFF_SIMPLE: strcat(str, "db"); break;
+ case DIFF_INTERSECT: strcat(str, "di"); break;
+ case DIFF_SET: strcat(str, "da"); break;
+ case DIFF_RECURSIVE: strcat(str, "du"); break;
+ }
+ }
return dupstr(str);
}
ret[3].name = "Difficulty";
ret[3].type = C_CHOICES;
- ret[3].sval = ":Trivial:Basic:Intermediate:Advanced";
+ ret[3].sval = ":Trivial:Basic:Intermediate:Advanced:Unreasonable";
ret[3].ival = params->diff;
ret[4].name = NULL;
return ret;
}
+struct nsolve_scratch {
+ unsigned char *grid, *rowidx, *colidx, *set;
+};
+
static int nsolve_set(struct nsolve_usage *usage,
+ struct nsolve_scratch *scratch,
int start, int step1, int step2
#ifdef STANDALONE_SOLVER
, char *fmt, ...
{
int c = usage->c, r = usage->r, cr = c*r;
int i, j, n, count;
- unsigned char *grid = snewn(cr*cr, unsigned char);
- unsigned char *rowidx = snewn(cr, unsigned char);
- unsigned char *colidx = snewn(cr, unsigned char);
- unsigned char *set = snewn(cr, unsigned char);
+ unsigned char *grid = scratch->grid;
+ unsigned char *rowidx = scratch->rowidx;
+ unsigned char *colidx = scratch->colidx;
+ unsigned char *set = scratch->set;
/*
* We are passed a cr-by-cr matrix of booleans. Our first job
}
if (progress) {
- sfree(set);
- sfree(colidx);
- sfree(rowidx);
- sfree(grid);
return TRUE;
}
}
break; /* done */
}
- sfree(set);
- sfree(colidx);
- sfree(rowidx);
- sfree(grid);
-
return FALSE;
}
+static struct nsolve_scratch *nsolve_new_scratch(struct nsolve_usage *usage)
+{
+ struct nsolve_scratch *scratch = snew(struct nsolve_scratch);
+ int cr = usage->cr;
+ scratch->grid = snewn(cr*cr, unsigned char);
+ scratch->rowidx = snewn(cr, unsigned char);
+ scratch->colidx = snewn(cr, unsigned char);
+ scratch->set = snewn(cr, unsigned char);
+ return scratch;
+}
+
+static void nsolve_free_scratch(struct nsolve_scratch *scratch)
+{
+ sfree(scratch->set);
+ sfree(scratch->colidx);
+ sfree(scratch->rowidx);
+ sfree(scratch->grid);
+ sfree(scratch);
+}
+
static int nsolve(int c, int r, digit *grid)
{
struct nsolve_usage *usage;
+ struct nsolve_scratch *scratch;
int cr = c*r;
int x, y, n;
int diff = DIFF_BLOCK;
memset(usage->col, FALSE, cr * cr);
memset(usage->blk, FALSE, cr * cr);
+ scratch = nsolve_new_scratch(usage);
+
/*
* Place all the clue numbers we are given.
*/
*/
for (x = 0; x < cr; x += r)
for (y = 0; y < r; y++)
- if (nsolve_set(usage, cubepos(x,y,1), r*cr, 1
+ if (nsolve_set(usage, scratch, cubepos(x,y,1), r*cr, 1
#ifdef STANDALONE_SOLVER
, "set elimination, block (%d,%d)", 1+x/r, 1+y
#endif
* Row-wise set elimination.
*/
for (y = 0; y < cr; y++)
- if (nsolve_set(usage, cubepos(0,y,1), cr*cr, 1
+ if (nsolve_set(usage, scratch, cubepos(0,y,1), cr*cr, 1
#ifdef STANDALONE_SOLVER
, "set elimination, row %d", 1+YUNTRANS(y)
#endif
* Column-wise set elimination.
*/
for (x = 0; x < cr; x++)
- if (nsolve_set(usage, cubepos(x,0,1), cr, 1
+ if (nsolve_set(usage, scratch, cubepos(x,0,1), cr, 1
#ifdef STANDALONE_SOLVER
, "set elimination, column %d", 1+x
#endif
break;
}
+ nsolve_free_scratch(scratch);
+
sfree(usage->cube);
sfree(usage->row);
sfree(usage->col);
return i;
}
-static char *new_game_seed(game_params *params, random_state *rs,
- game_aux_info **aux)
+struct game_aux_info {
+ int c, r;
+ digit *grid;
+};
+
+static char *new_game_desc(game_params *params, random_state *rs,
+ game_aux_info **aux, int interactive)
{
int c = params->c, r = params->r, cr = c*r;
int area = cr*cr;
struct xy { int x, y; } *locs;
int nlocs;
int ret;
- char *seed;
+ char *desc;
int coords[16], ncoords;
int xlim, ylim;
- int maxdiff;
+ int maxdiff, recursing;
/*
* Adjust the maximum difficulty level to be consistent with
assert(ret == 1);
assert(check_valid(c, r, grid));
+ /*
+ * Save the solved grid in the aux_info.
+ */
+ {
+ game_aux_info *ai = snew(game_aux_info);
+ ai->c = c;
+ ai->r = r;
+ ai->grid = snewn(cr * cr, digit);
+ memcpy(ai->grid, grid, cr * cr * sizeof(digit));
+ /*
+ * We might already have written *aux the last time we
+ * went round this loop, in which case we should free
+ * the old aux_info before overwriting it with the new
+ * one.
+ */
+ if (*aux) {
+ sfree((*aux)->grid);
+ sfree(*aux);
+ }
+ *aux = ai;
+ }
+
/*
* Now we have a solved grid, start removing things from it
* while preserving solubility.
*/
symmetry_limit(params, &xlim, &ylim, params->symm);
+ recursing = FALSE;
while (1) {
int x, y, i, j;
* nsolve.
*/
for (i = 0; i < nlocs; i++) {
+ int ret;
+
x = locs[i].x;
y = locs[i].y;
for (j = 0; j < ncoords; j++)
grid2[coords[2*j+1]*cr+coords[2*j]] = 0;
- if (nsolve(c, r, grid2) <= maxdiff) {
+ if (recursing)
+ ret = (rsolve(c, r, grid2, NULL, 2) == 1);
+ else
+ ret = (nsolve(c, r, grid2) <= maxdiff);
+
+ if (ret) {
for (j = 0; j < ncoords; j++)
grid[coords[2*j+1]*cr+coords[2*j]] = 0;
break;
if (i == nlocs) {
/*
- * There was nothing we could remove without destroying
- * solvability.
+ * There was nothing we could remove without
+ * destroying solvability. If we're trying to
+ * generate a recursion-only grid and haven't
+ * switched over to rsolve yet, we now do;
+ * otherwise we give up.
*/
- break;
+ if (maxdiff == DIFF_RECURSIVE && !recursing) {
+ recursing = TRUE;
+ } else {
+ break;
+ }
}
}
memcpy(grid2, grid, area);
- } while (nsolve(c, r, grid2) != maxdiff);
+ } while (nsolve(c, r, grid2) < maxdiff);
sfree(grid2);
sfree(locs);
/*
* Now we have the grid as it will be presented to the user.
- * Encode it in a game seed.
+ * Encode it in a game desc.
*/
{
char *p;
int run, i;
- seed = snewn(5 * area, char);
- p = seed;
+ desc = snewn(5 * area, char);
+ p = desc;
run = 0;
for (i = 0; i <= area; i++) {
int n = (i < area ? grid[i] : -1);
* bottom right, there's no point putting an
* unnecessary _ before or after it.
*/
- if (p > seed && n > 0)
+ if (p > desc && n > 0)
*p++ = '_';
}
if (n > 0)
run = 0;
}
}
- assert(p - seed < 5 * area);
+ assert(p - desc < 5 * area);
*p++ = '\0';
- seed = sresize(seed, p - seed, char);
+ desc = sresize(desc, p - desc, char);
}
sfree(grid);
- return seed;
+ return desc;
}
static void game_free_aux_info(game_aux_info *aux)
{
- assert(!"Shouldn't happen");
+ sfree(aux->grid);
+ sfree(aux);
}
-static char *validate_seed(game_params *params, char *seed)
+static char *validate_desc(game_params *params, char *desc)
{
int area = params->r * params->r * params->c * params->c;
int squares = 0;
- while (*seed) {
- int n = *seed++;
+ while (*desc) {
+ int n = *desc++;
if (n >= 'a' && n <= 'z') {
squares += n - 'a' + 1;
} else if (n == '_') {
/* do nothing */;
} else if (n > '0' && n <= '9') {
squares++;
- while (*seed >= '0' && *seed <= '9')
- seed++;
+ while (*desc >= '0' && *desc <= '9')
+ desc++;
} else
- return "Invalid character in game specification";
+ return "Invalid character in game description";
}
if (squares < area)
return NULL;
}
-static game_state *new_game(game_params *params, char *seed)
+static game_state *new_game(midend_data *me, game_params *params, char *desc)
{
game_state *state = snew(game_state);
int c = params->c, r = params->r, cr = c*r, area = cr * cr;
state->r = params->r;
state->grid = snewn(area, digit);
+ state->pencil = snewn(area * cr, unsigned char);
+ memset(state->pencil, 0, area * cr);
state->immutable = snewn(area, unsigned char);
memset(state->immutable, FALSE, area);
state->completed = state->cheated = FALSE;
i = 0;
- while (*seed) {
- int n = *seed++;
+ while (*desc) {
+ int n = *desc++;
if (n >= 'a' && n <= 'z') {
int run = n - 'a' + 1;
assert(i + run <= area);
} else if (n > '0' && n <= '9') {
assert(i < area);
state->immutable[i] = TRUE;
- state->grid[i++] = atoi(seed-1);
- while (*seed >= '0' && *seed <= '9')
- seed++;
+ state->grid[i++] = atoi(desc-1);
+ while (*desc >= '0' && *desc <= '9')
+ desc++;
} else {
assert(!"We can't get here");
}
ret->grid = snewn(area, digit);
memcpy(ret->grid, state->grid, area);
+ ret->pencil = snewn(area * cr, unsigned char);
+ memcpy(ret->pencil, state->pencil, area * cr);
+
ret->immutable = snewn(area, unsigned char);
memcpy(ret->immutable, state->immutable, area);
static void free_game(game_state *state)
{
sfree(state->immutable);
+ sfree(state->pencil);
sfree(state->grid);
sfree(state);
}
-static game_state *solve_game(game_state *state, game_aux_info *aux,
+static game_state *solve_game(game_state *state, game_aux_info *ai,
char **error)
{
game_state *ret;
- int c = state->c, r = state->r;
+ int c = state->c, r = state->r, cr = c*r;
int rsolve_ret;
- /*
- * I could have stored the grid I invented in the game_aux_info
- * and extracted it here where available, but it seems easier
- * just to run my internal solver in all cases.
- */
-
ret = dup_game(state);
ret->completed = ret->cheated = TRUE;
- rsolve_ret = rsolve(c, r, ret->grid, NULL, 2);
+ /*
+ * If we already have the solution in the aux_info, save
+ * ourselves some time.
+ */
+ if (ai) {
+
+ assert(c == ai->c);
+ assert(r == ai->r);
+ memcpy(ret->grid, ai->grid, cr * cr * sizeof(digit));
- if (rsolve_ret != 1) {
- free_game(ret);
- if (rsolve_ret == 0)
- *error = "No solution exists for this puzzle";
- else
- *error = "Multiple solutions exist for this puzzle";
- return NULL;
+ } else {
+ rsolve_ret = rsolve(c, r, ret->grid, NULL, 2);
+
+ if (rsolve_ret != 1) {
+ free_game(ret);
+ if (rsolve_ret == 0)
+ *error = "No solution exists for this puzzle";
+ else
+ *error = "Multiple solutions exist for this puzzle";
+ return NULL;
+ }
}
return ret;
* enter that number or letter in the grid.
*/
int hx, hy;
+ /*
+ * This indicates whether the current highlight is a
+ * pencil-mark one or a real one.
+ */
+ int hpencil;
};
static game_ui *new_ui(game_state *state)
game_ui *ui = snew(game_ui);
ui->hx = ui->hy = -1;
+ ui->hpencil = 0;
return ui;
}
sfree(ui);
}
-static game_state *make_move(game_state *from, game_ui *ui, int x, int y,
- int button)
+static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds,
+ int x, int y, int button)
{
int c = from->c, r = from->r, cr = c*r;
int tx, ty;
game_state *ret;
+ button &= ~MOD_MASK;
+
tx = (x + TILE_SIZE - BORDER) / TILE_SIZE - 1;
ty = (y + TILE_SIZE - BORDER) / TILE_SIZE - 1;
- if (tx >= 0 && tx < cr && ty >= 0 && ty < cr && button == LEFT_BUTTON) {
- if (tx == ui->hx && ty == ui->hy) {
- ui->hx = ui->hy = -1;
- } else {
- ui->hx = tx;
- ui->hy = ty;
- }
- return from; /* UI activity occurred */
+ if (tx >= 0 && tx < cr && ty >= 0 && ty < cr) {
+ if (button == LEFT_BUTTON) {
+ if (from->immutable[ty*cr+tx]) {
+ ui->hx = ui->hy = -1;
+ } else if (tx == ui->hx && ty == ui->hy && ui->hpencil == 0) {
+ ui->hx = ui->hy = -1;
+ } else {
+ ui->hx = tx;
+ ui->hy = ty;
+ ui->hpencil = 0;
+ }
+ return from; /* UI activity occurred */
+ }
+ if (button == RIGHT_BUTTON) {
+ /*
+ * Pencil-mode highlighting for non filled squares.
+ */
+ if (from->grid[ty*cr+tx] == 0) {
+ if (tx == ui->hx && ty == ui->hy && ui->hpencil) {
+ ui->hx = ui->hy = -1;
+ } else {
+ ui->hpencil = 1;
+ ui->hx = tx;
+ ui->hy = ty;
+ }
+ } else {
+ ui->hx = ui->hy = -1;
+ }
+ return from; /* UI activity occurred */
+ }
}
if (ui->hx != -1 && ui->hy != -1 &&
if (button == ' ')
n = 0;
+ /*
+ * Can't overwrite this square. In principle this shouldn't
+ * happen anyway because we should never have even been
+ * able to highlight the square, but it never hurts to be
+ * careful.
+ */
if (from->immutable[ui->hy*cr+ui->hx])
- return NULL; /* can't overwrite this square */
+ return NULL;
+
+ /*
+ * Can't make pencil marks in a filled square. In principle
+ * this shouldn't happen anyway because we should never
+ * have even been able to pencil-highlight the square, but
+ * it never hurts to be careful.
+ */
+ if (ui->hpencil && from->grid[ui->hy*cr+ui->hx])
+ return NULL;
ret = dup_game(from);
- ret->grid[ui->hy*cr+ui->hx] = n;
- ui->hx = ui->hy = -1;
+ if (ui->hpencil && n > 0) {
+ int index = (ui->hy*cr+ui->hx) * cr + (n-1);
+ ret->pencil[index] = !ret->pencil[index];
+ } else {
+ ret->grid[ui->hy*cr+ui->hx] = n;
+ memset(ret->pencil + (ui->hy*cr+ui->hx)*cr, 0, cr);
- /*
- * We've made a real change to the grid. Check to see
- * if the game has been completed.
- */
- if (!ret->completed && check_valid(c, r, ret->grid)) {
- ret->completed = TRUE;
- }
+ /*
+ * We've made a real change to the grid. Check to see
+ * if the game has been completed.
+ */
+ if (!ret->completed && check_valid(c, r, ret->grid)) {
+ ret->completed = TRUE;
+ }
+ }
+ ui->hx = ui->hy = -1;
return ret; /* made a valid move */
}
int started;
int c, r, cr;
digit *grid;
+ unsigned char *pencil;
unsigned char *hl;
};
ret[COL_HIGHLIGHT * 3 + 1] = 0.85F * ret[COL_BACKGROUND * 3 + 1];
ret[COL_HIGHLIGHT * 3 + 2] = 0.85F * ret[COL_BACKGROUND * 3 + 2];
+ ret[COL_PENCIL * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
+ ret[COL_PENCIL * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
+ ret[COL_PENCIL * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
+
*ncolours = NCOLOURS;
return ret;
}
ds->cr = cr;
ds->grid = snewn(cr*cr, digit);
memset(ds->grid, 0, cr*cr);
+ ds->pencil = snewn(cr*cr*cr, digit);
+ memset(ds->pencil, 0, cr*cr*cr);
ds->hl = snewn(cr*cr, unsigned char);
memset(ds->hl, 0, cr*cr);
static void game_free_drawstate(game_drawstate *ds)
{
sfree(ds->hl);
+ sfree(ds->pencil);
sfree(ds->grid);
sfree(ds);
}
int cx, cy, cw, ch;
char str[2];
- if (ds->grid[y*cr+x] == state->grid[y*cr+x] && ds->hl[y*cr+x] == hl)
+ if (ds->grid[y*cr+x] == state->grid[y*cr+x] &&
+ ds->hl[y*cr+x] == hl &&
+ !memcmp(ds->pencil+(y*cr+x)*cr, state->pencil+(y*cr+x)*cr, cr))
return; /* no change required */
tx = BORDER + x * TILE_SIZE + 2;
clip(fe, cx, cy, cw, ch);
- /* background needs erasing? */
- if (ds->grid[y*cr+x] || ds->hl[y*cr+x] != hl)
- draw_rect(fe, cx, cy, cw, ch, hl ? COL_HIGHLIGHT : COL_BACKGROUND);
+ /* background needs erasing */
+ draw_rect(fe, cx, cy, cw, ch, hl == 1 ? COL_HIGHLIGHT : COL_BACKGROUND);
+
+ /* pencil-mode highlight */
+ if (hl == 2) {
+ int coords[6];
+ coords[0] = cx;
+ coords[1] = cy;
+ coords[2] = cx+cw/2;
+ coords[3] = cy;
+ coords[4] = cx;
+ coords[5] = cy+ch/2;
+ draw_polygon(fe, coords, 3, TRUE, COL_HIGHLIGHT);
+ }
/* new number needs drawing? */
if (state->grid[y*cr+x]) {
draw_text(fe, tx + TILE_SIZE/2, ty + TILE_SIZE/2,
FONT_VARIABLE, TILE_SIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE,
state->immutable[y*cr+x] ? COL_CLUE : COL_USER, str);
+ } else {
+ /* pencil marks required? */
+ int i, j;
+
+ for (i = j = 0; i < cr; i++)
+ if (state->pencil[(y*cr+x)*cr+i]) {
+ int dx = j % r, dy = j / r, crm = max(c, r);
+ str[1] = '\0';
+ str[0] = i + '1';
+ if (str[0] > '9')
+ str[0] += 'a' - ('9'+1);
+ draw_text(fe, tx + (4*dx+3) * TILE_SIZE / (4*r+2),
+ ty + (4*dy+3) * TILE_SIZE / (4*c+2),
+ FONT_VARIABLE, TILE_SIZE/(crm*5/4),
+ ALIGN_VCENTRE | ALIGN_HCENTRE, COL_PENCIL, str);
+ j++;
+ }
}
unclip(fe);
draw_update(fe, cx, cy, cw, ch);
ds->grid[y*cr+x] = state->grid[y*cr+x];
+ memcpy(ds->pencil+(y*cr+x)*cr, state->pencil+(y*cr+x)*cr, cr);
ds->hl[y*cr+x] = hl;
}
*/
for (x = 0; x < cr; x++) {
for (y = 0; y < cr; y++) {
- draw_number(fe, ds, state, x, y,
- (x == ui->hx && y == ui->hy) ||
- (flashtime > 0 &&
- (flashtime <= FLASH_TIME/3 ||
- flashtime >= FLASH_TIME*2/3)));
+ int highlight = 0;
+ if (flashtime > 0 &&
+ (flashtime <= FLASH_TIME/3 ||
+ flashtime >= FLASH_TIME*2/3))
+ highlight = 1;
+ if (x == ui->hx && y == ui->hy)
+ highlight = ui->hpencil ? 2 : 1;
+ draw_number(fe, ds, state, x, y, highlight);
}
}
}
static float game_anim_length(game_state *oldstate, game_state *newstate,
- int dir)
+ int dir, game_ui *ui)
{
return 0.0F;
}
static float game_flash_length(game_state *oldstate, game_state *newstate,
- int dir)
+ int dir, game_ui *ui)
{
if (!oldstate->completed && newstate->completed &&
!oldstate->cheated && !newstate->cheated)
return FALSE;
}
+static int game_timing_state(game_state *state)
+{
+ return TRUE;
+}
+
#ifdef COMBINED
#define thegame solo
#endif
dup_params,
TRUE, game_configure, custom_params,
validate_params,
- new_game_seed,
+ new_game_desc,
game_free_aux_info,
- validate_seed,
+ validate_desc,
new_game,
dup_game,
free_game,
game_anim_length,
game_flash_length,
game_wants_statusbar,
+ FALSE, game_timing_state,
+ 0, /* mouse_priorities */
};
#ifdef STANDALONE_SOLVER
game_params *p;
game_state *s;
int recurse = TRUE;
- char *id = NULL, *seed, *err;
+ char *id = NULL, *desc, *err;
int y, x;
int grade = FALSE;
return 1;
}
- seed = strchr(id, ':');
- if (!seed) {
+ desc = strchr(id, ':');
+ if (!desc) {
fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
return 1;
}
- *seed++ = '\0';
+ *desc++ = '\0';
- p = decode_params(id);
- err = validate_seed(p, seed);
+ p = default_params();
+ decode_params(p, id);
+ err = validate_desc(p, desc);
if (err) {
fprintf(stderr, "%s: %s\n", argv[0], err);
return 1;
}
- s = new_game(p, seed);
+ s = new_game(NULL, p, desc);
if (recurse) {
int ret = rsolve(p->c, p->r, s->grid, NULL, 2);