return i;
}
-struct game_aux_info {
- int c, r;
- digit *grid;
-};
+static char *encode_solve_move(int cr, digit *grid)
+{
+ int i, len;
+ char *ret, *p, *sep;
+
+ /*
+ * It's surprisingly easy to work out _exactly_ how long this
+ * string needs to be. To decimal-encode all the numbers from 1
+ * to n:
+ *
+ * - every number has a units digit; total is n.
+ * - all numbers above 9 have a tens digit; total is max(n-9,0).
+ * - all numbers above 99 have a hundreds digit; total is max(n-99,0).
+ * - and so on.
+ */
+ len = 0;
+ for (i = 1; i <= cr; i *= 10)
+ len += max(cr - i + 1, 0);
+ len += cr; /* don't forget the commas */
+ len *= cr; /* there are cr rows of these */
+
+ /*
+ * Now len is one bigger than the total size of the
+ * comma-separated numbers (because we counted an
+ * additional leading comma). We need to have a leading S
+ * and a trailing NUL, so we're off by one in total.
+ */
+ len++;
+
+ ret = snewn(len, char);
+ p = ret;
+ *p++ = 'S';
+ sep = "";
+ for (i = 0; i < cr*cr; i++) {
+ p += sprintf(p, "%s%d", sep, grid[i]);
+ sep = ",";
+ }
+ *p++ = '\0';
+ assert(p - ret == len);
+
+ return ret;
+}
static char *new_game_desc(game_params *params, random_state *rs,
- game_aux_info **aux, int interactive)
+ char **aux, int interactive)
{
int c = params->c, r = params->r, cr = c*r;
int area = cr*cr;
assert(check_valid(c, r, grid));
/*
- * Save the solved grid in the aux_info.
+ * Save the solved grid in aux.
*/
{
- game_aux_info *ai = snew(game_aux_info);
- ai->c = c;
- ai->r = r;
- ai->grid = snewn(cr * cr, digit);
- memcpy(ai->grid, grid, cr * cr * sizeof(digit));
/*
* We might already have written *aux the last time we
* went round this loop, in which case we should free
- * the old aux_info before overwriting it with the new
- * one.
+ * the old aux before overwriting it with the new one.
*/
if (*aux) {
- sfree((*aux)->grid);
sfree(*aux);
}
- *aux = ai;
+
+ *aux = encode_solve_move(cr, grid);
}
/*
return desc;
}
-static void game_free_aux_info(game_aux_info *aux)
-{
- sfree(aux->grid);
- sfree(aux);
-}
-
static char *validate_desc(game_params *params, char *desc)
{
int area = params->r * params->r * params->c * params->c;
sfree(state);
}
-static game_state *solve_game(game_state *state, game_aux_info *ai,
- char **error)
+static char *solve_game(game_state *state, game_state *currstate,
+ char *ai, char **error)
{
- game_state *ret;
int c = state->c, r = state->r, cr = c*r;
+ char *ret;
+ digit *grid;
int rsolve_ret;
- ret = dup_game(state);
- ret->completed = ret->cheated = TRUE;
-
/*
- * If we already have the solution in the aux_info, save
- * ourselves some time.
+ * If we already have the solution in ai, save ourselves some
+ * time.
*/
- if (ai) {
+ if (ai)
+ return dupstr(ai);
- assert(c == ai->c);
- assert(r == ai->r);
- memcpy(ret->grid, ai->grid, cr * cr * sizeof(digit));
+ grid = snewn(cr*cr, digit);
+ memcpy(grid, state->grid, cr*cr);
+ rsolve_ret = rsolve(c, r, grid, NULL, 2);
- } else {
- rsolve_ret = rsolve(c, r, ret->grid, NULL, 2);
-
- if (rsolve_ret != 1) {
- free_game(ret);
- if (rsolve_ret == 0)
- *error = "No solution exists for this puzzle";
- else
- *error = "Multiple solutions exist for this puzzle";
- return NULL;
- }
+ if (rsolve_ret != 1) {
+ sfree(grid);
+ if (rsolve_ret == 0)
+ *error = "No solution exists for this puzzle";
+ else
+ *error = "Multiple solutions exist for this puzzle";
+ return NULL;
}
+ ret = encode_solve_move(cr, grid);
+
+ sfree(grid);
+
return ret;
}
sfree(ui);
}
+static char *encode_ui(game_ui *ui)
+{
+ return NULL;
+}
+
+static void decode_ui(game_ui *ui, char *encoding)
+{
+}
+
static void game_changed_state(game_ui *ui, game_state *oldstate,
game_state *newstate)
{
int *entered_items;
};
-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 *state, game_ui *ui, game_drawstate *ds,
+ int x, int y, int button)
{
- int c = from->c, r = from->r, cr = c*r;
+ int c = state->c, r = state->r, cr = c*r;
int tx, ty;
- game_state *ret;
+ char buf[80];
button &= ~MOD_MASK;
if (tx >= 0 && tx < cr && ty >= 0 && ty < cr) {
if (button == LEFT_BUTTON) {
- if (from->immutable[ty*cr+tx]) {
+ if (state->immutable[ty*cr+tx]) {
ui->hx = ui->hy = -1;
} else if (tx == ui->hx && ty == ui->hy && ui->hpencil == 0) {
ui->hx = ui->hy = -1;
ui->hy = ty;
ui->hpencil = 0;
}
- return from; /* UI activity occurred */
+ return ""; /* UI activity occurred */
}
if (button == RIGHT_BUTTON) {
/*
* Pencil-mode highlighting for non filled squares.
*/
- if (from->grid[ty*cr+tx] == 0) {
+ if (state->grid[ty*cr+tx] == 0) {
if (tx == ui->hx && ty == ui->hy && ui->hpencil) {
ui->hx = ui->hy = -1;
} else {
} else {
ui->hx = ui->hy = -1;
}
- return from; /* UI activity occurred */
+ return ""; /* UI activity occurred */
}
}
* able to highlight the square, but it never hurts to be
* careful.
*/
- if (from->immutable[ui->hy*cr+ui->hx])
+ if (state->immutable[ui->hy*cr+ui->hx])
return NULL;
/*
* have even been able to pencil-highlight the square, but
* it never hurts to be careful.
*/
- if (ui->hpencil && from->grid[ui->hy*cr+ui->hx])
+ if (ui->hpencil && state->grid[ui->hy*cr+ui->hx])
return NULL;
+ sprintf(buf, "%c%d,%d,%d",
+ ui->hpencil && n > 0 ? 'P' : 'R', ui->hx, ui->hy, n);
+
+ ui->hx = ui->hy = -1;
+
+ return dupstr(buf);
+ }
+
+ return NULL;
+}
+
+static game_state *execute_move(game_state *from, char *move)
+{
+ int c = from->c, r = from->r, cr = c*r;
+ game_state *ret;
+ int x, y, n;
+
+ if (move[0] == 'S') {
+ char *p;
+
ret = dup_game(from);
- if (ui->hpencil && n > 0) {
- int index = (ui->hy*cr+ui->hx) * cr + (n-1);
+ ret->completed = ret->cheated = TRUE;
+
+ p = move+1;
+ for (n = 0; n < cr*cr; n++) {
+ ret->grid[n] = atoi(p);
+
+ if (!*p || ret->grid[n] < 1 || ret->grid[n] > cr) {
+ free_game(ret);
+ return NULL;
+ }
+
+ while (*p && isdigit((unsigned char)*p)) p++;
+ if (*p == ',') p++;
+ }
+
+ return ret;
+ } else if ((move[0] == 'P' || move[0] == 'R') &&
+ sscanf(move+1, "%d,%d,%d", &x, &y, &n) == 3 &&
+ x >= 0 && x < cr && y >= 0 && y < cr && n >= 0 && n <= cr) {
+
+ ret = dup_game(from);
+ if (move[0] == 'P' && n > 0) {
+ int index = (y*cr+x) * cr + (n-1);
ret->pencil[index] = !ret->pencil[index];
} else {
- ret->grid[ui->hy*cr+ui->hx] = n;
- memset(ret->pencil + (ui->hy*cr+ui->hx)*cr, 0, cr);
+ ret->grid[y*cr+x] = n;
+ memset(ret->pencil + (y*cr+x)*cr, 0, cr);
/*
* We've made a real change to the grid. Check to see
ret->completed = TRUE;
}
}
- ui->hx = ui->hy = -1;
-
- return ret; /* made a valid move */
- }
-
- return NULL;
+ return ret;
+ } else
+ return NULL; /* couldn't parse move string */
}
/* ----------------------------------------------------------------------
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,
+ interpret_move,
+ execute_move,
game_size,
game_colours,
game_new_drawstate,