case 2: w = 11, h = 11; break;
case 3: w = 13, h = 13; break;
case 4: w = 15, h = 15; break;
-#ifndef SLOW_SYSTEM
case 5: w = 17, h = 17; break;
case 6: w = 19, h = 19; break;
-#endif
default: return FALSE;
}
return ret;
}
-static char *validate_params(game_params *params)
+static char *validate_params(game_params *params, int full)
{
if (params->w <= 0 || params->h <= 0)
return "Width and height must both be greater than zero";
}
static int rect_solver(int w, int h, int nrects, struct numberdata *numbers,
- game_state *result, random_state *rs)
+ unsigned char *hedge, unsigned char *vedge,
+ random_state *rs)
{
struct rectlist *rectpositions;
int *overlaps, *rectbyplace, *workspace;
int placement;
int number;
} *rpns = NULL;
- int nrpns = 0, rpnsize = 0;
+ size_t nrpns = 0, rpnsize = 0;
int j;
for (i = 0; i < nrects; i++) {
assert(rectpositions[i].n > 0);
if (rectpositions[i].n > 1) {
ret = FALSE;
- } else if (result) {
- /*
- * Place the rectangle in its only possible position.
- */
- int x, y;
- struct rect *r = &rectpositions[i].rects[0];
-
- for (y = 0; y < r->h; y++) {
- if (r->x > 0)
- vedge(result, r->x, r->y+y) = 1;
- if (r->x+r->w < result->w)
- vedge(result, r->x+r->w, r->y+y) = 1;
- }
- for (x = 0; x < r->w; x++) {
- if (r->y > 0)
- hedge(result, r->x+x, r->y) = 1;
- if (r->y+r->h < result->h)
- hedge(result, r->x+x, r->y+r->h) = 1;
- }
+ } else if (hedge && vedge) {
+ /*
+ * Place the rectangle in its only possible position.
+ */
+ int x, y;
+ struct rect *r = &rectpositions[i].rects[0];
+
+ for (y = 0; y < r->h; y++) {
+ if (r->x > 0)
+ vedge[(r->y+y) * w + r->x] = 1;
+ if (r->x+r->w < w)
+ vedge[(r->y+y) * w + r->x+r->w] = 1;
+ }
+ for (x = 0; x < r->w; x++) {
+ if (r->y > 0)
+ hedge[r->y * w + r->x+x] = 1;
+ if (r->y+r->h < h)
+ hedge[(r->y+r->h) * w + r->x+x] = 1;
+ }
}
}
}
#endif
-struct game_aux_info {
- int w, h;
- unsigned char *vedge; /* (w+1) x h */
- unsigned char *hedge; /* w x (h+1) */
-};
-
static char *new_game_desc(game_params *params, random_state *rs,
- game_aux_info **aux, int interactive)
+ char **aux, int interactive)
{
int *grid, *numbers = NULL;
int x, y, y2, y2last, yx, run, i, nsquares;
if (params->unique)
ret = rect_solver(params->w, params->h, nnumbers, nd,
- NULL, rs);
+ NULL, NULL, rs);
else
ret = TRUE; /* allow any number placement at all */
}
/*
- * Store the rectangle data in the game_aux_info.
+ * Store the solution in aux.
*/
{
- game_aux_info *ai = snew(game_aux_info);
+ char *ai;
+ int len;
+
+ len = 2 + (params->w-1)*params->h + (params->h-1)*params->w;
+ ai = snewn(len, char);
- ai->w = params->w;
- ai->h = params->h;
- ai->vedge = snewn(ai->w * ai->h, unsigned char);
- ai->hedge = snewn(ai->w * ai->h, unsigned char);
+ ai[0] = 'S';
+
+ p = ai+1;
for (y = 0; y < params->h; y++)
- for (x = 1; x < params->w; x++) {
- vedge(ai, x, y) =
- index(params, grid, x, y) != index(params, grid, x-1, y);
- }
+ for (x = 1; x < params->w; x++)
+ *p++ = (index(params, grid, x, y) !=
+ index(params, grid, x-1, y) ? '1' : '0');
+
for (y = 1; y < params->h; y++)
- for (x = 0; x < params->w; x++) {
- hedge(ai, x, y) =
- index(params, grid, x, y) != index(params, grid, x, y-1);
- }
+ for (x = 0; x < params->w; x++)
+ *p++ = (index(params, grid, x, y) !=
+ index(params, grid, x, y-1) ? '1' : '0');
+
+ assert(p - ai == len-1);
+ *p = '\0';
*aux = ai;
}
return desc;
}
-static void game_free_aux_info(game_aux_info *ai)
-{
- sfree(ai->vedge);
- sfree(ai->hedge);
- sfree(ai);
-}
-
static char *validate_desc(game_params *params, char *desc)
{
int area = params->w * params->h;
sfree(state);
}
-static game_state *solve_game(game_state *state, game_state *currstate,
- game_aux_info *ai, char **error)
+static char *solve_game(game_state *state, game_state *currstate,
+ char *ai, char **error)
{
- game_state *ret;
+ unsigned char *vedge, *hedge;
+ int x, y, len;
+ char *ret, *p;
+ int i, j, n;
+ struct numberdata *nd;
- if (!ai) {
- int i, j, n;
- struct numberdata *nd;
-
- /*
- * Attempt the in-built solver.
- */
-
- /* Set up each number's (very short) candidate position list. */
- for (i = n = 0; i < state->h * state->w; i++)
- if (state->grid[i])
- n++;
-
- nd = snewn(n, struct numberdata);
-
- for (i = j = 0; i < state->h * state->w; i++)
- if (state->grid[i]) {
- nd[j].area = state->grid[i];
- nd[j].npoints = 1;
- nd[j].points = snewn(1, struct point);
- nd[j].points[0].x = i % state->w;
- nd[j].points[0].y = i / state->w;
- j++;
- }
+ if (ai)
+ return dupstr(ai);
- assert(j == n);
+ /*
+ * Attempt the in-built solver.
+ */
- ret = dup_game(state);
- ret->cheated = TRUE;
+ /* Set up each number's (very short) candidate position list. */
+ for (i = n = 0; i < state->h * state->w; i++)
+ if (state->grid[i])
+ n++;
+
+ nd = snewn(n, struct numberdata);
+
+ for (i = j = 0; i < state->h * state->w; i++)
+ if (state->grid[i]) {
+ nd[j].area = state->grid[i];
+ nd[j].npoints = 1;
+ nd[j].points = snewn(1, struct point);
+ nd[j].points[0].x = i % state->w;
+ nd[j].points[0].y = i / state->w;
+ j++;
+ }
- rect_solver(state->w, state->h, n, nd, ret, NULL);
+ assert(j == n);
- /*
- * Clean up.
- */
- for (i = 0; i < n; i++)
- sfree(nd[i].points);
- sfree(nd);
+ vedge = snewn(state->w * state->h, unsigned char);
+ hedge = snewn(state->w * state->h, unsigned char);
+ memset(vedge, 0, state->w * state->h);
+ memset(hedge, 0, state->w * state->h);
- return ret;
- }
+ rect_solver(state->w, state->h, n, nd, hedge, vedge, NULL);
- assert(state->w == ai->w);
- assert(state->h == ai->h);
+ /*
+ * Clean up.
+ */
+ for (i = 0; i < n; i++)
+ sfree(nd[i].points);
+ sfree(nd);
- ret = dup_game(state);
- memcpy(ret->vedge, ai->vedge, ai->w * ai->h * sizeof(unsigned char));
- memcpy(ret->hedge, ai->hedge, ai->w * ai->h * sizeof(unsigned char));
- ret->cheated = TRUE;
+ len = 2 + (state->w-1)*state->h + (state->h-1)*state->w;
+ ret = snewn(len, char);
+
+ p = ret;
+ *p++ = 'S';
+ for (y = 0; y < state->h; y++)
+ for (x = 1; x < state->w; x++)
+ *p++ = vedge[y*state->w+x] ? '1' : '0';
+ for (y = 1; y < state->h; y++)
+ for (x = 0; x < state->w; x++)
+ *p++ = hedge[y*state->w+x] ? '1' : '0';
+ *p++ = '\0';
+ assert(p - ret == len);
+
+ sfree(vedge);
+ sfree(hedge);
return ret;
}
sfree(ui);
}
+static char *encode_ui(game_ui *ui)
+{
+ return NULL;
+}
+
+static void decode_ui(game_ui *ui, char *encoding)
+{
+}
+
static void coord_round(float x, float y, int *xr, int *yr)
{
float xs, ys, xv, yv, dx, dy, dist;
}
}
-static void ui_draw_rect(game_state *state, game_ui *ui,
- unsigned char *hedge, unsigned char *vedge, int c)
+/*
+ * Returns TRUE if it has made any change to the grid.
+ */
+static int grid_draw_rect(game_state *state,
+ unsigned char *hedge, unsigned char *vedge,
+ int c, int really,
+ int x1, int y1, int x2, int y2)
{
int x, y;
- int x1 = ui->x1;
- int y1 = ui->y1;
- int x2 = ui->x2;
- int y2 = ui->y2;
+ int changed = FALSE;
/*
* Draw horizontal edges of rectangles.
val = c;
else if (c == 1)
val = 0;
- index(state,hedge,x,y) = val;
+ changed = changed || (index(state,hedge,x,y) != val);
+ if (really)
+ index(state,hedge,x,y) = val;
}
/*
val = c;
else if (c == 1)
val = 0;
- index(state,vedge,x,y) = val;
+ changed = changed || (index(state,vedge,x,y) != val);
+ if (really)
+ index(state,vedge,x,y) = val;
}
+
+ return changed;
+}
+
+static int ui_draw_rect(game_state *state, game_ui *ui,
+ unsigned char *hedge, unsigned char *vedge, int c,
+ int really)
+{
+ return grid_draw_rect(state, hedge, vedge, c, really,
+ ui->x1, ui->y1, ui->x2, ui->y2);
}
static void game_changed_state(game_ui *ui, game_state *oldstate,
unsigned long *visible;
};
-static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds,
- int x, int y, int button) {
+static char *interpret_move(game_state *from, game_ui *ui, game_drawstate *ds,
+ int x, int y, int button)
+{
int xc, yc;
int startdrag = FALSE, enddrag = FALSE, active = FALSE;
- game_state *ret;
+ char buf[80], *ret;
button &= ~MOD_MASK;
coord_round(FROMCOORD((float)x), FROMCOORD((float)y), &xc, &yc);
- if (startdrag) {
+ if (startdrag &&
+ xc >= 0 && xc <= 2*from->w &&
+ yc >= 0 && yc <= 2*from->h) {
+
ui->drag_start_x = xc;
ui->drag_start_y = yc;
ui->drag_end_x = xc;
active = TRUE;
}
- if (xc != ui->drag_end_x || yc != ui->drag_end_y) {
+ if (ui->drag_start_x >= 0 &&
+ (xc != ui->drag_end_x || yc != ui->drag_end_y)) {
int t;
ui->drag_end_x = xc;
ret = NULL;
- if (enddrag) {
+ if (enddrag && (ui->drag_start_x >= 0)) {
if (xc >= 0 && xc <= 2*from->w &&
yc >= 0 && yc <= 2*from->h) {
- ret = dup_game(from);
if (ui->dragged) {
- ui_draw_rect(ret, ui, ret->hedge, ret->vedge, 1);
+ if (ui_draw_rect(from, ui, from->hedge,
+ from->vedge, 1, FALSE)) {
+ sprintf(buf, "R%d,%d,%d,%d",
+ ui->x1, ui->y1, ui->x2 - ui->x1, ui->y2 - ui->y1);
+ ret = dupstr(buf);
+ }
} else {
if ((xc & 1) && !(yc & 1) && HRANGE(from,xc/2,yc/2)) {
- hedge(ret,xc/2,yc/2) = !hedge(ret,xc/2,yc/2);
+ sprintf(buf, "H%d,%d", xc/2, yc/2);
+ ret = dupstr(buf);
}
if ((yc & 1) && !(xc & 1) && VRANGE(from,xc/2,yc/2)) {
- vedge(ret,xc/2,yc/2) = !vedge(ret,xc/2,yc/2);
+ sprintf(buf, "V%d,%d", xc/2, yc/2);
+ ret = dupstr(buf);
}
}
-
- if (!memcmp(ret->hedge, from->hedge, from->w*from->h) &&
- !memcmp(ret->vedge, from->vedge, from->w*from->h)) {
- free_game(ret);
- ret = NULL;
- }
-
- /*
- * We've made a real change to the grid. Check to see
- * if the game has been completed.
- */
- if (ret && !ret->completed) {
- int x, y, ok;
- unsigned char *correct = get_correct(ret);
-
- ok = TRUE;
- for (x = 0; x < ret->w; x++)
- for (y = 0; y < ret->h; y++)
- if (!index(ret, correct, x, y))
- ok = FALSE;
-
- sfree(correct);
-
- if (ok)
- ret->completed = TRUE;
- }
}
ui->drag_start_x = -1;
if (ret)
return ret; /* a move has been made */
else if (active)
- return from; /* UI activity has occurred */
+ return ""; /* UI activity has occurred */
else
return NULL;
}
+static game_state *execute_move(game_state *from, char *move)
+{
+ game_state *ret;
+ int x1, y1, x2, y2, mode;
+
+ if (move[0] == 'S') {
+ char *p = move+1;
+ int x, y;
+
+ ret = dup_game(from);
+ ret->cheated = TRUE;
+
+ for (y = 0; y < ret->h; y++)
+ for (x = 1; x < ret->w; x++) {
+ vedge(ret, x, y) = (*p == '1');
+ if (*p) p++;
+ }
+ for (y = 1; y < ret->h; y++)
+ for (x = 0; x < ret->w; x++) {
+ hedge(ret, x, y) = (*p == '1');
+ if (*p) p++;
+ }
+
+ return ret;
+
+ } else if (move[0] == 'R' &&
+ sscanf(move+1, "%d,%d,%d,%d", &x1, &y1, &x2, &y2) == 4 &&
+ x1 >= 0 && x2 >= 0 && x1+x2 <= from->w &&
+ y1 >= 0 && y2 >= 0 && y1+y2 <= from->h) {
+ x2 += x1;
+ y2 += y1;
+ mode = move[0];
+ } else if ((move[0] == 'H' || move[0] == 'V') &&
+ sscanf(move+1, "%d,%d", &x1, &y1) == 2 &&
+ (move[0] == 'H' ? HRANGE(from, x1, y1) :
+ VRANGE(from, x1, y1))) {
+ mode = move[0];
+ } else
+ return NULL; /* can't parse move string */
+
+ ret = dup_game(from);
+
+ if (mode == 'R') {
+ grid_draw_rect(ret, ret->hedge, ret->vedge, 1, TRUE, x1, y1, x2, y2);
+ } else if (mode == 'H') {
+ hedge(ret,x1,y1) = !hedge(ret,x1,y1);
+ } else if (mode == 'V') {
+ vedge(ret,x1,y1) = !vedge(ret,x1,y1);
+ }
+
+ /*
+ * We've made a real change to the grid. Check to see
+ * if the game has been completed.
+ */
+ if (!ret->completed) {
+ int x, y, ok;
+ unsigned char *correct = get_correct(ret);
+
+ ok = TRUE;
+ for (x = 0; x < ret->w; x++)
+ for (y = 0; y < ret->h; y++)
+ if (!index(ret, correct, x, y))
+ ok = FALSE;
+
+ sfree(correct);
+
+ if (ok)
+ ret->completed = TRUE;
+ }
+
+ return ret;
+}
+
/* ----------------------------------------------------------------------
* Drawing routines.
*/
#define COLOUR(k) ( (k)==1 ? COL_LINE : COL_DRAG )
#define MAX4(x,y,z,w) ( max(max(x,y),max(z,w)) )
-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)
{
- int tsx, tsy, ts;
- /*
- * Each window dimension equals the tile size times 1.5 more
- * than the grid dimension (the border is 3/4 the width of the
- * tiles).
- *
- * We must cast to unsigned before multiplying by two, because
- * *x might be INT_MAX.
- */
- tsx = 2 * (unsigned)*x / (2 * params->w + 3);
- tsy = 2 * (unsigned)*y / (2 * params->h + 3);
- ts = min(tsx, tsy);
- if (expand)
- ds->tilesize = ts;
- else
- ds->tilesize = min(ts, PREFERRED_TILE_SIZE);
+ /* Ick: fake up `ds->tilesize' for macro expansion purposes */
+ struct { int tilesize; } ads, *ds = &ads;
+ ads.tilesize = tilesize;
*x = params->w * TILE_SIZE + 2*BORDER + 1;
*y = params->h * TILE_SIZE + 2*BORDER + 1;
}
+static void game_set_size(game_drawstate *ds, game_params *params,
+ int tilesize)
+{
+ ds->tilesize = tilesize;
+}
+
static float *game_colours(frontend *fe, game_state *state, int *ncolours)
{
float *ret = snewn(3 * NCOLOURS, float);
vedge = snewn(state->w*state->h, unsigned char);
memcpy(hedge, state->hedge, state->w*state->h);
memcpy(vedge, state->vedge, state->w*state->h);
- ui_draw_rect(state, ui, hedge, vedge, 2);
+ ui_draw_rect(state, ui, hedge, vedge, 2, TRUE);
} else {
hedge = state->hedge;
vedge = state->vedge;
TRUE, game_configure, custom_params,
validate_params,
new_game_desc,
- game_free_aux_info,
validate_desc,
new_game,
dup_game,
TRUE, game_text_format,
new_ui,
free_ui,
+ encode_ui,
+ decode_ui,
game_changed_state,
- make_move,
- game_size,
+ interpret_move,
+ execute_move,
+ PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
game_colours,
game_new_drawstate,
game_free_drawstate,