static char const *const pegs_lowertypes[] = { TYPELIST(LOWER) };
#define TYPECONFIG TYPELIST(CONFIG)
+#define FLASH_FRAME 0.13F
+
struct game_params {
int w, h;
int type;
struct game_state {
int w, h;
+ int completed;
unsigned char *grid;
};
return ret;
}
-static char *validate_params(game_params *params)
+static char *validate_params(game_params *params, int full)
{
- if (params->w <= 3 || params->h <= 3)
+ if (full && (params->w <= 3 || params->h <= 3))
return "Width and height must both be greater than three";
/*
* soluble. For the moment, therefore, I'm going to disallow
* them at any size other than the standard one.
*/
- if (params->type == TYPE_CROSS || params->type == TYPE_OCTAGON) {
+ if (full && (params->type == TYPE_CROSS || params->type == TYPE_OCTAGON)) {
if (params->w != 7 || params->h != 7)
return "This board type is only supported at 7x7";
}
printf("insufficient extent; trying again\n");
#endif
}
+#ifdef GENERATION_DIAGNOSTICS
fflush(stdout);
+#endif
}
/* ----------------------------------------------------------------------
state->w = w;
state->h = h;
+ state->completed = 0;
state->grid = snewn(w*h, unsigned char);
for (i = 0; i < w*h; i++)
state->grid[i] = (desc[i] == 'P' ? GRID_PEG :
ret->w = state->w;
ret->h = state->h;
+ ret->completed = state->completed;
ret->grid = snewn(w*h, unsigned char);
memcpy(ret->grid, state->grid, w*h);
int w, h;
unsigned char *grid;
int started;
+ int bgcolour;
};
static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
ret->grid[my*w+mx] = GRID_HOLE;
ret->grid[ty*w+tx] = GRID_PEG;
+ /*
+ * Opinion varies on whether getting to a single peg counts as
+ * completing the game, or whether that peg has to be at a
+ * specific location (central in the classic cross game, for
+ * instance). For now we take the former, rather lax position.
+ */
+ if (!ret->completed) {
+ int count = 0, i;
+ for (i = 0; i < w*h; i++)
+ if (ret->grid[i] == GRID_PEG)
+ count++;
+ if (count == 1)
+ ret->completed = 1;
+ }
+
return ret;
}
return NULL;
* 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)
{
- double tsx, tsy, ts;
- /*
- * Each window dimension equals the tile size times one more
- * than the grid dimension (the border is half the width of the
- * tiles).
- */
- tsx = (double)*x / ((double)params->w + 1.0);
- tsy = (double)*y / ((double)params->h + 1.0);
- ts = min(tsx, tsy);
- if (expand)
- ds->tilesize = (int)(ts + 0.5);
- else
- ds->tilesize = min((int)ts, PREFERRED_TILE_SIZE);
+ /* Ick: fake up `ds->tilesize' for macro expansion purposes */
+ struct { int tilesize; } ads, *ds = &ads;
+ ads.tilesize = tilesize;
*x = TILESIZE * params->w + 2 * BORDER;
*y = TILESIZE * params->h + 2 * BORDER;
+}
+
+static void game_set_size(game_drawstate *ds, game_params *params,
+ int tilesize)
+{
+ ds->tilesize = tilesize;
+
+ assert(TILESIZE > 0);
if (ds->drag_background)
blitter_free(ds->drag_background);
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]);
- /*
- * 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;
- }
+ game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
ret[COL_PEG * 3 + 0] = 0.0F;
ret[COL_PEG * 3 + 1] = 0.0F;
memset(ds->grid, 255, w*h);
ds->started = FALSE;
+ ds->bgcolour = -1;
return ds;
}
}
static void draw_tile(frontend *fe, game_drawstate *ds,
- int x, int y, int v, int erasebg)
+ int x, int y, int v, int bgcolour)
{
- if (erasebg) {
- draw_rect(fe, x, y, TILESIZE, TILESIZE, COL_BACKGROUND);
+ if (bgcolour >= 0) {
+ draw_rect(fe, x, y, TILESIZE, TILESIZE, bgcolour);
}
if (v == GRID_HOLE) {
{
int w = state->w, h = state->h;
int x, y;
+ int bgcolour;
+
+ if (flashtime > 0) {
+ int frame = (int)(flashtime / FLASH_FRAME);
+ bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT);
+ } else
+ bgcolour = COL_BACKGROUND;
/*
* Erase the sprite currently being dragged, if any.
*/
if (ui->dragging && ui->sx == x && ui->sy == y && v == GRID_PEG)
v = GRID_HOLE;
- if (v != ds->grid[y*w+x] && v != GRID_OBST) {
- draw_tile(fe, ds, COORD(x), COORD(y), v, TRUE);
+ if (v != GRID_OBST &&
+ (bgcolour != ds->bgcolour || /* always redraw when flashing */
+ v != ds->grid[y*w+x])) {
+ draw_tile(fe, ds, COORD(x), COORD(y), v, bgcolour);
}
}
ds->dragx = ui->dx - TILESIZE/2;
ds->dragy = ui->dy - TILESIZE/2;
blitter_save(fe, ds->drag_background, ds->dragx, ds->dragy);
- draw_tile(fe, ds, ds->dragx, ds->dragy, GRID_PEG, FALSE);
+ draw_tile(fe, ds, ds->dragx, ds->dragy, GRID_PEG, -1);
}
+
+ ds->bgcolour = bgcolour;
}
static float game_anim_length(game_state *oldstate, game_state *newstate,
static float game_flash_length(game_state *oldstate, game_state *newstate,
int dir, game_ui *ui)
{
- return 0.0F;
+ if (!oldstate->completed && newstate->completed)
+ return 2 * FLASH_FRAME;
+ else
+ return 0.0F;
}
static int game_wants_statusbar(void)
game_changed_state,
interpret_move,
execute_move,
- game_size,
+ PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
game_colours,
game_new_drawstate,
game_free_drawstate,