X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/4a29930e1b9db1332c93244365db1642fcfac1cd..aafaa7fbd122f8109c1a7d99faf74d4892bf22de:/sixteen.c diff --git a/sixteen.c b/sixteen.c index 9e93b2f..457e9c9 100644 --- a/sixteen.c +++ b/sixteen.c @@ -100,7 +100,7 @@ static void decode_params(game_params *ret, char const *string) { ret->w = ret->h = atoi(string); ret->movetarget = 0; - while (*string && isdigit(*string)) string++; + while (*string && isdigit((unsigned char)*string)) string++; if (*string == 'x') { string++; ret->h = atoi(string); @@ -172,7 +172,7 @@ static game_params *custom_params(config_item *cfg) return ret; } -static char *validate_params(game_params *params) +static char *validate_params(game_params *params, int full) { if (params->w < 2 || params->h < 2) return "Width and height must both be at least two"; @@ -195,7 +195,7 @@ static int perm_parity(int *perm, int n) } static char *new_game_desc(game_params *params, random_state *rs, - game_aux_info **aux, int interactive) + char **aux, int interactive) { int stop, n, i, x; int x1, x2, p1, p2; @@ -398,11 +398,6 @@ static char *new_game_desc(game_params *params, random_state *rs, return ret; } -static void game_free_aux_info(game_aux_info *aux) -{ - assert(!"Shouldn't happen"); -} - static char *validate_desc(game_params *params, char *desc) { @@ -510,25 +505,10 @@ static void free_game(game_state *state) sfree(state); } -static game_state *solve_game(game_state *state, game_state *currstate, - game_aux_info *aux, char **error) +static char *solve_game(game_state *state, game_state *currstate, + char *aux, char **error) { - game_state *ret = dup_game(state); - int i; - - /* - * Simply replace the grid with a solved one. For this game, - * this isn't a useful operation for actually telling the user - * what they should have done, but it is useful for - * conveniently being able to get hold of a clean state from - * which to practise manoeuvres. - */ - for (i = 0; i < ret->n; i++) - ret->tiles[i] = i+1; - ret->used_solve = ret->just_used_solve = TRUE; - ret->completed = ret->movecount = 1; - - return ret; + return dupstr("S"); } static char *game_text_format(game_state *state) @@ -579,6 +559,15 @@ static void free_ui(game_ui *ui) { } +static char *encode_ui(game_ui *ui) +{ + return NULL; +} + +static void decode_ui(game_ui *ui, char *encoding) +{ +} + static void game_changed_state(game_ui *ui, game_state *oldstate, game_state *newstate) { @@ -591,11 +580,11 @@ struct game_drawstate { int tilesize; }; -static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds, - int x, int y, int button) { - int cx, cy; - int dx, dy, tx, ty, n; - game_state *ret; +static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, + int x, int y, int button) +{ + int cx, cy, dx, dy; + char buf[80]; button &= ~MOD_MASK; if (button != LEFT_BUTTON && button != RIGHT_BUTTON) @@ -603,38 +592,81 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds, cx = FROMCOORD(x); cy = FROMCOORD(y); - if (cx == -1 && cy >= 0 && cy < from->h) - n = from->w, dx = +1, dy = 0; - else if (cx == from->w && cy >= 0 && cy < from->h) - n = from->w, dx = -1, dy = 0; - else if (cy == -1 && cx >= 0 && cx < from->w) - n = from->h, dy = +1, dx = 0; - else if (cy == from->h && cx >= 0 && cx < from->w) - n = from->h, dy = -1, dx = 0; + if (cx == -1 && cy >= 0 && cy < state->h) + dx = -1, dy = 0; + else if (cx == state->w && cy >= 0 && cy < state->h) + dx = +1, dy = 0; + else if (cy == -1 && cx >= 0 && cx < state->w) + dy = -1, dx = 0; + else if (cy == state->h && cx >= 0 && cx < state->w) + dy = +1, dx = 0; else return NULL; /* invalid click location */ /* reverse direction if right hand button is pressed */ - if (button == RIGHT_BUTTON) - { - dx = -dx; if (dx) cx = from->w - 1 - cx; - dy = -dy; if (dy) cy = from->h - 1 - cy; + if (button == RIGHT_BUTTON) { + dx = -dx; + dy = -dy; } + if (dx) + sprintf(buf, "R%d,%d", cy, dx); + else + sprintf(buf, "C%d,%d", cx, dy); + return dupstr(buf); +} + +static game_state *execute_move(game_state *from, char *move) +{ + int cx, cy, dx, dy; + int tx, ty, n; + game_state *ret; + + if (!strcmp(move, "S")) { + int i; + + ret = dup_game(from); + + /* + * Simply replace the grid with a solved one. For this game, + * this isn't a useful operation for actually telling the user + * what they should have done, but it is useful for + * conveniently being able to get hold of a clean state from + * which to practise manoeuvres. + */ + for (i = 0; i < ret->n; i++) + ret->tiles[i] = i+1; + ret->used_solve = ret->just_used_solve = TRUE; + ret->completed = ret->movecount = 1; + + return ret; + } + + if (move[0] == 'R' && sscanf(move+1, "%d,%d", &cy, &dx) == 2 && + cy >= 0 && cy < from->h) { + cx = dy = 0; + n = from->w; + } else if (move[0] == 'C' && sscanf(move+1, "%d,%d", &cx, &dy) == 2 && + cx >= 0 && cx < from->w) { + cy = dx = 0; + n = from->h; + } else + return NULL; + ret = dup_game(from); ret->just_used_solve = FALSE; /* zero this in a hurry */ do { - cx += dx; - cy += dy; - tx = (cx + dx + from->w) % from->w; - ty = (cy + dy + from->h) % from->h; + tx = (cx - dx + from->w) % from->w; + ty = (cy - dy + from->h) % from->h; ret->tiles[C(ret, cx, cy)] = from->tiles[C(from, tx, ty)]; + cx = tx; + cy = ty; } while (--n > 0); ret->movecount++; - ret->last_movement_sense = -(dx+dy); + ret->last_movement_sense = dx+dy; /* * See if the game has been completed. @@ -653,54 +685,32 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds, * Drawing routines. */ -static void game_size(game_params *params, game_drawstate *ds, - int *x, int *y, int expand) +static void game_compute_size(game_params *params, int tilesize, + int *x, int *y) { - int tsx, tsy, ts; - /* - * Each window dimension equals the tile size times two more - * than the grid dimension (the border is the same size as the - * tiles). - */ - tsx = *x / (params->w + 2); - tsy = *y / (params->h + 2); - ts = min(tsx, tsy); - - if (expand) - ds->tilesize = ts; - else - ds->tilesize = min(ts, PREFERRED_TILE_SIZE); + /* Ick: fake up `ds->tilesize' for macro expansion purposes */ + struct { int tilesize; } ads, *ds = &ads; + ads.tilesize = tilesize; *x = TILE_SIZE * params->w + 2 * BORDER; *y = TILE_SIZE * params->h + 2 * BORDER; } +static void game_set_size(game_drawstate *ds, game_params *params, + int tilesize) +{ + ds->tilesize = tilesize; +} + static float *game_colours(frontend *fe, game_state *state, int *ncolours) { float *ret = snewn(3 * NCOLOURS, float); int i; - float max; - frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); + game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); - /* - * Drop the background colour so that the highlight is - * noticeably brighter than it while still being under 1. - */ - max = ret[COL_BACKGROUND*3]; - for (i = 1; i < 3; i++) - if (ret[COL_BACKGROUND*3+i] > max) - max = ret[COL_BACKGROUND*3+i]; - if (max * 1.2F > 1.0F) { - for (i = 0; i < 3; i++) - ret[COL_BACKGROUND*3+i] /= (max * 1.2F); - } - - for (i = 0; i < 3; i++) { - ret[COL_HIGHLIGHT * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 1.2F; - ret[COL_LOWLIGHT * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.8F; + for (i = 0; i < 3; i++) ret[COL_TEXT * 3 + i] = 0.0; - } *ncolours = NCOLOURS; return ret; @@ -746,13 +756,11 @@ static void draw_tile(frontend *fe, game_drawstate *ds, coords[3] = y; coords[4] = x; coords[5] = y + TILE_SIZE - 1; - draw_polygon(fe, coords, 3, TRUE, COL_LOWLIGHT); - draw_polygon(fe, coords, 3, FALSE, COL_LOWLIGHT); + draw_polygon(fe, coords, 3, COL_LOWLIGHT, COL_LOWLIGHT); coords[0] = x; coords[1] = y; - draw_polygon(fe, coords, 3, TRUE, COL_HIGHLIGHT); - draw_polygon(fe, coords, 3, FALSE, COL_HIGHLIGHT); + draw_polygon(fe, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); draw_rect(fe, x + HIGHLIGHT_WIDTH, y + HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH, @@ -784,8 +792,7 @@ static void draw_arrow(frontend *fe, game_drawstate *ds, POINT(5, 3 * TILE_SIZE / 8, TILE_SIZE / 2); /* left concave */ POINT(6, TILE_SIZE / 4, TILE_SIZE / 2); /* left corner */ - draw_polygon(fe, coords, 7, TRUE, COL_LOWLIGHT); - draw_polygon(fe, coords, 7, FALSE, COL_TEXT); + draw_polygon(fe, coords, 7, COL_LOWLIGHT, COL_TEXT); } static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, @@ -823,13 +830,11 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, coords[9] = COORD(state->h) + HIGHLIGHT_WIDTH - 1; coords[6] = coords[8] + TILE_SIZE; coords[7] = coords[9] - TILE_SIZE; - draw_polygon(fe, coords, 5, TRUE, COL_HIGHLIGHT); - draw_polygon(fe, coords, 5, FALSE, COL_HIGHLIGHT); + draw_polygon(fe, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT); coords[1] = COORD(0) - HIGHLIGHT_WIDTH; coords[0] = COORD(0) - HIGHLIGHT_WIDTH; - draw_polygon(fe, coords, 5, TRUE, COL_LOWLIGHT); - draw_polygon(fe, coords, 5, FALSE, COL_LOWLIGHT); + draw_polygon(fe, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT); /* * Arrows for making moves. @@ -1003,7 +1008,7 @@ static int game_wants_statusbar(void) return TRUE; } -static int game_timing_state(game_state *state) +static int game_timing_state(game_state *state, game_ui *ui) { return TRUE; } @@ -1023,7 +1028,6 @@ const struct game thegame = { TRUE, game_configure, custom_params, validate_params, new_game_desc, - game_free_aux_info, validate_desc, new_game, dup_game, @@ -1032,9 +1036,12 @@ const struct game thegame = { TRUE, game_text_format, new_ui, free_ui, + encode_ui, + decode_ui, game_changed_state, - make_move, - game_size, + interpret_move, + execute_move, + PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate,