X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=pearl.c;h=f44365630d25ebbf4e4a80567c5522837ed099f5;hb=c5500926bf7458aabb0e11945bfd24038bfeedee;hp=a06ecc25f55db9f80194dd554d6c136a57899c9f;hpb=faabfe3b625d94efbbfc3dda5368213c466530b4;p=sgt-puzzles.git diff --git a/pearl.c b/pearl.c index a06ecc2..f443656 100644 --- a/pearl.c +++ b/pearl.c @@ -180,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 */ @@ -214,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); @@ -225,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]; @@ -262,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); @@ -274,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"; @@ -1158,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); @@ -1245,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 */ @@ -1253,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 */ @@ -1321,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 */ @@ -1348,7 +1356,7 @@ static int new_clues(game_params *params, random_state *rs, 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; @@ -1385,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; @@ -1408,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; @@ -1444,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; @@ -1500,11 +1509,7 @@ static void dsf_update_completion(game_state *state, int *loopclass, assert(INGRID(state, bx, by)); /* should not have a link off grid */ bc = by*w+bx; -#if 0 assert(state->lines[bc] & F(dir)); /* should have reciprocal link */ -#endif - /* TODO put above assertion back in once we stop generating partially - * soluble puzzles. */ if (!(state->lines[bc] & F(dir))) return; ae = dsf_canonify(dsf, ac); @@ -1632,8 +1637,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); @@ -1647,7 +1650,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) */ @@ -1668,8 +1671,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; @@ -1713,14 +1716,40 @@ 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 { @@ -1733,7 +1762,7 @@ struct game_ui { 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; @@ -1752,17 +1781,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) { } @@ -1812,7 +1841,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; @@ -1896,9 +1926,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]; @@ -1927,24 +1958,21 @@ static void interpret_ui_drag(game_state *state, game_ui *ui, int *clearing, } } -static char *mark_in_direction(game_state *state, int x, int y, int dir, - int ismark, char *buf) +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 = ismark ? 'M' : 'F'; + + 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. */ - if (ismark) { - if ((state->lines[y*w+x] & dir) || (state->lines[y2*w+x2] & dir2)) - return ""; - } else { - if ((state->marks[y*w+x] & dir) || (state->marks[y2*w+x2] & dir2)) - return ""; - } + 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); @@ -1954,14 +1982,18 @@ static char *mark_in_direction(game_state *state, int x, int y, int dir, (btn) == CURSOR_DOWN ? D : (btn) == CURSOR_UP ? U :\ (btn) == CURSOR_LEFT ? L : R) -static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, - int x, int y, int button) +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]; + int shift = button & MOD_SHFT, control = button & MOD_CTRL; + button &= ~MOD_MASK; + if (IS_MOUSE_DOWN(button)) { ui->cursor_active = FALSE; @@ -1984,15 +2016,18 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, if (IS_MOUSE_RELEASE(button)) release = TRUE; - if (IS_CURSOR_MOVE(button & ~MOD_MASK)) { + if (IS_CURSOR_MOVE(button)) { if (!ui->cursor_active) { ui->cursor_active = TRUE; - } else if (button & (MOD_SHFT | MOD_CTRL)) { + } else if (control | shift) { + char *move; if (ui->ndragcoords > 0) return NULL; ui->ndragcoords = -1; - return mark_in_direction(state, ui->curx, ui->cury, - KEY_DIRECTION(button & ~MOD_MASK), - (button & MOD_SHFT), tmpbuf); + 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) @@ -2001,7 +2036,7 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, return ""; } - if (IS_CURSOR_SELECT(button & ~MOD_MASK)) { + if (IS_CURSOR_SELECT(button)) { if (!ui->cursor_active) { ui->cursor_active = TRUE; return ""; @@ -2019,6 +2054,11 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, } } + if (button == 27 || button == '\b') { + ui->ndragcoords = -1; + return ""; + } + if (release) { if (ui->ndragcoords > 0) { /* End of a drag: process the cached line data. */ @@ -2085,7 +2125,7 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, direction = (x < cx) ? L : R; } return mark_in_direction(state, gx, gy, direction, - (button == RIGHT_RELEASE), tmpbuf); + (button == LEFT_RELEASE), tmpbuf); } } } @@ -2096,7 +2136,7 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, 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; @@ -2171,8 +2211,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; @@ -2183,7 +2223,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; } @@ -2222,7 +2262,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; @@ -2281,7 +2321,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); @@ -2358,9 +2398,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; @@ -2433,33 +2474,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; @@ -2471,7 +2512,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); @@ -2526,7 +2567,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,