X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/b6b0369e58b13a0de27f1758af5e385a1d786e7a..fdb3b29aacf3f9d0bf16c816ba2ec959e6518bd3:/pattern.c diff --git a/pattern.c b/pattern.c index d597127..f83746a 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 @@ -15,13 +11,6 @@ #include "puzzles.h" -#define max(x,y) ( (x)>(y) ? (x):(y) ) -#define min(x,y) ( (x)<(y) ? (x):(y) ) - -const char *const game_name = "Pattern"; -const char *const game_winhelp_topic = "games.pattern"; -const int game_can_configure = TRUE; - enum { COL_BACKGROUND, COL_EMPTY, @@ -31,15 +20,17 @@ enum { NCOLOURS }; -#define BORDER 18 +#define PREFERRED_TILE_SIZE 24 +#define TILE_SIZE (ds->tilesize) +#define BORDER (3 * TILE_SIZE / 4) #define TLBORDER(d) ( (d) / 5 + 2 ) -#define GUTTER 12 -#define TILE_SIZE 24 +#define GUTTER (TILE_SIZE / 2) #define FROMCOORD(d, x) \ ( ((x) - (BORDER + GUTTER + TILE_SIZE * TLBORDER(d))) / TILE_SIZE ) #define SIZE(d) (2*BORDER + GUTTER + TILE_SIZE * (TLBORDER(d) + (d))) +#define GETTILESIZE(d, w) ((double)w / (2.0 + (double)TLBORDER(d) + (double)(d))) #define TOCOORD(d, x) (BORDER + GUTTER + TILE_SIZE * (TLBORDER(d) + (x))) @@ -56,12 +47,12 @@ struct game_state { unsigned char *grid; int rowsize; int *rowdata, *rowlen; - int completed; + int completed, cheated; }; #define FLASH_TIME 0.13F -game_params *default_params(void) +static game_params *default_params(void) { game_params *ret = snew(game_params); @@ -70,24 +61,26 @@ game_params *default_params(void) return ret; } -int game_fetch_preset(int i, char **name, game_params **params) +static const struct game_params pattern_presets[] = { + {10, 10}, + {15, 15}, + {20, 20}, +#ifndef SLOW_SYSTEM + {25, 25}, + {30, 30}, +#endif +}; + +static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char str[80]; - static const struct { int x, y; } values[] = { - {10, 10}, - {15, 15}, - {20, 20}, - {25, 25}, - {30, 30}, - }; - - if (i < 0 || i >= lenof(values)) + + if (i < 0 || i >= lenof(pattern_presets)) return FALSE; ret = snew(game_params); - ret->w = values[i].x; - ret->h = values[i].y; + *ret = pattern_presets[i]; sprintf(str, "%dx%d", ret->w, ret->h); @@ -96,21 +89,20 @@ int game_fetch_preset(int i, char **name, game_params **params) return TRUE; } -void free_params(game_params *params) +static void free_params(game_params *params) { sfree(params); } -game_params *dup_params(game_params *params) +static game_params *dup_params(game_params *params) { game_params *ret = snew(game_params); *ret = *params; /* structure copy */ return ret; } -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); @@ -122,11 +114,9 @@ game_params *decode_params(char const *string) } else { ret->h = ret->w; } - - return ret; } -char *encode_params(game_params *params) +static char *encode_params(game_params *params, int full) { char ret[400]; int len; @@ -138,7 +128,7 @@ char *encode_params(game_params *params) return dupstr(ret); } -config_item *game_configure(game_params *params) +static config_item *game_configure(game_params *params) { config_item *ret; char buf[80]; @@ -165,7 +155,7 @@ config_item *game_configure(game_params *params) return ret; } -game_params *custom_params(config_item *cfg) +static game_params *custom_params(config_item *cfg) { game_params *ret = snew(game_params); @@ -175,14 +165,10 @@ game_params *custom_params(config_item *cfg) return ret; } -char *validate_params(game_params *params) +static char *validate_params(game_params *params, int full) { - if (params->w <= 0 && params->h <= 0) + if (params->w <= 0 || params->h <= 0) return "Width and height must both be greater than zero"; - if (params->w <= 0) - return "Width must be greater than zero"; - if (params->h <= 0) - return "Height must be greater than zero"; return NULL; } @@ -285,6 +271,15 @@ static void generate(random_state *rs, int w, int h, unsigned char *retgrid) for (q = -1; q <= +1; q++) { if (i+p < 0 || i+p >= h || j+q < 0 || j+q >= w) continue; + /* + * An additional special case not mentioned + * above: if a grid dimension is 2xn then + * we do not average across that dimension + * at all. Otherwise a 2x2 grid would + * contain four identical squares. + */ + if ((h==2 && p!=0) || (w==2 && q!=0)) + continue; n++; sx += fgrid[(i+p)*w+(j+q)]; } @@ -307,7 +302,7 @@ static void generate(random_state *rs, int w, int h, unsigned char *retgrid) for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { - retgrid[i*w+j] = (fgrid[i*w+j] > threshold ? GRID_FULL : + retgrid[i*w+j] = (fgrid[i*w+j] >= threshold ? GRID_FULL : GRID_EMPTY); } } @@ -315,23 +310,23 @@ static void generate(random_state *rs, int w, int h, unsigned char *retgrid) sfree(fgrid); } -int compute_rowdata(int *ret, unsigned char *start, int len, int step) +static int compute_rowdata(int *ret, unsigned char *start, int len, int step) { int i, n; n = 0; for (i = 0; i < len; i++) { - if (start[i*step] == GRID_UNKNOWN) - return -1; - if (start[i*step] == GRID_FULL) { int runlen = 1; - while (i+runlen < len && start[(i+runlen)*step]) + while (i+runlen < len && start[(i+runlen)*step] == GRID_FULL) runlen++; ret[n++] = runlen; i += runlen; } + + if (i < len && start[i*step] == GRID_UNKNOWN) + return -1; } return n; @@ -412,6 +407,34 @@ static unsigned char *generate_soluble(random_state *rs, int w, int h) generate(rs, w, h, grid); + /* + * The game is a bit too easy if any row or column is + * completely black or completely white. An exception is + * made for rows/columns that are under 3 squares, + * otherwise nothing will ever be successfully generated. + */ + ok = TRUE; + if (w > 2) { + for (i = 0; i < h; i++) { + int colours = 0; + for (j = 0; j < w; j++) + colours |= (grid[i*w+j] == GRID_FULL ? 2 : 1); + if (colours != 3) + ok = FALSE; + } + } + if (h > 2) { + for (j = 0; j < w; j++) { + int colours = 0; + for (i = 0; i < h; i++) + colours |= (grid[i*w+j] == GRID_FULL ? 2 : 1); + if (colours != 3) + ok = FALSE; + } + } + if (!ok) + continue; + memset(matrix, 0, w*h); do { @@ -443,18 +466,40 @@ static unsigned char *generate_soluble(random_state *rs, int w, int h) return grid; } -char *new_game_seed(game_params *params, random_state *rs) +static char *new_game_desc(game_params *params, random_state *rs, + char **aux, int interactive) { 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 aux. + */ + { + char *ai = snewn(params->w * params->h + 2, char); + + /* + * String format is exactly the same as a solve move, so we + * can just dupstr this in solve_game(). + */ + + ai[0] = 'S'; + + for (i = 0; i < params->w * params->h; i++) + ai[i+1] = grid[i] ? '1' : '0'; + + ai[params->w * params->h + 1] = '\0'; + + *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, @@ -464,7 +509,7 @@ 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); @@ -473,14 +518,14 @@ 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); @@ -489,25 +534,26 @@ 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; + sfree(grid); + return desc; } -char *validate_seed(game_params *params, char *seed) +static char *validate_desc(game_params *params, char *desc) { int i, n, rowspace; char *p; @@ -518,10 +564,10 @@ 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; @@ -531,15 +577,15 @@ 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 @@ -549,7 +595,7 @@ char *validate_seed(game_params *params, char *seed) return NULL; } -game_state *new_game(game_params *params, char *seed) +static game_state *new_game(midend_data *me, game_params *params, char *desc) { int i; char *p; @@ -565,26 +611,26 @@ 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 */ } } return state; } -game_state *dup_game(game_state *state) +static game_state *dup_game(game_state *state) { game_state *ret = snew(game_state); @@ -603,11 +649,12 @@ game_state *dup_game(game_state *state) (ret->w + ret->h) * sizeof(int)); ret->completed = state->completed; + ret->cheated = state->cheated; return ret; } -void free_game(game_state *state) +static void free_game(game_state *state) { sfree(state->rowdata); sfree(state->rowlen); @@ -615,6 +662,76 @@ void free_game(game_state *state) sfree(state); } +static char *solve_game(game_state *state, game_state *currstate, + char *ai, char **error) +{ + unsigned char *matrix; + int w = state->w, h = state->h; + int i; + char *ret; + int done_any, max; + unsigned char *workspace; + int *rowdata; + + /* + * If we already have the solved state in ai, copy it out. + */ + if (ai) + return dupstr(ai); + + 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); + + sfree(workspace); + sfree(rowdata); + + for (i = 0; i < w*h; i++) { + if (matrix[i] != BLOCK && matrix[i] != DOT) { + sfree(matrix); + *error = "Solving algorithm cannot complete this puzzle"; + return NULL; + } + } + + ret = snewn(w*h+2, char); + ret[0] = 'S'; + for (i = 0; i < w*h; i++) { + assert(matrix[i] == BLOCK || matrix[i] == DOT); + ret[i+1] = (matrix[i] == BLOCK ? '1' : '0'); + } + ret[w*h+1] = '\0'; + + sfree(matrix); + + return ret; +} + +static char *game_text_format(game_state *state) +{ + return NULL; +} + struct game_ui { int dragging; int drag_start_x; @@ -624,7 +741,7 @@ struct game_ui { int drag, release, state; }; -game_ui *new_ui(game_state *state) +static game_ui *new_ui(game_state *state) { game_ui *ret; @@ -634,19 +751,41 @@ game_ui *new_ui(game_state *state) return ret; } -void free_ui(game_ui *ui) +static void free_ui(game_ui *ui) { sfree(ui); } -game_state *make_move(game_state *from, game_ui *ui, int x, int y, int button) +static char *encode_ui(game_ui *ui) { - game_state *ret; + return NULL; +} + +static void decode_ui(game_ui *ui, char *encoding) +{ +} - x = FROMCOORD(from->w, x); - y = FROMCOORD(from->h, y); +static void game_changed_state(game_ui *ui, game_state *oldstate, + game_state *newstate) +{ +} - if (x >= 0 && x < from->w && y >= 0 && y < from->h && +struct game_drawstate { + int started; + int w, h; + int tilesize; + unsigned char *visible; +}; + +static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, + int x, int y, int button) +{ + button &= ~MOD_MASK; + + x = FROMCOORD(state->w, x); + y = FROMCOORD(state->h, y); + + if (x >= 0 && x < state->w && y >= 0 && y < state->h && (button == LEFT_BUTTON || button == RIGHT_BUTTON || button == MIDDLE_BUTTON)) { @@ -669,7 +808,7 @@ game_state *make_move(game_state *from, game_ui *ui, int x, int y, int button) ui->drag_start_x = ui->drag_end_x = x; ui->drag_start_y = ui->drag_end_y = y; - return from; /* UI activity occurred */ + return ""; /* UI activity occurred */ } if (ui->dragging && button == ui->drag) { @@ -692,13 +831,13 @@ game_state *make_move(game_state *from, game_ui *ui, int x, int y, int button) if (x < 0) x = 0; if (y < 0) y = 0; - if (x >= from->w) x = from->w - 1; - if (y >= from->h) y = from->h - 1; + if (x >= state->w) x = state->w - 1; + if (y >= state->h) y = state->h - 1; ui->drag_end_x = x; ui->drag_end_y = y; - return from; /* UI activity occurred */ + return ""; /* UI activity occurred */ } if (ui->dragging && button == ui->release) { @@ -712,76 +851,118 @@ game_state *make_move(game_state *from, game_ui *ui, int x, int y, int button) for (yy = y1; yy <= y2; yy++) for (xx = x1; xx <= x2; xx++) - if (from->grid[yy * from->w + xx] != ui->state) + if (state->grid[yy * state->w + xx] != ui->state) move_needed = TRUE; ui->dragging = FALSE; if (move_needed) { - ret = dup_game(from); - for (yy = y1; yy <= y2; yy++) - for (xx = x1; xx <= x2; xx++) - ret->grid[yy * ret->w + xx] = ui->state; - - /* - * An actual change, so check to see if we've completed - * the game. - */ - if (!ret->completed) { - int *rowdata = snewn(ret->rowsize, int); - int i, len; - - ret->completed = TRUE; - - for (i=0; iw; i++) { - len = compute_rowdata(rowdata, - ret->grid+i, ret->h, ret->w); - if (len != ret->rowlen[i] || - memcmp(ret->rowdata+i*ret->rowsize, rowdata, - len * sizeof(int))) { - ret->completed = FALSE; - break; - } - } - for (i=0; ih; i++) { - len = compute_rowdata(rowdata, - ret->grid+i*ret->w, ret->w, 1); - if (len != ret->rowlen[i+ret->w] || - memcmp(ret->rowdata+(i+ret->w)*ret->rowsize, rowdata, - len * sizeof(int))) { - ret->completed = FALSE; - break; - } - } - - sfree(rowdata); - } - - return ret; + char buf[80]; + sprintf(buf, "%c%d,%d,%d,%d", + (char)(ui->state == GRID_FULL ? 'F' : + ui->state == GRID_EMPTY ? 'E' : 'U'), + x1, y1, x2-x1+1, y2-y1+1); + return dupstr(buf); } else - return from; /* UI activity occurred */ + return ""; /* UI activity occurred */ } return NULL; } +static game_state *execute_move(game_state *from, char *move) +{ + game_state *ret; + int x1, x2, y1, y2, xx, yy; + int val; + + if (move[0] == 'S' && strlen(move) == from->w * from->h + 1) { + int i; + + ret = dup_game(from); + + for (i = 0; i < ret->w * ret->h; i++) + ret->grid[i] = (move[i+1] == '1' ? GRID_FULL : GRID_EMPTY); + + ret->completed = ret->cheated = TRUE; + + return ret; + } else if ((move[0] == 'F' || move[0] == 'E' || move[0] == 'U') && + 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; + val = (move[0] == 'F' ? GRID_FULL : + move[0] == 'E' ? GRID_EMPTY : GRID_UNKNOWN); + + ret = dup_game(from); + for (yy = y1; yy < y2; yy++) + for (xx = x1; xx < x2; xx++) + ret->grid[yy * ret->w + xx] = val; + + /* + * An actual change, so check to see if we've completed the + * game. + */ + if (!ret->completed) { + int *rowdata = snewn(ret->rowsize, int); + int i, len; + + ret->completed = TRUE; + + for (i=0; iw; i++) { + len = compute_rowdata(rowdata, + ret->grid+i, ret->h, ret->w); + if (len != ret->rowlen[i] || + memcmp(ret->rowdata+i*ret->rowsize, rowdata, + len * sizeof(int))) { + ret->completed = FALSE; + break; + } + } + for (i=0; ih; i++) { + len = compute_rowdata(rowdata, + ret->grid+i*ret->w, ret->w, 1); + if (len != ret->rowlen[i+ret->w] || + memcmp(ret->rowdata+(i+ret->w)*ret->rowsize, rowdata, + len * sizeof(int))) { + ret->completed = FALSE; + break; + } + } + + sfree(rowdata); + } + + return ret; + } else + return NULL; +} + /* ---------------------------------------------------------------------- * Drawing routines. */ -struct game_drawstate { - int started; - int w, h; - unsigned char *visible; -}; - -void game_size(game_params *params, int *x, int *y) +static void game_compute_size(game_params *params, int tilesize, + int *x, int *y) { + /* Ick: fake up `ds->tilesize' for macro expansion purposes */ + struct { int tilesize; } ads, *ds = &ads; + ads.tilesize = tilesize; + *x = SIZE(params->w); *y = SIZE(params->h); } -float *game_colours(frontend *fe, game_state *state, int *ncolours) +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); @@ -807,7 +988,7 @@ float *game_colours(frontend *fe, game_state *state, int *ncolours) return ret; } -game_drawstate *game_new_drawstate(game_state *state) +static game_drawstate *game_new_drawstate(game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); @@ -815,12 +996,13 @@ game_drawstate *game_new_drawstate(game_state *state) ds->w = state->w; ds->h = state->h; ds->visible = snewn(ds->w * ds->h, unsigned char); + ds->tilesize = 0; /* not decided yet */ memset(ds->visible, 255, ds->w * ds->h); return ds; } -void game_free_drawstate(game_drawstate *ds) +static void game_free_drawstate(game_drawstate *ds) { sfree(ds->visible); sfree(ds); @@ -848,9 +1030,9 @@ static void grid_square(frontend *fe, game_drawstate *ds, TILE_SIZE, TILE_SIZE); } -void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, - game_state *state, int dir, game_ui *ui, - float animtime, float flashtime) +static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, + game_state *state, int dir, game_ui *ui, + float animtime, float flashtime) { int i, j; int x1, x2, y1, y2; @@ -906,7 +1088,7 @@ void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, * Draw the grid outline. */ draw_rect(fe, TOCOORD(ds->w, 0) - 1, TOCOORD(ds->h, 0) - 1, - ds->w * TILE_SIZE + 2, ds->h * TILE_SIZE + 2, + ds->w * TILE_SIZE + 3, ds->h * TILE_SIZE + 3, COL_GRID); ds->started = TRUE; @@ -957,19 +1139,185 @@ void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, } } -float game_anim_length(game_state *oldstate, game_state *newstate, int dir) +static float game_anim_length(game_state *oldstate, + game_state *newstate, int dir, game_ui *ui) { return 0.0F; } -float game_flash_length(game_state *oldstate, game_state *newstate, int dir) +static float game_flash_length(game_state *oldstate, + game_state *newstate, int dir, game_ui *ui) { - if (!oldstate->completed && newstate->completed) + if (!oldstate->completed && newstate->completed && + !oldstate->cheated && !newstate->cheated) return FLASH_TIME; return 0.0F; } -int game_wants_statusbar(void) +static int game_wants_statusbar(void) { return FALSE; } + +static int game_timing_state(game_state *state) +{ + return TRUE; +} + +#ifdef COMBINED +#define thegame pattern +#endif + +const struct game thegame = { + "Pattern", "games.pattern", + default_params, + game_fetch_preset, + decode_params, + encode_params, + free_params, + dup_params, + TRUE, game_configure, custom_params, + validate_params, + new_game_desc, + validate_desc, + new_game, + dup_game, + free_game, + TRUE, solve_game, + FALSE, game_text_format, + new_ui, + free_ui, + encode_ui, + decode_ui, + game_changed_state, + interpret_move, + execute_move, + PREFERRED_TILE_SIZE, game_compute_size, game_set_size, + game_colours, + game_new_drawstate, + game_free_drawstate, + game_redraw, + game_anim_length, + game_flash_length, + game_wants_statusbar, + FALSE, game_timing_state, + 0, /* mouse_priorities */ +}; + +#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 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; + char *id = NULL, *desc, *err; + + while (--argc > 0) { + char *p = *++argv; + if (*p == '-') { + fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); + 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(NULL, 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