X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=signpost.c;h=ca72768c27530e1a8ce43954666727987738fa85;hb=db313b3948d27244dd7c34c2609c66d6204d8931;hp=19649b2a390268a9cf674973c4c56141f2f40b5e;hpb=295fb9fd83512432404cd701c8b09bae4e627e20;p=sgt-puzzles.git diff --git a/signpost.c b/signpost.c index 19649b2..ca72768 100644 --- a/signpost.c +++ b/signpost.c @@ -92,7 +92,8 @@ static int whichdiri(game_state *state, int fromi, int toi) return whichdir(fromi%w, fromi/w, toi%w, toi/w); } -static int ispointing(game_state *state, int fromx, int fromy, int tox, int toy) +static int ispointing(const game_state *state, int fromx, int fromy, + int tox, int toy) { int w = state->w, dir = state->dirs[fromy*w+fromx]; @@ -119,7 +120,7 @@ static int ispointingi(game_state *state, int fromi, int toi) /* Taking the number 'num', work out the gap between it and the next * available number up or down (depending on d). Return 1 if the region * at (x,y) will fit in that gap, or 0 otherwise. */ -static int move_couldfit(game_state *state, int num, int d, int x, int y) +static int move_couldfit(const game_state *state, int num, int d, int x, int y) { int n, gap, i = y*state->w+x, sz; @@ -144,7 +145,7 @@ static int move_couldfit(game_state *state, int num, int d, int x, int y) return (sz > gap) ? 0 : 1; } -static int isvalidmove(game_state *state, int clever, +static int isvalidmove(const game_state *state, int clever, int fromx, int fromy, int tox, int toy) { int w = state->w, from = fromy*w+fromx, to = toy*w+tox; @@ -196,13 +197,13 @@ static void makelink(game_state *state, int from, int to) state->prev[to] = from; } -static int game_can_format_as_text_now(game_params *params) +static int game_can_format_as_text_now(const game_params *params) { if (params->w * params->h >= 100) return 0; return 1; } -static char *game_text_format(game_state *state) +static char *game_text_format(const game_state *state) { int len = state->h * 2 * (4*state->w + 1) + state->h + 2; int x, y, i, num, n, set; @@ -283,7 +284,7 @@ static int check_nums(game_state *orig, game_state *copy, int only_immutable) int i, ret = 1; assert(copy->n == orig->n); for (i = 0; i < copy->n; i++) { - if (only_immutable && !copy->flags[i] & FLAG_IMMUTABLE) continue; + if (only_immutable && !(copy->flags[i] & FLAG_IMMUTABLE)) continue; assert(copy->nums[i] >= 0); assert(copy->nums[i] <= copy->n); if (copy->nums[i] != orig->nums[i]) { @@ -339,7 +340,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 */ @@ -363,7 +364,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 data[256]; @@ -376,7 +377,7 @@ static char *encode_params(game_params *params, int full) return dupstr(data); } -static config_item *game_configure(game_params *params) +static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; @@ -408,7 +409,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); @@ -419,13 +420,14 @@ 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 < 2 || params->h < 2) - return "Width and height must both be at least two"; - if (params->w == 2 && params->h == 2) /* leads to generation hang */ - return "Width and height cannot both be two"; - + if (params->w < 1) return "Width must be at least one"; + if (params->h < 1) return "Height must be at least one"; + if (full && params->w == 1 && params->h == 1) + /* The UI doesn't let us move these from unsolved to solved, + * so we disallow generating (but not playing) them. */ + return "Width and height cannot both be one"; return NULL; } @@ -463,7 +465,7 @@ static game_state *blank_game(int w, int h) return state; } -static void dup_game_to(game_state *to, game_state *from) +static void dup_game_to(game_state *to, const game_state *from) { to->completed = from->completed; to->used_solve = from->used_solve; @@ -480,7 +482,7 @@ static void dup_game_to(game_state *to, game_state *from) memcpy(to->numsi, from->numsi, (to->n+1)*sizeof(int)); } -static game_state *dup_game(game_state *state) +static game_state *dup_game(const game_state *state) { game_state *ret = blank_game(state->w, state->h); dup_game_to(ret, state); @@ -499,7 +501,7 @@ static void free_game(game_state *state) sfree(state); } -static void unpick_desc(game_params *params, char *desc, +static void unpick_desc(const game_params *params, const char *desc, game_state **sout, char **mout) { game_state *state = blank_game(params->w, params->h); @@ -622,6 +624,7 @@ static int new_game_fill(game_state *state, random_state *rs, state->dirs[taili] = 0; nfilled = 2; + assert(state->n > 1); while (nfilled < state->n) { /* Try and expand _from_ headi; keep going if there's only one @@ -637,6 +640,8 @@ static int new_game_fill(game_state *state, random_state *rs, an = cell_adj(state, headi, aidx, adir); } while (an == 1); + if (nfilled == state->n) break; + /* Try and expand _to_ taili; keep going if there's only one * place to go to. */ an = cell_adj(state, taili, aidx, adir); @@ -793,13 +798,16 @@ done: return ret; } -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) { game_state *state = blank_game(params->w, params->h); char *ret; int headi, taili; + /* this shouldn't happen (validate_params), but let's play it safe */ + if (params->w == 1 && params->h == 1) return dupstr("1a"); + generate: blank_game_into(state); @@ -840,7 +848,7 @@ generate: return ret; } -static char *validate_desc(game_params *params, char *desc) +static char *validate_desc(const game_params *params, const char *desc) { char *ret = NULL; @@ -1202,7 +1210,8 @@ static int check_completion(game_state *state, int mark_errors) if (error) return 0; return complete; } -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 = NULL; @@ -1332,8 +1341,8 @@ static int solve_state(game_state *state) return ret; } -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 *tosolve; char *ret = NULL; @@ -1371,7 +1380,7 @@ struct game_ui { int dx, dy; /* pixel coords of drag posn */ }; -static game_ui *new_ui(game_state *state) +static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); @@ -1391,17 +1400,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) { if (!oldstate->completed && newstate->completed) ui->cshow = ui->dragging = 0; @@ -1418,8 +1427,9 @@ struct game_drawstate { blitter *dragb; }; -static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, - int mx, int my, int button) +static char *interpret_move(const game_state *state, game_ui *ui, + const game_drawstate *ds, + int mx, int my, int button) { int x = FROMCOORD(mx), y = FROMCOORD(my), w = state->w; char buf[80]; @@ -1539,7 +1549,7 @@ static void unlink_cell(game_state *state, int si) } } -static game_state *execute_move(game_state *state, char *move) +static game_state *execute_move(const game_state *state, const char *move) { game_state *ret = NULL; int sx, sy, ex, ey, si, ei, w = state->w; @@ -1575,6 +1585,8 @@ static game_state *execute_move(game_state *state, char *move) si = sy*w+sx; ei = ey*w+ex; makelink(ret, si, ei); } else if (sscanf(move, "%c%d,%d", &c, &sx, &sy) == 3) { + int sset; + if (c != 'C' && c != 'X') return NULL; if (!INGRID(state, sx, sy)) return NULL; si = sy*w+sx; @@ -1583,11 +1595,12 @@ static game_state *execute_move(game_state *state, char *move) ret = dup_game(state); - if (c == 'C') { + sset = state->nums[si] / (state->n+1); + if (c == 'C' || (c == 'X' && sset == 0)) { /* Unlink the single cell we dragged from the board. */ unlink_cell(ret, si); } else { - int i, set, sset = state->nums[si] / (state->n+1); + int i, set; for (i = 0; i < state->n; i++) { /* Unlink all cells in the same set as the one we dragged * from the board. */ @@ -1615,8 +1628,8 @@ static game_state *execute_move(game_state *state, char *move) * Drawing routines. */ -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 tilesize, order; } ads, *ds = &ads; @@ -1627,7 +1640,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->tilesize = tilesize; assert(TILE_SIZE > 0); @@ -1706,7 +1719,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; @@ -1963,7 +1976,8 @@ static void tile_redraw(drawing *dr, game_drawstate *ds, int tx, int ty, } static void draw_drag_indicator(drawing *dr, game_drawstate *ds, - game_state *state, game_ui *ui, int validdrag) + const game_state *state, const game_ui *ui, + int validdrag) { int dir, w = ds->w, acol = COL_ARROW; int fx = FROMCOORD(ui->dx), fy = FROMCOORD(ui->dy); @@ -1978,7 +1992,7 @@ static void draw_drag_indicator(drawing *dr, game_drawstate *ds, /* Draw an arrow pointing away from/towards the origin cell. */ int ox = COORD(ui->sx) + TILE_SIZE/2, oy = COORD(ui->sy) + TILE_SIZE/2; double tana, offset; - double xdiff = fabs(ox - ui->dx), ydiff = fabs(oy - ui->dy); + double xdiff = abs(ox - ui->dx), ydiff = abs(oy - ui->dy); if (xdiff == 0) { ang = (oy > ui->dy) ? 0.0F : PI; @@ -2007,9 +2021,10 @@ static void draw_drag_indicator(drawing *dr, game_drawstate *ds, draw_arrow(dr, ui->dx, ui->dy, ARROW_HALFSZ, ang, acol, acol); } -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 x, y, i, w = ds->w, dirp, force = 0; unsigned int f; @@ -2094,11 +2109,33 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, if (state->nums[i] != ds->nums[i] || f != ds->f[i] || dirp != ds->dirp[i] || force || !ds->started) { + int sign; + { + /* + * Trivial and foolish configurable option done on + * purest whim. With this option enabled, the + * victory flash is done by rotating each square + * in the opposite direction from its immediate + * neighbours, so that they behave like a field of + * interlocking gears. With it disabled, they all + * rotate in the same direction. Choose for + * yourself which is more brain-twisting :-) + */ + static int gear_mode = -1; + if (gear_mode < 0) { + char *env = getenv("SIGNPOST_GEARS"); + gear_mode = (env && (env[0] == 'y' || env[0] == 'Y')); + } + if (gear_mode) + sign = 1 - 2 * ((x ^ y) & 1); + else + sign = 1; + } tile_redraw(dr, ds, BORDER + x * TILE_SIZE, BORDER + y * TILE_SIZE, state->dirs[i], dirp, state->nums[i], f, - angle_offset, -1); + sign * angle_offset, -1); ds->nums[i] = state->nums[i]; ds->f[i] = f; ds->dirp[i] = dirp; @@ -2117,14 +2154,14 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, if (!ds->started) ds->started = TRUE; } -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) @@ -2133,17 +2170,17 @@ static float game_flash_length(game_state *oldstate, game_state *newstate, 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; @@ -2152,7 +2189,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 ink = print_mono_colour(dr, 0); int x, y; @@ -2191,7 +2228,7 @@ static void game_print(drawing *dr, game_state *state, int tilesize) const struct game thegame = { "Signpost", "games.signpost", "signpost", default_params, - game_fetch_preset, + game_fetch_preset, NULL, decode_params, encode_params, free_params, @@ -2366,7 +2403,7 @@ int main(int argc, const char *argv[]) } } - sprintf(newseed, "%lu", time(NULL)); + sprintf(newseed, "%lu", (unsigned long) time(NULL)); seedstr = dupstr(newseed); if (id || !stdin_desc) {