static char *validate_params(game_params *params)
{
- if (params->w < 2 && params->h < 2)
+ if (params->w < 2 || params->h < 2)
return "Width and height must both be at least two";
return NULL;
}
static char *new_game_desc(game_params *params, random_state *rs,
- game_aux_info **aux)
+ game_aux_info **aux, int interactive)
{
int stop, n, i, x;
int x1, x2, p1, p2;
tiles = snewn(n, int);
if (params->movetarget) {
- int prevstart = -1, prevoffset = -1, prevdirection = 0, nrepeats = 0;
+ int prevoffset = -1;
+ int max = (params->w > params->h ? params->w : params->h);
+ int *prevmoves = snewn(max, int);
/*
* Shuffle the old-fashioned way, by making a series of
tiles[i] = i;
for (i = 0; i < params->movetarget; i++) {
- int start, offset, len, direction;
+ int start, offset, len, direction, index;
int j, tmp;
/*
if (j < params->w) {
/* Column. */
+ index = j;
start = j;
offset = params->w;
len = params->h;
} else {
/* Row. */
- start = (j - params->w) * params->w;
+ index = j - params->w;
+ start = index * params->w;
offset = 1;
len = params->w;
}
direction = -1 + 2 * random_upto(rs, 2);
/*
- * To at least _try_ to avoid boring cases, check that
- * this move doesn't directly undo the previous one, or
- * repeat it so many times as to turn it into fewer
- * moves.
+ * To at least _try_ to avoid boring cases, check
+ * that this move doesn't directly undo a previous
+ * one, or repeat it so many times as to turn it
+ * into fewer moves in the opposite direction. (For
+ * example, in a row of length 4, we're allowed to
+ * move it the same way twice, but not three
+ * times.)
+ *
+ * We track this for each individual row/column,
+ * and clear all the counters as soon as a
+ * perpendicular move is made. This isn't perfect
+ * (it _can't_ guaranteeably be perfect - there
+ * will always come a move count beyond which a
+ * shorter solution will be possible than the one
+ * which constructed the position) but it should
+ * sort out all the obvious cases.
*/
- if (start == prevstart && offset == prevoffset) {
- if (direction == -prevdirection)
- continue; /* inverse of previous move */
- else if (2 * (nrepeats+1) > len)
- continue; /* previous move repeated too often */
- }
+ if (offset == prevoffset) {
+ tmp = prevmoves[index] + direction;
+ if (abs(2*tmp) > len || abs(tmp) < abs(prevmoves[index]))
+ continue;
+ }
/* If we didn't `continue', we've found an OK move to make. */
+ if (offset != prevoffset) {
+ int i;
+ for (i = 0; i < max; i++)
+ prevmoves[i] = 0;
+ prevoffset = offset;
+ }
+ prevmoves[index] += direction;
break;
}
/*
- * Now save the move into the `prev' variables.
- */
- if (start == prevstart && offset == prevoffset) {
- nrepeats++;
- } else {
- prevstart = start;
- prevoffset = offset;
- prevdirection = direction;
- nrepeats = 1;
- }
-
- /*
- * And make it.
+ * Make the move.
*/
if (direction < 0) {
start += (len-1) * offset;
tiles[start + (len-1) * offset] = tmp;
}
+ sfree(prevmoves);
+
} else {
used = snewn(n, int);
static void free_game(game_state *state)
{
+ sfree(state->tiles);
sfree(state);
}
{
}
-static game_state *make_move(game_state *from, game_ui *ui,
- int x, int y, int button)
-{
+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;
bgcolour = COL_BACKGROUND;
if (!ds->started) {
- int coords[6];
+ int coords[10];
draw_rect(fe, 0, 0,
TILE_SIZE * state->w + 2 * BORDER,
coords[1] = COORD(state->h) + HIGHLIGHT_WIDTH - 1;
coords[2] = COORD(state->w) + HIGHLIGHT_WIDTH - 1;
coords[3] = COORD(0) - HIGHLIGHT_WIDTH;
- coords[4] = COORD(0) - HIGHLIGHT_WIDTH;
- coords[5] = COORD(state->h) + HIGHLIGHT_WIDTH - 1;
- draw_polygon(fe, coords, 3, TRUE, COL_HIGHLIGHT);
- draw_polygon(fe, coords, 3, FALSE, COL_HIGHLIGHT);
+ coords[4] = coords[2] - TILE_SIZE;
+ coords[5] = coords[3] + TILE_SIZE;
+ coords[8] = COORD(0) - HIGHLIGHT_WIDTH;
+ 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);
coords[1] = COORD(0) - HIGHLIGHT_WIDTH;
coords[0] = COORD(0) - HIGHLIGHT_WIDTH;
- draw_polygon(fe, coords, 3, TRUE, COL_LOWLIGHT);
- draw_polygon(fe, coords, 3, FALSE, COL_LOWLIGHT);
+ draw_polygon(fe, coords, 5, TRUE, COL_LOWLIGHT);
+ draw_polygon(fe, coords, 5, FALSE, COL_LOWLIGHT);
/*
* Arrows for making moves.
return TRUE;
}
+static int game_timing_state(game_state *state)
+{
+ return TRUE;
+}
+
#ifdef COMBINED
#define thegame sixteen
#endif
game_anim_length,
game_flash_length,
game_wants_statusbar,
+ FALSE, game_timing_state,
+ 0, /* mouse_priorities */
};