X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=pearl.c;h=20ba3d44a6bbfaec99aa132bc938147e5eb0bb38;hb=3234912f921916a1b8da164fd61dc75579358577;hp=c018d5aef489645083294f9c4aa5f76c787b8454;hpb=b16eece9fc502afb9dfb0aca9fd7bfba2239d3e3;p=sgt-puzzles.git diff --git a/pearl.c b/pearl.c index c018d5a..20ba3d4 100644 --- a/pearl.c +++ b/pearl.c @@ -5,10 +5,17 @@ /* * TODO: * - * - Keyboard-control cursor. (Would probably have to address both - * square centres, for laying multiple edges at a time in a - * drag-like style, and grid edges for marking particular line - * segments as no-go.) + * - The current keyboard cursor mechanism works well on ordinary PC + * keyboards, but for platforms with only arrow keys and a select + * button or two, we may at some point need a simpler one which can + * handle 'x' markings without needing shift keys. For instance, a + * cursor with twice the grid resolution, so that it can range + * across face centres, edge centres and vertices; 'clicks' on face + * centres begin a drag as currently, clicks on edges toggle + * markings, and clicks on vertices are ignored (but it would be + * too confusing not to let the cursor rest on them). But I'm + * pretty sure that would be less pleasant to play on a full + * keyboard, so probably a #ifdef would be the thing. * * - Generation is still pretty slow, due to difficulty coming up in * the first place with a loop that makes a soluble puzzle even @@ -83,6 +90,7 @@ enum { COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT, + COL_CURSOR_BACKGROUND = COL_LOWLIGHT, COL_BLACK, COL_WHITE, COL_ERROR, COL_GRID, COL_FLASH, COL_DRAGON, COL_DRAGOFF, @@ -172,7 +180,7 @@ static void free_params(game_params *params) sfree(params); } -static game_params *dup_params(game_params *params) +static game_params *dup_params(const game_params *params) { game_params *ret = snew(game_params); *ret = *params; /* structure copy */ @@ -206,7 +214,7 @@ static void decode_params(game_params *ret, char const *string) } } -static char *encode_params(game_params *params, int full) +static char *encode_params(const game_params *params, int full) { char buf[256]; sprintf(buf, "%dx%d", params->w, params->h); @@ -217,7 +225,7 @@ static char *encode_params(game_params *params, int full) return dupstr(buf); } -static config_item *game_configure(game_params *params) +static config_item *game_configure(const game_params *params) { config_item *ret; char buf[64]; @@ -254,7 +262,7 @@ static config_item *game_configure(game_params *params) return ret; } -static game_params *custom_params(config_item *cfg) +static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); @@ -266,7 +274,7 @@ static game_params *custom_params(config_item *cfg) return ret; } -static char *validate_params(game_params *params, int full) +static char *validate_params(const game_params *params, int full) { if (params->w < 5) return "Width must be at least five"; if (params->h < 5) return "Height must be at least five"; @@ -1150,12 +1158,20 @@ void pearl_loopgen(int w, int h, char *lines, random_state *rs) #endif } -static int new_clues(game_params *params, random_state *rs, +static int new_clues(const game_params *params, random_state *rs, char *clues, char *grid) { - int w = params->w, h = params->h; + int w = params->w, h = params->h, diff = params->difficulty; int ngen = 0, x, y, d, ret, i; + + /* + * Difficulty exception: 5x5 Tricky is not generable (the + * generator will spin forever trying) and so we fudge it to Easy. + */ + if (w == 5 && h == 5 && diff > DIFF_EASY) + diff = DIFF_EASY; + while (1) { ngen++; pearl_loopgen(w, h, grid, rs); @@ -1237,7 +1253,7 @@ static int new_clues(game_params *params, random_state *rs, /* * See if we can solve the puzzle just like this. */ - ret = pearl_solve(w, h, clues, grid, params->difficulty, FALSE); + ret = pearl_solve(w, h, clues, grid, diff, FALSE); assert(ret > 0); /* shouldn't be inconsistent! */ if (ret != 1) continue; /* go round and try again */ @@ -1245,8 +1261,8 @@ static int new_clues(game_params *params, random_state *rs, /* * Check this puzzle isn't too easy. */ - if (params->difficulty > DIFF_EASY) { - ret = pearl_solve(w, h, clues, grid, params->difficulty-1, FALSE); + if (diff > DIFF_EASY) { + ret = pearl_solve(w, h, clues, grid, diff-1, FALSE); assert(ret > 0); if (ret == 1) continue; /* too easy: try again */ @@ -1313,7 +1329,7 @@ static int new_clues(game_params *params, random_state *rs, clue = clues[y*w+x]; clues[y*w+x] = 0; /* try removing this clue */ - ret = pearl_solve(w, h, clues, grid, params->difficulty, FALSE); + ret = pearl_solve(w, h, clues, grid, diff, FALSE); assert(ret > 0); if (ret != 1) clues[y*w+x] = clue; /* oops, put it back again */ @@ -1335,22 +1351,22 @@ static int new_clues(game_params *params, random_state *rs, break; /* got it */ } + debug(("%d %dx%d loops before finished puzzle.\n", ngen, w, h)); + return ngen; } -static char *new_game_desc(game_params *params, random_state *rs, +static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { char *grid, *clues; char *desc; - int ngen, w = params->w, h = params->h, i, j; + int w = params->w, h = params->h, i, j; grid = snewn(w*h, char); clues = snewn(w*h, char); - ngen = new_clues(params, rs, clues, grid); - - debug(("%d %dx%d loops before finished puzzle.\n", ngen, w, h)); + new_clues(params, rs, clues, grid); desc = snewn(w * h + 1, char); for (i = j = 0; i < w*h; i++) { @@ -1377,7 +1393,7 @@ static char *new_game_desc(game_params *params, random_state *rs, return desc; } -static char *validate_desc(game_params *params, char *desc) +static char *validate_desc(const game_params *params, const char *desc) { int i, sizesofar; const int totalsize = params->w * params->h; @@ -1400,7 +1416,8 @@ static char *validate_desc(game_params *params, char *desc) return NULL; } -static game_state *new_game(midend *me, game_params *params, char *desc) +static game_state *new_game(midend *me, const game_params *params, + const char *desc) { game_state *state = snew(game_state); int i, j, sz = params->w*params->h; @@ -1436,7 +1453,7 @@ static game_state *new_game(midend *me, game_params *params, char *desc) return state; } -static game_state *dup_game(game_state *state) +static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); int sz = state->shared->sz, i; @@ -1624,8 +1641,6 @@ static void check_completion(game_state *state, int mark) if (!had_error && loopclass != -1) { state->completed = TRUE; state->loop_length = dsfsize[loopclass]; - } else { - state->completed = FALSE; } sfree(dsf); @@ -1639,7 +1654,7 @@ static void check_completion(game_state *state, int mark) * - no clues must be contradicted (highlight clue itself in error if so) * - if there is a closed loop it must include every line segment laid * - if there's a smaller closed loop then highlight whole loop as error - * - no square must have more than 3 lines radiating from centre point + * - no square must have more than 2 lines radiating from centre point * (highlight all lines in that square as error if so) */ @@ -1660,8 +1675,8 @@ static char *solve_for_diff(game_state *state, char *old_lines, char *new_lines) return move; } -static char *solve_game(game_state *state, game_state *currstate, - char *aux, char **error) +static char *solve_game(const game_state *state, const game_state *currstate, + const char *aux, char **error) { game_state *solved = dup_game(state); int i, ret, sz = state->shared->sz; @@ -1705,29 +1720,61 @@ done: return move; } -static int game_can_format_as_text_now(game_params *params) +static int game_can_format_as_text_now(const game_params *params) { - return FALSE; + return TRUE; } -static char *game_text_format(game_state *state) +static char *game_text_format(const game_state *state) { - return NULL; + int w = state->shared->w, h = state->shared->h, cw = 4, ch = 2; + int gw = cw*(w-1) + 2, gh = ch*(h-1) + 1, len = gw * gh, r, c, j; + char *board = snewn(len + 1, char); + + assert(board); + memset(board, ' ', len); + + for (r = 0; r < h; ++r) { + for (c = 0; c < w; ++c) { + int i = r*w + c, cell = r*ch*gw + c*cw; + board[cell] = "+BW"[(unsigned char)state->shared->clues[i]]; + if (c < w - 1 && (state->lines[i] & R || state->lines[i+1] & L)) + memset(board + cell + 1, '-', cw - 1); + if (r < h - 1 && (state->lines[i] & D || state->lines[i+w] & U)) + for (j = 1; j < ch; ++j) board[cell + j*gw] = '|'; + if (c < w - 1 && (state->marks[i] & R || state->marks[i+1] & L)) + board[cell + cw/2] = 'x'; + if (r < h - 1 && (state->marks[i] & D || state->marks[i+w] & U)) + board[cell + (ch/2 * gw)] = 'x'; + } + + for (j = 0; j < (r == h - 1 ? 1 : ch); ++j) + board[r*ch*gw + (gw - 1) + j*gw] = '\n'; + } + + board[len] = '\0'; + return board; } struct game_ui { int *dragcoords; /* list of (y*w+x) coords in drag so far */ - int ndragcoords; /* number of entries in dragcoords. 0 = no drag. */ + int ndragcoords; /* number of entries in dragcoords. + * 0 = click but no drag yet. -1 = no drag at all */ int clickx, clicky; /* pixel position of initial click */ + + int curx, cury; /* grid position of keyboard cursor */ + int cursor_active; /* TRUE iff cursor is shown */ }; -static game_ui *new_ui(game_state *state) +static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); int sz = state->shared->sz; - ui->ndragcoords = 0; + ui->ndragcoords = -1; ui->dragcoords = snewn(sz, int); + ui->cursor_active = FALSE; + ui->curx = ui->cury = 0; return ui; } @@ -1738,17 +1785,17 @@ static void free_ui(game_ui *ui) sfree(ui); } -static char *encode_ui(game_ui *ui) +static char *encode_ui(const game_ui *ui) { return NULL; } -static void decode_ui(game_ui *ui, char *encoding) +static void decode_ui(game_ui *ui, const char *encoding) { } -static void game_changed_state(game_ui *ui, game_state *oldstate, - game_state *newstate) +static void game_changed_state(game_ui *ui, const game_state *oldstate, + const game_state *newstate) { } @@ -1761,6 +1808,7 @@ static void game_changed_state(game_ui *ui, game_state *oldstate, #define BORDER_WIDTH (max(TILE_SIZE / 32, 1)) #define COORD(x) ( (x) * TILE_SIZE + BORDER ) +#define CENTERED_COORD(x) ( COORD(x) + TILE_SIZE/2 ) #define FROMCOORD(x) ( ((x) < BORDER) ? -1 : ( ((x) - BORDER) / TILE_SIZE) ) #define DS_ESHIFT 4 /* R/U/L/D shift, for error flags */ @@ -1769,6 +1817,7 @@ static void game_changed_state(game_ui *ui, game_state *oldstate, #define DS_ERROR_CLUE (1 << 20) #define DS_FLASH (1 << 21) +#define DS_CURSOR (1 << 22) enum { GUI_MASYU, GUI_LOOPY }; @@ -1796,7 +1845,8 @@ struct game_drawstate { char *draglines; /* size w*h; lines flipped by current drag */ }; -static void update_ui_drag(game_state *state, game_ui *ui, int gx, int gy) +static void update_ui_drag(const game_state *state, game_ui *ui, + int gx, int gy) { int /* sz = state->shared->sz, */ w = state->shared->w; int i, ox, oy, pos; @@ -1805,6 +1855,9 @@ static void update_ui_drag(game_state *state, game_ui *ui, int gx, int gy) if (!INGRID(state, gx, gy)) return; /* square is outside grid */ + if (ui->ndragcoords < 0) + return; /* drag not in progress anyway */ + pos = gy * w + gx; lastpos = ui->dragcoords[ui->ndragcoords > 0 ? ui->ndragcoords-1 : 0]; @@ -1836,7 +1889,15 @@ static void update_ui_drag(game_state *state, game_ui *ui, int gx, int gy) if (ox == gx || oy == gy) { int dx = (gx < ox ? -1 : gx > ox ? +1 : 0); int dy = (gy < oy ? -1 : gy > oy ? +1 : 0); + int dir = (dy>0 ? D : dy<0 ? U : dx>0 ? R : L); while (ox != gx || oy != gy) { + /* + * If the drag attempts to cross a 'no line here' mark, + * stop there. We physically don't allow the user to drag + * over those marks. + */ + if (state->marks[oy*w+ox] & dir) + break; ox += dx; oy += dy; ui->dragcoords[ui->ndragcoords++] = oy * w + ox; @@ -1869,9 +1930,10 @@ static void update_ui_drag(game_state *state, game_ui *ui, int gx, int gy) * to state newstate, each of which equals either 0 or dir] * } */ -static void interpret_ui_drag(game_state *state, game_ui *ui, int *clearing, - int i, int *sx, int *sy, int *dx, int *dy, - int *dir, int *oldstate, int *newstate) +static void interpret_ui_drag(const game_state *state, const game_ui *ui, + int *clearing, int i, int *sx, int *sy, + int *dx, int *dy, int *dir, + int *oldstate, int *newstate) { int w = state->shared->w; int sp = ui->dragcoords[i], dp = ui->dragcoords[i+1]; @@ -1900,15 +1962,49 @@ static void interpret_ui_drag(game_state *state, game_ui *ui, int *clearing, } } -static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, - int x, int y, int button) +static char *mark_in_direction(const game_state *state, int x, int y, int dir, + int primary, char *buf) { int w = state->shared->w /*, h = state->shared->h, sz = state->shared->sz */; + int x2 = x + DX(dir); + int y2 = y + DY(dir); + int dir2 = F(dir); + + char ch = primary ? 'F' : 'M', *other; + + if (!INGRID(state, x, y) || !INGRID(state, x2, y2)) return ""; + + /* disallow laying a mark over a line, or vice versa. */ + other = primary ? state->marks : state->lines; + if (other[y*w+x] & dir || other[y2*w+x2] & dir2) return ""; + + sprintf(buf, "%c%d,%d,%d;%c%d,%d,%d", ch, dir, x, y, ch, dir2, x2, y2); + return dupstr(buf); +} + +#define KEY_DIRECTION(btn) (\ + (btn) == CURSOR_DOWN ? D : (btn) == CURSOR_UP ? U :\ + (btn) == CURSOR_LEFT ? L : R) + +static char *interpret_move(const game_state *state, game_ui *ui, + const game_drawstate *ds, + int x, int y, int button) +{ + int w = state->shared->w, h = state->shared->h /*, sz = state->shared->sz */; int gx = FROMCOORD(x), gy = FROMCOORD(y), i; + int release = FALSE; char tmpbuf[80]; - if (button == LEFT_BUTTON) { - if (!INGRID(state, gx, gy)) return NULL; + int shift = button & MOD_SHFT, control = button & MOD_CTRL; + button &= ~MOD_MASK; + + if (IS_MOUSE_DOWN(button)) { + ui->cursor_active = FALSE; + + if (!INGRID(state, gx, gy)) { + ui->ndragcoords = -1; + return NULL; + } ui->clickx = x; ui->clicky = y; ui->dragcoords[0] = gy * w + gx; @@ -1917,13 +2013,58 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, return ""; } - if (IS_MOUSE_DRAG(button)) { + if (button == LEFT_DRAG && ui->ndragcoords >= 0) { update_ui_drag(state, ui, gx, gy); return ""; } - if (IS_MOUSE_RELEASE(button)) { - if (ui->ndragcoords) { + if (IS_MOUSE_RELEASE(button)) release = TRUE; + + if (IS_CURSOR_MOVE(button)) { + if (!ui->cursor_active) { + ui->cursor_active = TRUE; + } else if (control | shift) { + char *move; + if (ui->ndragcoords > 0) return NULL; + ui->ndragcoords = -1; + move = mark_in_direction(state, ui->curx, ui->cury, + KEY_DIRECTION(button), control, tmpbuf); + if (control && !shift && *move) + move_cursor(button, &ui->curx, &ui->cury, w, h, FALSE); + return move; + } else { + move_cursor(button, &ui->curx, &ui->cury, w, h, FALSE); + if (ui->ndragcoords >= 0) + update_ui_drag(state, ui, ui->curx, ui->cury); + } + return ""; + } + + if (IS_CURSOR_SELECT(button)) { + if (!ui->cursor_active) { + ui->cursor_active = TRUE; + return ""; + } else if (button == CURSOR_SELECT) { + if (ui->ndragcoords == -1) { + ui->ndragcoords = 0; + ui->dragcoords[0] = ui->cury * w + ui->curx; + ui->clickx = CENTERED_COORD(ui->curx); + ui->clicky = CENTERED_COORD(ui->cury); + return ""; + } else release = TRUE; + } else if (button == CURSOR_SELECT2 && ui->ndragcoords >= 0) { + ui->ndragcoords = -1; + return ""; + } + } + + if (button == 27 || button == '\b') { + ui->ndragcoords = -1; + return ""; + } + + if (release) { + if (ui->ndragcoords > 0) { /* End of a drag: process the cached line data. */ int buflen = 0, bufsize = 256, tmplen; char *buf = NULL; @@ -1949,14 +2090,28 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, } } - ui->ndragcoords = 0; + ui->ndragcoords = -1; return buf ? buf : ""; - } else { - /* Click (or tiny drag). Work out which edge we were closest to. */ - int cx = COORD(gx) + TILE_SIZE/2, cy = COORD(gy) + TILE_SIZE/2; - int gx2, gy2, l1, l2, ismark = (button == RIGHT_RELEASE); - char movec = ismark ? 'M' : 'F'; + } else if (ui->ndragcoords == 0) { + /* Click (or tiny drag). Work out which edge we were + * closest to. */ + int cx, cy; + + ui->ndragcoords = -1; + + /* + * We process clicks based on the mouse-down location, + * because that's more natural for a user to carefully + * control than the mouse-up. + */ + x = ui->clickx; + y = ui->clicky; + + gx = FROMCOORD(x); + gy = FROMCOORD(y); + cx = CENTERED_COORD(gx); + cy = CENTERED_COORD(gy); if (!INGRID(state, gx, gy)) return ""; @@ -1965,30 +2120,16 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, return ""; } else { + int direction; if (abs(x-cx) < abs(y-cy)) { /* Closest to top/bottom edge. */ - l1 = (y < cy) ? U : D; + direction = (y < cy) ? U : D; } else { /* Closest to left/right edge. */ - l1 = (x < cx) ? L : R; + direction = (x < cx) ? L : R; } - gx2 = gx + DX(l1); gy2 = gy + DY(l1); - l2 = F(l1); - - if (!INGRID(state, gx, gy) || !INGRID(state, gx2, gy2)) return ""; - - /* disallow laying a mark over a line, or vice versa. */ - if (ismark) { - if ((state->lines[gy*w+gx] & l1) || (state->lines[gy2*w+gx2] & l2)) - return ""; - } else { - if ((state->marks[gy*w+gx] & l1) || (state->marks[gy2*w+gx2] & l2)) - return ""; - } - - sprintf(tmpbuf, "%c%d,%d,%d;%c%d,%d,%d", - movec, l1, gx, gy, movec, l2, gx2, gy2); - return dupstr(tmpbuf); + return mark_in_direction(state, gx, gy, direction, + (button == LEFT_RELEASE), tmpbuf); } } } @@ -1996,12 +2137,10 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, if (button == 'H' || button == 'h') return dupstr("H"); - /* TODO cursor */ - return NULL; } -static game_state *execute_move(game_state *state, char *move) +static game_state *execute_move(const game_state *state, const char *move) { int w = state->shared->w, h = state->shared->h; char c; @@ -2023,9 +2162,6 @@ static game_state *execute_move(game_state *state, char *move) if (!INGRID(state, x, y)) goto badmove; if (l < 0 || l > 15) goto badmove; - /* TODO trying to set a line over a no-line mark should be - * a failed move? */ - if (c == 'L') ret->lines[y*w + x] |= (char)l; else if (c == 'N') @@ -2038,6 +2174,16 @@ static game_state *execute_move(game_state *state, char *move) else if (c == 'M') ret->marks[y*w + x] ^= (char)l; + /* + * If we ended up trying to lay a line _over_ a mark, + * that's a failed move: interpret_move() should have + * ensured we never received a move string like that in + * the first place. + */ + if ((ret->lines[y*w + x] & (char)l) && + (ret->marks[y*w + x] & (char)l)) + goto badmove; + move += n; } else if (strcmp(move, "H") == 0) { pearl_solve(ret->shared->w, ret->shared->h, @@ -2069,8 +2215,8 @@ badmove: #define FLASH_TIME 0.5F -static void game_compute_size(game_params *params, int tilesize, - int *x, int *y) +static void game_compute_size(const game_params *params, int tilesize, + int *x, int *y) { /* Ick: fake up `ds->tilesize' for macro expansion purposes */ struct { int halfsz; } ads, *ds = &ads; @@ -2081,7 +2227,7 @@ static void game_compute_size(game_params *params, int tilesize, } static void game_set_size(drawing *dr, game_drawstate *ds, - game_params *params, int tilesize) + const game_params *params, int tilesize) { ds->halfsz = (tilesize-1)/2; } @@ -2120,7 +2266,7 @@ static float *game_colours(frontend *fe, int *ncolours) return ret; } -static game_drawstate *game_new_drawstate(drawing *dr, game_state *state) +static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int i; @@ -2179,7 +2325,7 @@ static void draw_lines_specific(drawing *dr, game_drawstate *ds, } } -static void draw_square(drawing *dr, game_drawstate *ds, game_ui *ui, +static void draw_square(drawing *dr, game_drawstate *ds, const game_ui *ui, int x, int y, unsigned int lflags, char clue) { int ox = COORD(x), oy = COORD(y); @@ -2193,7 +2339,10 @@ static void draw_square(drawing *dr, game_drawstate *ds, game_ui *ui, clip(dr, ox, oy, TILE_SIZE, TILE_SIZE); /* Clear the square. */ - draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, COL_BACKGROUND); + draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, + (lflags & DS_CURSOR) ? + COL_CURSOR_BACKGROUND : COL_BACKGROUND); + if (get_gui_style() == GUI_LOOPY) { /* Draw small dot, underneath any lines. */ @@ -2241,7 +2390,7 @@ static void draw_square(drawing *dr, game_drawstate *ds, game_ui *ui, /* Draw a clue, if present */ if (clue != NOCLUE) { int c = (lflags & DS_FLASH) ? COL_FLASH : - (clue == CORNER) ? COL_BLACK : COL_WHITE; + (clue == STRAIGHT) ? COL_WHITE : COL_BLACK; if (lflags & DS_ERROR_CLUE) /* draw a bigger 'error' clue circle. */ draw_circle(dr, cx, cy, TILE_SIZE*3/8, COL_ERROR, COL_ERROR); @@ -2253,9 +2402,10 @@ static void draw_square(drawing *dr, game_drawstate *ds, game_ui *ui, draw_update(dr, ox, oy, TILE_SIZE, TILE_SIZE); } -static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, - game_state *state, int dir, game_ui *ui, - float animtime, float flashtime) +static void game_redraw(drawing *dr, game_drawstate *ds, + const game_state *oldstate, const game_state *state, + int dir, const game_ui *ui, + float animtime, float flashtime) { int w = state->shared->w, h = state->shared->h, sz = state->shared->sz; int x, y, force = 0, flashing = 0; @@ -2292,7 +2442,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, flashing = DS_FLASH; memset(ds->draglines, 0, sz); - if (ui->dragcoords) { + if (ui->ndragcoords > 0) { int i, clearing = TRUE; for (i = 0; i < ui->ndragcoords - 1; i++) { int sx, sy, dx, dy, dir, oldstate, newstate; @@ -2317,6 +2467,9 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, f |= flashing; + if (ui->cursor_active && x == ui->curx && y == ui->cury) + f |= DS_CURSOR; + if (f != ds->lflags[y*w+x] || force) { ds->lflags[y*w+x] = f; draw_square(dr, ds, ui, x, y, f, state->shared->clues[y*w+x]); @@ -2325,33 +2478,33 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, } } -static float game_anim_length(game_state *oldstate, game_state *newstate, - int dir, game_ui *ui) +static float game_anim_length(const game_state *oldstate, + const game_state *newstate, int dir, game_ui *ui) { return 0.0F; } -static float game_flash_length(game_state *oldstate, game_state *newstate, - int dir, game_ui *ui) +static float game_flash_length(const game_state *oldstate, + const game_state *newstate, int dir, game_ui *ui) { - if (!oldstate->completed && - newstate->completed && !newstate->used_solve) + if (!oldstate->completed && newstate->completed && + !oldstate->used_solve && !newstate->used_solve) return FLASH_TIME; else return 0.0F; } -static int game_status(game_state *state) +static int game_status(const game_state *state) { return state->completed ? +1 : 0; } -static int game_timing_state(game_state *state, game_ui *ui) +static int game_timing_state(const game_state *state, game_ui *ui) { return TRUE; } -static void game_print_size(game_params *params, float *x, float *y) +static void game_print_size(const game_params *params, float *x, float *y) { int pw, ph; @@ -2363,7 +2516,7 @@ static void game_print_size(game_params *params, float *x, float *y) *y = ph / 100.0F; } -static void game_print(drawing *dr, game_state *state, int tilesize) +static void game_print(drawing *dr, const game_state *state, int tilesize) { int w = state->shared->w, h = state->shared->h, x, y; int black = print_mono_colour(dr, 0); @@ -2418,7 +2571,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - FALSE, game_can_format_as_text_now, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui,