return whichdir(fromi%w, fromi/w, toi%w, toi/w);
}
-static int ispointing(game_state *state, int fromx, int fromy, int tox, int toy)
+static int ispointing(const game_state *state, int fromx, int fromy,
+ int tox, int toy)
{
int w = state->w, dir = state->dirs[fromy*w+fromx];
/* Taking the number 'num', work out the gap between it and the next
* available number up or down (depending on d). Return 1 if the region
* at (x,y) will fit in that gap, or 0 otherwise. */
-static int move_couldfit(game_state *state, int num, int d, int x, int y)
+static int move_couldfit(const game_state *state, int num, int d, int x, int y)
{
int n, gap, i = y*state->w+x, sz;
return (sz > gap) ? 0 : 1;
}
-static int isvalidmove(game_state *state, int clever,
+static int isvalidmove(const game_state *state, int clever,
int fromx, int fromy, int tox, int toy)
{
int w = state->w, from = fromy*w+fromx, to = toy*w+tox;
state->prev[to] = from;
}
-static int game_can_format_as_text_now(game_params *params)
+static int game_can_format_as_text_now(const game_params *params)
{
if (params->w * params->h >= 100) return 0;
return 1;
}
-static char *game_text_format(game_state *state)
+static char *game_text_format(const game_state *state)
{
int len = state->h * 2 * (4*state->w + 1) + state->h + 2;
int x, y, i, num, n, set;
int i, ret = 1;
assert(copy->n == orig->n);
for (i = 0; i < copy->n; i++) {
- if (only_immutable && !copy->flags[i] & FLAG_IMMUTABLE) continue;
+ if (only_immutable && !(copy->flags[i] & FLAG_IMMUTABLE)) continue;
assert(copy->nums[i] >= 0);
assert(copy->nums[i] <= copy->n);
if (copy->nums[i] != orig->nums[i]) {
sfree(params);
}
-static game_params *dup_params(game_params *params)
+static game_params *dup_params(const game_params *params)
{
game_params *ret = snew(game_params);
*ret = *params; /* structure copy */
}
-static char *encode_params(game_params *params, int full)
+static char *encode_params(const game_params *params, int full)
{
char data[256];
return dupstr(data);
}
-static config_item *game_configure(game_params *params)
+static config_item *game_configure(const game_params *params)
{
config_item *ret;
char buf[80];
return ret;
}
-static game_params *custom_params(config_item *cfg)
+static game_params *custom_params(const config_item *cfg)
{
game_params *ret = snew(game_params);
return ret;
}
-static char *validate_params(game_params *params, int full)
+static char *validate_params(const game_params *params, int full)
{
- if (params->w < 2 || params->h < 2)
- return "Width and height must both be at least two";
- if (params->w == 2 && params->h == 2) /* leads to generation hang */
- return "Width and height cannot both be two";
-
+ if (params->w < 1) return "Width must be at least one";
+ if (params->h < 1) return "Height must be at least one";
+ if (full && params->w == 1 && params->h == 1)
+ /* The UI doesn't let us move these from unsolved to solved,
+ * so we disallow generating (but not playing) them. */
+ return "Width and height cannot both be one";
return NULL;
}
return state;
}
-static void dup_game_to(game_state *to, game_state *from)
+static void dup_game_to(game_state *to, const game_state *from)
{
to->completed = from->completed;
to->used_solve = from->used_solve;
memcpy(to->numsi, from->numsi, (to->n+1)*sizeof(int));
}
-static game_state *dup_game(game_state *state)
+static game_state *dup_game(const game_state *state)
{
game_state *ret = blank_game(state->w, state->h);
dup_game_to(ret, state);
sfree(state);
}
-static void unpick_desc(game_params *params, char *desc,
+static void unpick_desc(const game_params *params, const char *desc,
game_state **sout, char **mout)
{
game_state *state = blank_game(params->w, params->h);
state->dirs[taili] = 0;
nfilled = 2;
+ assert(state->n > 1);
while (nfilled < state->n) {
/* Try and expand _from_ headi; keep going if there's only one
an = cell_adj(state, headi, aidx, adir);
} while (an == 1);
+ if (nfilled == state->n) break;
+
/* Try and expand _to_ taili; keep going if there's only one
* place to go to. */
an = cell_adj(state, taili, aidx, adir);
return ret;
}
-static char *new_game_desc(game_params *params, random_state *rs,
+static char *new_game_desc(const game_params *params, random_state *rs,
char **aux, int interactive)
{
game_state *state = blank_game(params->w, params->h);
char *ret;
int headi, taili;
+ /* this shouldn't happen (validate_params), but let's play it safe */
+ if (params->w == 1 && params->h == 1) return dupstr("1a");
+
generate:
blank_game_into(state);
return ret;
}
-static char *validate_desc(game_params *params, char *desc)
+static char *validate_desc(const game_params *params, const char *desc)
{
char *ret = NULL;
if (error) return 0;
return complete;
}
-static game_state *new_game(midend *me, game_params *params, char *desc)
+static game_state *new_game(midend *me, const game_params *params,
+ const char *desc)
{
game_state *state = NULL;
return ret;
}
-static char *solve_game(game_state *state, game_state *currstate,
- char *aux, char **error)
+static char *solve_game(const game_state *state, const game_state *currstate,
+ const char *aux, char **error)
{
game_state *tosolve;
char *ret = NULL;
int dx, dy; /* pixel coords of drag posn */
};
-static game_ui *new_ui(game_state *state)
+static game_ui *new_ui(const game_state *state)
{
game_ui *ui = snew(game_ui);
sfree(ui);
}
-static char *encode_ui(game_ui *ui)
+static char *encode_ui(const game_ui *ui)
{
return NULL;
}
-static void decode_ui(game_ui *ui, char *encoding)
+static void decode_ui(game_ui *ui, const char *encoding)
{
}
-static void game_changed_state(game_ui *ui, game_state *oldstate,
- game_state *newstate)
+static void game_changed_state(game_ui *ui, const game_state *oldstate,
+ const game_state *newstate)
{
if (!oldstate->completed && newstate->completed)
ui->cshow = ui->dragging = 0;
blitter *dragb;
};
-static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
- int mx, int my, int button)
+static char *interpret_move(const game_state *state, game_ui *ui,
+ const game_drawstate *ds,
+ int mx, int my, int button)
{
int x = FROMCOORD(mx), y = FROMCOORD(my), w = state->w;
char buf[80];
}
}
-static game_state *execute_move(game_state *state, char *move)
+static game_state *execute_move(const game_state *state, const char *move)
{
game_state *ret = NULL;
int sx, sy, ex, ey, si, ei, w = state->w;
si = sy*w+sx; ei = ey*w+ex;
makelink(ret, si, ei);
} else if (sscanf(move, "%c%d,%d", &c, &sx, &sy) == 3) {
+ int sset;
+
if (c != 'C' && c != 'X') return NULL;
if (!INGRID(state, sx, sy)) return NULL;
si = sy*w+sx;
ret = dup_game(state);
- if (c == 'C') {
+ sset = state->nums[si] / (state->n+1);
+ if (c == 'C' || (c == 'X' && sset == 0)) {
/* Unlink the single cell we dragged from the board. */
unlink_cell(ret, si);
} else {
- int i, set, sset = state->nums[si] / (state->n+1);
+ int i, set;
for (i = 0; i < state->n; i++) {
/* Unlink all cells in the same set as the one we dragged
* from the board. */
* Drawing routines.
*/
-static void game_compute_size(game_params *params, int tilesize,
- int *x, int *y)
+static void game_compute_size(const game_params *params, int tilesize,
+ int *x, int *y)
{
/* Ick: fake up `ds->tilesize' for macro expansion purposes */
struct { int tilesize, order; } ads, *ds = &ads;
}
static void game_set_size(drawing *dr, game_drawstate *ds,
- game_params *params, int tilesize)
+ const game_params *params, int tilesize)
{
ds->tilesize = tilesize;
assert(TILE_SIZE > 0);
return ret;
}
-static game_drawstate *game_new_drawstate(drawing *dr, game_state *state)
+static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
{
struct game_drawstate *ds = snew(struct game_drawstate);
int i;
{
int set = num / (ds->n+1);
- if (num <= 0) return COL_B0;
- return COL_B0 + (set % 16);
+ if (num <= 0 || set == 0) return COL_B0;
+ return COL_B0 + 1 + ((set-1) % 15);
}
#define ARROW_HALFSZ (7 * TILE_SIZE / 32)
if (!empty) {
int set = (num <= 0) ? 0 : num / (ds->n+1);
+ char *p = buf;
if (set == 0 || num <= 0) {
sprintf(buf, "%d", num);
} else {
int n = num % (ds->n+1);
+ p += sizeof(buf) - 1;
- if (n == 0)
- sprintf(buf, "%c", (int)(set+'a'-1));
- else
- sprintf(buf, "%c+%d", (int)(set+'a'-1), n);
+ if (n != 0) {
+ sprintf(buf, "+%d", n); /* Just to get the length... */
+ p -= strlen(buf);
+ sprintf(p, "+%d", n);
+ } else {
+ *p = '\0';
+ }
+ do {
+ set--;
+ p--;
+ *p = (char)((set % 26)+'a');
+ set /= 26;
+ } while (set);
}
- textsz = min(2*asz, (TILE_SIZE - 2 * cb) / (int)strlen(buf));
+ textsz = min(2*asz, (TILE_SIZE - 2 * cb) / (int)strlen(p));
draw_text(dr, tx + cb, ty + TILE_SIZE/4, FONT_VARIABLE, textsz,
- ALIGN_VCENTRE | ALIGN_HLEFT, textcol, buf);
+ ALIGN_VCENTRE | ALIGN_HLEFT, textcol, p);
}
if (print_ink < 0) {
}
static void draw_drag_indicator(drawing *dr, game_drawstate *ds,
- game_state *state, game_ui *ui, int validdrag)
+ const game_state *state, const game_ui *ui,
+ int validdrag)
{
int dir, w = ds->w, acol = COL_ARROW;
int fx = FROMCOORD(ui->dx), fy = FROMCOORD(ui->dy);
/* Draw an arrow pointing away from/towards the origin cell. */
int ox = COORD(ui->sx) + TILE_SIZE/2, oy = COORD(ui->sy) + TILE_SIZE/2;
double tana, offset;
- double xdiff = fabs(ox - ui->dx), ydiff = fabs(oy - ui->dy);
+ double xdiff = abs(ox - ui->dx), ydiff = abs(oy - ui->dy);
if (xdiff == 0) {
ang = (oy > ui->dy) ? 0.0F : PI;
draw_arrow(dr, ui->dx, ui->dy, ARROW_HALFSZ, ang, acol, acol);
}
-static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
- game_state *state, int dir, game_ui *ui,
- float animtime, float flashtime)
+static void game_redraw(drawing *dr, game_drawstate *ds,
+ const game_state *oldstate, const game_state *state,
+ int dir, const game_ui *ui,
+ float animtime, float flashtime)
{
int x, y, i, w = ds->w, dirp, force = 0;
unsigned int f;
if (state->nums[i] != ds->nums[i] ||
f != ds->f[i] || dirp != ds->dirp[i] ||
force || !ds->started) {
+ int sign;
+ {
+ /*
+ * Trivial and foolish configurable option done on
+ * purest whim. With this option enabled, the
+ * victory flash is done by rotating each square
+ * in the opposite direction from its immediate
+ * neighbours, so that they behave like a field of
+ * interlocking gears. With it disabled, they all
+ * rotate in the same direction. Choose for
+ * yourself which is more brain-twisting :-)
+ */
+ static int gear_mode = -1;
+ if (gear_mode < 0) {
+ char *env = getenv("SIGNPOST_GEARS");
+ gear_mode = (env && (env[0] == 'y' || env[0] == 'Y'));
+ }
+ if (gear_mode)
+ sign = 1 - 2 * ((x ^ y) & 1);
+ else
+ sign = 1;
+ }
tile_redraw(dr, ds,
BORDER + x * TILE_SIZE,
BORDER + y * TILE_SIZE,
state->dirs[i], dirp, state->nums[i], f,
- angle_offset, -1);
+ sign * angle_offset, -1);
ds->nums[i] = state->nums[i];
ds->f[i] = f;
ds->dirp[i] = dirp;
if (!ds->started) ds->started = TRUE;
}
-static float game_anim_length(game_state *oldstate, game_state *newstate,
- int dir, game_ui *ui)
+static float game_anim_length(const game_state *oldstate,
+ const game_state *newstate, int dir, game_ui *ui)
{
return 0.0F;
}
-static float game_flash_length(game_state *oldstate, game_state *newstate,
- int dir, game_ui *ui)
+static float game_flash_length(const game_state *oldstate,
+ const game_state *newstate, int dir, game_ui *ui)
{
if (!oldstate->completed &&
newstate->completed && !newstate->used_solve)
return 0.0F;
}
-static int game_status(game_state *state)
+static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
}
-static int game_timing_state(game_state *state, game_ui *ui)
+static int game_timing_state(const game_state *state, game_ui *ui)
{
return TRUE;
}
-static void game_print_size(game_params *params, float *x, float *y)
+static void game_print_size(const game_params *params, float *x, float *y)
{
int pw, ph;
*y = ph / 100.0F;
}
-static void game_print(drawing *dr, game_state *state, int tilesize)
+static void game_print(drawing *dr, const game_state *state, int tilesize)
{
int ink = print_mono_colour(dr, 0);
int x, y;
const struct game thegame = {
"Signpost", "games.signpost", "signpost",
default_params,
- game_fetch_preset,
+ game_fetch_preset, NULL,
decode_params,
encode_params,
free_params,
}
}
- sprintf(newseed, "%lu", time(NULL));
+ sprintf(newseed, "%lu", (unsigned long) time(NULL));
seedstr = dupstr(newseed);
if (id || !stdin_desc) {