X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/d108c3429e902a82bdb703ba6f36df549782bba4..11c44cf574b43cec747d490cb264829a535ed42a:/fifteen.c diff --git a/fifteen.c b/fifteen.c index 338bdfb..2788604 100644 --- a/fifteen.c +++ b/fifteen.c @@ -6,11 +6,13 @@ #include #include #include +#include #include #include "puzzles.h" const char *const game_name = "Fifteen"; +const int game_can_configure = TRUE; #define TILE_SIZE 48 #define BORDER (TILE_SIZE / 2) @@ -18,8 +20,8 @@ const char *const game_name = "Fifteen"; #define COORD(x) ( (x) * TILE_SIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 ) -#define ANIM_TIME 0.1F -#define FLASH_FRAME 0.1F +#define ANIM_TIME 0.13F +#define FLASH_FRAME 0.13F #define X(state, i) ( (i) % (state)->w ) #define Y(state, i) ( (i) / (state)->w ) @@ -71,6 +73,74 @@ game_params *dup_params(game_params *params) return ret; } +game_params *decode_params(char const *string) +{ + game_params *ret = default_params(); + + ret->w = ret->h = atoi(string); + while (*string && isdigit(*string)) string++; + if (*string == 'x') { + string++; + ret->h = atoi(string); + } + + return ret; +} + +char *encode_params(game_params *params) +{ + char data[256]; + + sprintf(data, "%dx%d", params->w, params->h); + + return dupstr(data); +} + +config_item *game_configure(game_params *params) +{ + config_item *ret; + char buf[80]; + + ret = snewn(3, config_item); + + ret[0].name = "Width"; + ret[0].type = C_STRING; + sprintf(buf, "%d", params->w); + ret[0].sval = dupstr(buf); + ret[0].ival = 0; + + ret[1].name = "Height"; + ret[1].type = C_STRING; + sprintf(buf, "%d", params->h); + ret[1].sval = dupstr(buf); + ret[1].ival = 0; + + ret[2].name = NULL; + ret[2].type = C_END; + ret[2].sval = NULL; + ret[2].ival = 0; + + return ret; +} + +game_params *custom_params(config_item *cfg) +{ + game_params *ret = snew(game_params); + + ret->w = atoi(cfg[0].sval); + ret->h = atoi(cfg[1].sval); + + return ret; +} + +char *validate_params(game_params *params) +{ + if (params->w < 2 && params->h < 2) + return "Width and height must both be at least two"; + + return NULL; +} + int perm_parity(int *perm, int n) { int i, j, ret; @@ -85,7 +155,7 @@ int perm_parity(int *perm, int n) return ret; } -char *new_game_seed(game_params *params) +char *new_game_seed(game_params *params, random_state *rs) { int gap, n, i, x; int x1, x2, p1, p2, parity; @@ -103,7 +173,7 @@ char *new_game_seed(game_params *params) used[i] = FALSE; } - gap = rand_upto(n); + gap = random_upto(rs, n); tiles[gap] = 0; used[0] = TRUE; @@ -111,7 +181,7 @@ char *new_game_seed(game_params *params) * Place everything else except the last two tiles. */ for (x = 0, i = n-1; i > 2; i--) { - int k = rand_upto(i); + int k = random_upto(rs, i); int j; for (j = 0; j < n; j++) @@ -153,15 +223,16 @@ char *new_game_seed(game_params *params) * Determine the required parity of the overall permutation. * This is the XOR of: * - * - The chessboard parity ((x^y)&1) of the gap square. The - * bottom right, and therefore also the top left, count as - * even. + * - The chessboard parity ((x^y)&1) of the gap square. The + * bottom right counts as even. * * - The parity of n. (The target permutation is 1,...,n-1,0 * rather than 0,...,n-1; this is a cyclic permutation of * the starting point and hence is odd iff n is even.) */ - parity = (X(params, gap) ^ Y(params, gap) ^ (n+1)) & 1; + parity = ((X(params, gap) - (params->w-1)) ^ + (Y(params, gap) - (params->h-1)) ^ + (n+1)) & 1; /* * Try the last two tiles one way round. If that fails, swap @@ -199,6 +270,57 @@ char *new_game_seed(game_params *params) return ret; } +char *validate_seed(game_params *params, char *seed) +{ + char *p, *err; + int i, area; + int *used; + + area = params->w * params->h; + p = seed; + err = NULL; + + used = snewn(area, int); + for (i = 0; i < area; i++) + used[i] = FALSE; + + for (i = 0; i < area; i++) { + char *q = p; + int n; + + if (*p < '0' || *p > '9') { + err = "Not enough numbers in string"; + goto leave; + } + while (*p >= '0' && *p <= '9') + p++; + if (i < area-1 && *p != ',') { + err = "Expected comma after number"; + goto leave; + } + else if (i == area-1 && *p) { + err = "Excess junk at end of string"; + goto leave; + } + n = atoi(q); + if (n < 0 || n >= area) { + err = "Number out of range"; + goto leave; + } + if (used[n]) { + err = "Number used twice"; + goto leave; + } + used[n] = TRUE; + + if (*p) p++; /* eat comma */ + } + + leave: + sfree(used); + return err; +} + game_state *new_game(game_params *params, char *seed) { game_state *state = snew(game_state); @@ -251,7 +373,16 @@ void free_game(game_state *state) sfree(state); } -game_state *make_move(game_state *from, int x, int y, int button) +game_ui *new_ui(game_state *state) +{ + return NULL; +} + +void free_ui(game_ui *ui) +{ +} + +game_state *make_move(game_state *from, game_ui *ui, int x, int y, int button) { int gx, gy, dx, dy, ux, uy, up, p; game_state *ret; @@ -421,7 +552,8 @@ static void draw_tile(frontend *fe, game_state *state, int x, int y, } void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, - game_state *state, float animtime, float flashtime) + game_state *state, game_ui *ui, + float animtime, float flashtime) { int i, pass, bgcolour;