#include "puzzles.h"
const char *const game_name = "Sixteen";
+const int game_can_configure = TRUE;
#define TILE_SIZE 48
#define BORDER TILE_SIZE /* big border to fill with arrows */
#define COORD(x) ( (x) * TILE_SIZE + BORDER )
#define FROMCOORD(x) ( ((x) - BORDER + 2*TILE_SIZE) / TILE_SIZE - 2 )
-#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 )
int *tiles;
int completed;
int movecount;
+ int last_movement_sense;
};
game_params *default_params(void)
return ret;
}
+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;
return ret;
}
-char *new_game_seed(game_params *params)
+char *new_game_seed(game_params *params, random_state *rs)
{
int stop, n, i, x;
int x1, x2, p1, p2;
* Place everything except (possibly) the last two tiles.
*/
for (x = 0, i = n; i > stop; i--) {
- int k = i > 1 ? rand_upto(i) : 0;
+ int k = i > 1 ? random_upto(rs, i) : 0;
int j;
for (j = 0; j < n; j++)
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 < 1 || n > area) {
+ err = "Number out of range";
+ goto leave;
+ }
+ if (used[n-1]) {
+ err = "Number used twice";
+ goto leave;
+ }
+ used[n-1] = 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);
assert(!*p);
state->completed = state->movecount = 0;
+ state->last_movement_sense = 0;
return state;
}
memcpy(ret->tiles, state->tiles, state->w * state->h * sizeof(int));
ret->completed = state->completed;
ret->movecount = state->movecount;
+ ret->last_movement_sense = state->last_movement_sense;
return ret;
}
ret->movecount++;
+ ret->last_movement_sense = -(dx+dy);
+
/*
* See if the game has been completed.
*/
y0 = COORD(Y(state, j));
dx = (x1 - x0);
- if (abs(dx) > TILE_SIZE) {
+ if (dx != 0 &&
+ dx != TILE_SIZE * state->last_movement_sense) {
dx = (dx < 0 ? dx + TILE_SIZE * state->w :
dx - TILE_SIZE * state->w);
assert(abs(dx) == TILE_SIZE);
}
dy = (y1 - y0);
- if (abs(dy) > TILE_SIZE) {
+ if (dy != 0 &&
+ dy != TILE_SIZE * state->last_movement_sense) {
dy = (dy < 0 ? dy + TILE_SIZE * state->h :
dy - TILE_SIZE * state->h);
assert(abs(dy) == TILE_SIZE);