X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/15f00e064a52e87a78e925c764221dcbd15b6bed..aa27d4932844e6aa590afba411b1503217f1602d:/pattern.c diff --git a/pattern.c b/pattern.c index 4fc1562..e09ed3d 100644 --- a/pattern.c +++ b/pattern.c @@ -1,9 +1,5 @@ /* * pattern.c: the pattern-reconstruction game known as `nonograms'. - * - * TODO before checkin: - * - * - make some sort of stab at number-of-numbers judgment */ #include @@ -52,7 +48,7 @@ struct game_state { unsigned char *grid; int rowsize; int *rowdata, *rowlen; - int completed; + int completed, cheated; }; #define FLASH_TIME 0.13F @@ -104,9 +100,8 @@ static game_params *dup_params(game_params *params) return ret; } -static game_params *decode_params(char const *string) +static void decode_params(game_params *ret, char const *string) { - game_params *ret = default_params(); char const *p = string; ret->w = atoi(p); @@ -118,11 +113,9 @@ static game_params *decode_params(char const *string) } else { ret->h = ret->w; } - - return ret; } -static char *encode_params(game_params *params) +static char *encode_params(game_params *params, int full) { char ret[400]; int len; @@ -476,18 +469,38 @@ static unsigned char *generate_soluble(random_state *rs, int w, int h) return grid; } -static char *new_game_seed(game_params *params, random_state *rs) +struct game_aux_info { + int w, h; + unsigned char *grid; +}; + +static char *new_game_desc(game_params *params, random_state *rs, + game_aux_info **aux) { unsigned char *grid; int i, j, max, rowlen, *rowdata; - char intbuf[80], *seed; - int seedlen, seedpos; + char intbuf[80], *desc; + int desclen, descpos; grid = generate_soluble(rs, params->w, params->h); max = max(params->w, params->h); rowdata = snewn(max, int); /* + * Save the solved game in an aux_info. + */ + { + game_aux_info *ai = snew(game_aux_info); + + ai->w = params->w; + ai->h = params->h; + ai->grid = snewn(ai->w * ai->h, unsigned char); + memcpy(ai->grid, grid, ai->w * ai->h); + + *aux = ai; + } + + /* * Seed is a slash-separated list of row contents; each row * contents section is a dot-separated list of integers. Row * contents are listed in the order (columns left to right, @@ -497,7 +510,7 @@ static char *new_game_seed(game_params *params, random_state *rs) * passes, first computing the seed size and then writing it * out. */ - seedlen = 0; + desclen = 0; for (i = 0; i < params->w + params->h; i++) { if (i < params->w) rowlen = compute_rowdata(rowdata, grid+i, params->h, params->w); @@ -506,14 +519,14 @@ static char *new_game_seed(game_params *params, random_state *rs) params->w, 1); if (rowlen > 0) { for (j = 0; j < rowlen; j++) { - seedlen += 1 + sprintf(intbuf, "%d", rowdata[j]); + desclen += 1 + sprintf(intbuf, "%d", rowdata[j]); } } else { - seedlen++; + desclen++; } } - seed = snewn(seedlen, char); - seedpos = 0; + desc = snewn(desclen, char); + descpos = 0; for (i = 0; i < params->w + params->h; i++) { if (i < params->w) rowlen = compute_rowdata(rowdata, grid+i, params->h, params->w); @@ -522,25 +535,31 @@ static char *new_game_seed(game_params *params, random_state *rs) params->w, 1); if (rowlen > 0) { for (j = 0; j < rowlen; j++) { - int len = sprintf(seed+seedpos, "%d", rowdata[j]); + int len = sprintf(desc+descpos, "%d", rowdata[j]); if (j+1 < rowlen) - seed[seedpos + len] = '.'; + desc[descpos + len] = '.'; else - seed[seedpos + len] = '/'; - seedpos += len+1; + desc[descpos + len] = '/'; + descpos += len+1; } } else { - seed[seedpos++] = '/'; + desc[descpos++] = '/'; } } - assert(seedpos == seedlen); - assert(seed[seedlen-1] == '/'); - seed[seedlen-1] = '\0'; + assert(descpos == desclen); + assert(desc[desclen-1] == '/'); + desc[desclen-1] = '\0'; sfree(rowdata); - return seed; + return desc; +} + +static void game_free_aux_info(game_aux_info *aux) +{ + sfree(aux->grid); + sfree(aux); } -static char *validate_seed(game_params *params, char *seed) +static char *validate_desc(game_params *params, char *desc) { int i, n, rowspace; char *p; @@ -551,10 +570,10 @@ static char *validate_seed(game_params *params, char *seed) else rowspace = params->w + 1; - if (*seed && isdigit((unsigned char)*seed)) { + if (*desc && isdigit((unsigned char)*desc)) { do { - p = seed; - while (seed && isdigit((unsigned char)*seed)) seed++; + p = desc; + while (desc && isdigit((unsigned char)*desc)) desc++; n = atoi(p); rowspace -= n+1; @@ -564,15 +583,15 @@ static char *validate_seed(game_params *params, char *seed) else return "at least one row contains more numbers than will fit"; } - } while (*seed++ == '.'); + } while (*desc++ == '.'); } else { - seed++; /* expect a slash immediately */ + desc++; /* expect a slash immediately */ } - if (seed[-1] == '/') { + if (desc[-1] == '/') { if (i+1 == params->w + params->h) return "too many row/column specifications"; - } else if (seed[-1] == '\0') { + } else if (desc[-1] == '\0') { if (i+1 < params->w + params->h) return "too few row/column specifications"; } else @@ -582,7 +601,7 @@ static char *validate_seed(game_params *params, char *seed) return NULL; } -static game_state *new_game(game_params *params, char *seed) +static game_state *new_game(game_params *params, char *desc) { int i; char *p; @@ -598,19 +617,19 @@ static game_state *new_game(game_params *params, char *seed) state->rowdata = snewn(state->rowsize * (state->w + state->h), int); state->rowlen = snewn(state->w + state->h, int); - state->completed = FALSE; + state->completed = state->cheated = FALSE; for (i = 0; i < params->w + params->h; i++) { state->rowlen[i] = 0; - if (*seed && isdigit((unsigned char)*seed)) { + if (*desc && isdigit((unsigned char)*desc)) { do { - p = seed; - while (seed && isdigit((unsigned char)*seed)) seed++; + p = desc; + while (desc && isdigit((unsigned char)*desc)) desc++; state->rowdata[state->rowsize * i + state->rowlen[i]++] = atoi(p); - } while (*seed++ == '.'); + } while (*desc++ == '.'); } else { - seed++; /* expect a slash immediately */ + desc++; /* expect a slash immediately */ } } @@ -636,6 +655,7 @@ static game_state *dup_game(game_state *state) (ret->w + ret->h) * sizeof(int)); ret->completed = state->completed; + ret->cheated = state->cheated; return ret; } @@ -648,6 +668,78 @@ static void free_game(game_state *state) sfree(state); } +static game_state *solve_game(game_state *state, game_aux_info *ai, + char **error) +{ + game_state *ret; + + ret = dup_game(state); + ret->completed = ret->cheated = TRUE; + + /* + * If we already have the solved state in an aux_info, copy it + * out. + */ + if (ai) { + + assert(ret->w == ai->w); + assert(ret->h == ai->h); + memcpy(ret->grid, ai->grid, ai->w * ai->h); + + } else { + int w = state->w, h = state->h, i, j, done_any, max; + unsigned char *matrix, *workspace; + int *rowdata; + + matrix = snewn(w*h, unsigned char); + max = max(w, h); + workspace = snewn(max*3, unsigned char); + rowdata = snewn(max+1, int); + + memset(matrix, 0, w*h); + + do { + done_any = 0; + for (i=0; irowdata + state->rowsize*(w+i), + max*sizeof(int)); + rowdata[state->rowlen[w+i]] = 0; + done_any |= do_row(workspace, workspace+max, workspace+2*max, + matrix+i*w, w, 1, rowdata); + } + for (i=0; irowdata + state->rowsize*i, max*sizeof(int)); + rowdata[state->rowlen[i]] = 0; + done_any |= do_row(workspace, workspace+max, workspace+2*max, + matrix+i, h, w, rowdata); + } + } while (done_any); + + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + int c = (matrix[i*w+j] == BLOCK ? GRID_FULL : + matrix[i*w+j] == DOT ? GRID_EMPTY : GRID_UNKNOWN); + ret->grid[i*w+j] = c; + if (c == GRID_UNKNOWN) + ret->completed = FALSE; + } + } + + if (!ret->completed) { + free_game(ret); + *error = "Solving algorithm cannot complete this puzzle"; + return NULL; + } + } + + return ret; +} + +static char *game_text_format(game_state *state) +{ + return NULL; +} + struct game_ui { int dragging; int drag_start_x; @@ -677,6 +769,8 @@ static game_state *make_move(game_state *from, game_ui *ui, { game_state *ret; + button &= ~MOD_MASK; + x = FROMCOORD(from->w, x); y = FROMCOORD(from->h, y); @@ -1000,7 +1094,8 @@ static float game_anim_length(game_state *oldstate, static float game_flash_length(game_state *oldstate, game_state *newstate, int dir) { - if (!oldstate->completed && newstate->completed) + if (!oldstate->completed && newstate->completed && + !oldstate->cheated && !newstate->cheated) return FLASH_TIME; return 0.0F; } @@ -1015,21 +1110,23 @@ static int game_wants_statusbar(void) #endif const struct game thegame = { - "Pattern", "games.pattern", TRUE, + "Pattern", "games.pattern", default_params, game_fetch_preset, decode_params, encode_params, free_params, dup_params, - game_configure, - custom_params, + TRUE, game_configure, custom_params, validate_params, - new_game_seed, - validate_seed, + new_game_desc, + game_free_aux_info, + validate_desc, new_game, dup_game, free_game, + TRUE, solve_game, + FALSE, game_text_format, new_ui, free_ui, make_move, @@ -1042,3 +1139,126 @@ const struct game thegame = { game_flash_length, game_wants_statusbar, }; + +#ifdef STANDALONE_SOLVER + +/* + * gcc -DSTANDALONE_SOLVER -o patternsolver pattern.c malloc.c + */ + +#include + +void frontend_default_colour(frontend *fe, float *output) {} +void draw_text(frontend *fe, int x, int y, int fonttype, int fontsize, + int align, int colour, char *text) {} +void draw_rect(frontend *fe, int x, int y, int w, int h, int colour) {} +void draw_line(frontend *fe, int x1, int y1, int x2, int y2, int colour) {} +void draw_polygon(frontend *fe, int *coords, int npoints, + int fill, int colour) {} +void clip(frontend *fe, int x, int y, int w, int h) {} +void unclip(frontend *fe) {} +void start_draw(frontend *fe) {} +void draw_update(frontend *fe, int x, int y, int w, int h) {} +void end_draw(frontend *fe) {} +unsigned long random_upto(random_state *state, unsigned long limit) +{ assert(!"Shouldn't get randomness"); return 0; } + +void fatal(char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "fatal error: "); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fprintf(stderr, "\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + game_params *p; + game_state *s; + int recurse = TRUE; + char *id = NULL, *desc, *err; + int y, x; + int grade = FALSE; + + while (--argc > 0) { + char *p = *++argv; + if (*p == '-') { + fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0]); + return 1; + } else { + id = p; + } + } + + if (!id) { + fprintf(stderr, "usage: %s \n", argv[0]); + return 1; + } + + desc = strchr(id, ':'); + if (!desc) { + fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]); + return 1; + } + *desc++ = '\0'; + + p = default_params(); + decode_params(p, id); + err = validate_desc(p, desc); + if (err) { + fprintf(stderr, "%s: %s\n", argv[0], err); + return 1; + } + s = new_game(p, desc); + + { + int w = p->w, h = p->h, i, j, done_any, max; + unsigned char *matrix, *workspace; + int *rowdata; + + matrix = snewn(w*h, unsigned char); + max = max(w, h); + workspace = snewn(max*3, unsigned char); + rowdata = snewn(max+1, int); + + memset(matrix, 0, w*h); + + do { + done_any = 0; + for (i=0; irowdata + s->rowsize*(w+i), + max*sizeof(int)); + rowdata[s->rowlen[w+i]] = 0; + done_any |= do_row(workspace, workspace+max, workspace+2*max, + matrix+i*w, w, 1, rowdata); + } + for (i=0; irowdata + s->rowsize*i, max*sizeof(int)); + rowdata[s->rowlen[i]] = 0; + done_any |= do_row(workspace, workspace+max, workspace+2*max, + matrix+i, h, w, rowdata); + } + } while (done_any); + + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + int c = (matrix[i*w+j] == UNKNOWN ? '?' : + matrix[i*w+j] == BLOCK ? '#' : + matrix[i*w+j] == DOT ? '.' : + '!'); + putchar(c); + } + printf("\n"); + } + } + + return 0; +} + +#endif