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 str[80];
return dupstr(str);
}
-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->c < 2)
return "Both dimensions must be at least 2";
}
assert(nsquares > 0);
- if (nsquares > 4)
+ if (nsquares < 2 || nsquares > 4)
return 0;
if (!cage_is_region) {
* Place all the clue numbers we are given.
*/
for (x = 0; x < cr; x++)
- for (y = 0; y < cr; y++)
- if (grid[y*cr+x])
+ for (y = 0; y < cr; y++) {
+ int n = grid[y*cr+x];
+ if (n) {
+ if (!cube(x,y,n)) {
+ diff = DIFF_IMPOSSIBLE;
+ goto got_result;
+ }
solver_place(usage, x, y, grid[y*cr+x]);
+ }
+ }
/*
* Now loop over the grid repeatedly trying all permitted modes
);
if (ret > 0) {
changed = TRUE;
- kdiff = max(kdiff, DIFF_KINTERSECT);
+ kdiff = max(kdiff, DIFF_KSUMS);
} else if (ret < 0) {
diff = DIFF_IMPOSSIBLE;
goto got_result;
scratch->indexlist[i*cr+n-1] = cubepos2(diag1(i), n);
ret = solver_set(usage, scratch, scratch->indexlist
#ifdef STANDALONE_SOLVER
- , "set elimination, \\-diagonal"
+ , "set elimination, /-diagonal"
#endif
);
if (ret < 0) {
* End of grid generator code.
*/
+static int check_killer_cage_sum(struct block_structure *kblocks,
+ digit *kgrid, digit *grid, int blk)
+{
+ /*
+ * Returns: -1 if the cage has any empty square; 0 if all squares
+ * are full but the sum is wrong; +1 if all squares are full and
+ * they have the right sum.
+ *
+ * Does not check uniqueness of numbers within the cage; that's
+ * done elsewhere (because in error highlighting it needs to be
+ * detected separately so as to flag the error in a visually
+ * different way).
+ */
+ int n_squares = kblocks->nr_squares[blk];
+ int sum = 0, clue = 0;
+ int i;
+
+ for (i = 0; i < n_squares; i++) {
+ int xy = kblocks->blocks[blk][i];
+
+ if (grid[xy] == 0)
+ return -1;
+ sum += grid[xy];
+
+ if (kgrid[xy]) {
+ assert(clue == 0);
+ clue = kgrid[xy];
+ }
+ }
+
+ assert(clue != 0);
+ return sum == clue;
+}
+
/*
* Check whether a grid contains a valid complete puzzle.
*/
static int check_valid(int cr, struct block_structure *blocks,
- struct block_structure *kblocks, int xtype, digit *grid)
+ struct block_structure *kblocks,
+ digit *kgrid, int xtype, digit *grid)
{
unsigned char *used;
int x, y, i, j, n;
/*
* Check that each Killer cage, if any, contains at most one of
- * everything.
+ * everything. If we also know the clues for those cages (which we
+ * might not, when this function is called early in puzzle
+ * generation), we also check that they all have the right sum.
*/
if (kblocks) {
for (i = 0; i < kblocks->nr_blocks; i++) {
}
used[grid[kblocks->blocks[i][j]]-1] = TRUE;
}
+
+ if (kgrid && check_killer_cage_sum(kblocks, kgrid, grid, i) != 1) {
+ sfree(used);
+ return FALSE;
+ }
}
}
return TRUE;
}
-static int symmetries(game_params *params, int x, int y, int *output, int s)
+static int symmetries(const game_params *params, int x, int y,
+ int *output, int s)
{
int c = params->c, r = params->r, cr = c*r;
int i = 0;
return grid_encode_space(area);
}
-static char *encode_puzzle_desc(game_params *params, digit *grid,
+static char *encode_puzzle_desc(const game_params *params, digit *grid,
struct block_structure *blocks,
digit *kgrid,
struct block_structure *kblocks)
return b;
}
-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)
{
int c = params->c, r = params->r, cr = c*r;
if (!gridgen(cr, blocks, kblocks, params->xtype, grid, rs, area*area))
continue;
- assert(check_valid(cr, blocks, kblocks, params->xtype, grid));
+ assert(check_valid(cr, blocks, kblocks, NULL, params->xtype, grid));
/*
* Save the solved grid in aux.
return desc;
}
-static char *spec_to_grid(char *desc, digit *grid, int area)
+static const char *spec_to_grid(const char *desc, digit *grid, int area)
{
int i = 0;
while (*desc && *desc != ',') {
* end of the block spec, and return an error string or NULL if everything
* is OK. The DSF is stored in *PDSF.
*/
-static char *spec_to_dsf(char **pdesc, int **pdsf, int cr, int area)
+static char *spec_to_dsf(const char **pdesc, int **pdsf, int cr, int area)
{
- char *desc = *pdesc;
+ const char *desc = *pdesc;
int pos = 0;
int *dsf;
}
desc++;
- adv = (c != 25); /* 'z' is a special case */
+ adv = (c != 26); /* 'z' is a special case */
while (c-- > 0) {
int p0, p1;
* Non-edge; merge the two dsf classes on either
* side of it.
*/
- assert(pos < 2*cr*(cr-1));
+ if (pos >= 2*cr*(cr-1)) {
+ sfree(dsf);
+ return "Too much data in block structure specification";
+ }
+
if (pos < cr*(cr-1)) {
int y = pos/(cr-1);
int x = pos%(cr-1);
return NULL;
}
-static char *validate_grid_desc(char **pdesc, int range, int area)
+static char *validate_grid_desc(const char **pdesc, int range, int area)
{
- char *desc = *pdesc;
+ const char *desc = *pdesc;
int squares = 0;
while (*desc && *desc != ',') {
int n = *desc++;
return NULL;
}
-static char *validate_block_desc(char **pdesc, int cr, int area,
+static char *validate_block_desc(const char **pdesc, int cr, int area,
int min_nr_blocks, int max_nr_blocks,
int min_nr_squares, int max_nr_squares)
{
return NULL;
}
-static char *validate_desc(game_params *params, char *desc)
+static char *validate_desc(const game_params *params, const char *desc)
{
int cr = params->c * params->r, area = cr*cr;
char *err;
return NULL;
}
-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 = snew(game_state);
int c = params->c, r = params->r, cr = c*r, area = cr * cr;
return state;
}
-static game_state *dup_game(game_state *state)
+static game_state *dup_game(const game_state *state)
{
game_state *ret = snew(game_state);
int cr = state->cr, area = cr * cr;
sfree(state);
}
-static char *solve_game(game_state *state, game_state *currstate,
- char *ai, char **error)
+static char *solve_game(const game_state *state, const game_state *currstate,
+ const char *ai, char **error)
{
int cr = state->cr;
char *ret;
return ret;
}
-static int game_can_format_as_text_now(game_params *params)
+static int game_can_format_as_text_now(const game_params *params)
{
/*
* Formatting Killer puzzles as text is currently unsupported. I
return TRUE;
}
-static char *game_text_format(game_state *state)
+static char *game_text_format(const game_state *state)
{
assert(!state->kblocks);
return grid_text_format(state->cr, state->blocks, state->xtype,
int hcursor;
};
-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)
{
int cr = newstate->cr;
/*
int nregions, *entered_items;
};
-static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
- int x, int y, int button)
+static char *interpret_move(const game_state *state, game_ui *ui,
+ const game_drawstate *ds,
+ int x, int y, int button)
{
int cr = state->cr;
int tx, ty;
return dupstr(buf);
}
+ if (button == 'M' || button == 'm')
+ return dupstr("M");
+
return NULL;
}
-static game_state *execute_move(game_state *from, char *move)
+static game_state *execute_move(const game_state *from, const char *move)
{
int cr = from->cr;
game_state *ret;
int x, y, n;
if (move[0] == 'S') {
- char *p;
+ const char *p;
ret = dup_game(from);
ret->completed = ret->cheated = TRUE;
* We've made a real change to the grid. Check to see
* if the game has been completed.
*/
- if (!ret->completed && check_valid(cr, ret->blocks, ret->kblocks,
- ret->xtype, ret->grid)) {
+ if (!ret->completed && check_valid(
+ cr, ret->blocks, ret->kblocks, ret->kgrid,
+ ret->xtype, ret->grid)) {
ret->completed = TRUE;
}
}
return ret;
+ } else if (move[0] == 'M') {
+ /*
+ * Fill in absolutely all pencil marks in unfilled squares,
+ * for those who like to play by the rigorous approach of
+ * starting off in that state and eliminating things.
+ */
+ ret = dup_game(from);
+ for (y = 0; y < cr; y++) {
+ for (x = 0; x < cr; x++) {
+ if (!ret->grid[y*cr+x]) {
+ memset(ret->pencil + (y*cr+x)*cr, 1, cr);
+ }
+ }
+ }
+ return ret;
} else
return NULL; /* couldn't parse move string */
}
#define SIZE(cr) ((cr) * TILE_SIZE + 2*BORDER + 1)
#define GETTILESIZE(cr, w) ( (double)(w-1) / (double)(cr+1) )
-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; } 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;
}
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 cr = state->cr;
sfree(ds);
}
-static void draw_number(drawing *dr, game_drawstate *ds, game_state *state,
- int x, int y, int hl)
+static void draw_number(drawing *dr, game_drawstate *ds,
+ const game_state *state, int x, int y, int hl)
{
int cr = state->cr;
int tx, ty, tw, th;
ds->hl[y*cr+x] = hl;
}
-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 cr = state->cr;
int x, y;
highlight |= 16;
if (d && state->kblocks) {
- int i, b = state->kblocks->whichblock[y*cr+x];
- int n_squares = state->kblocks->nr_squares[b];
- int sum = 0, clue = 0;
- for (i = 0; i < n_squares; i++) {
- int xy = state->kblocks->blocks[b][i];
- if (state->grid[xy] == 0)
- break;
-
- sum += state->grid[xy];
- if (state->kgrid[xy]) {
- assert(clue == 0);
- clue = state->kgrid[xy];
- }
- }
-
- if (i == n_squares) {
- assert(clue != 0);
- if (sum != clue)
- highlight |= 32;
- }
+ if (check_killer_cage_sum(
+ state->kblocks, state->kgrid, state->grid,
+ state->kblocks->whichblock[y*cr+x]) == 0)
+ highlight |= 32;
}
draw_number(dr, ds, state, x, y, highlight);
}
}
-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 &&
!oldstate->cheated && !newstate->cheated)
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)
{
if (state->completed)
return FALSE;
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;
* the interior of the affected squares.
*/
static void outline_block_structure(drawing *dr, game_drawstate *ds,
- game_state *state,
+ const game_state *state,
struct block_structure *blocks,
int ink, int inset)
{
sfree(coords);
}
-static void game_print(drawing *dr, game_state *state, int tilesize)
+static void game_print(drawing *dr, const game_state *state, int tilesize)
{
int cr = state->cr;
int ink = print_mono_colour(dr, 0);
dlev.diff==DIFF_IMPOSSIBLE ? "Impossible (no solution exists)":
"INTERNAL ERROR: unrecognised difficulty code");
if (p->killer)
- printf("Killer diffculty: %s\n",
+ printf("Killer difficulty: %s\n",
dlev.kdiff==DIFF_KSINGLE ? "Trivial (single square cages only)":
dlev.kdiff==DIFF_KMINMAX ? "Simple (maximum sum analysis required)":
dlev.kdiff==DIFF_KSUMS ? "Intermediate (sum possibilities)":