X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/c0361acdb2a1bfa484787a157cfeb5d2d7356795..fdb3b29aacf3f9d0bf16c816ba2ec959e6518bd3:/pattern.c diff --git a/pattern.c b/pattern.c index 4cd3669..f83746a 100644 --- a/pattern.c +++ b/pattern.c @@ -11,9 +11,6 @@ #include "puzzles.h" -#define max(x,y) ( (x)>(y) ? (x):(y) ) -#define min(x,y) ( (x)<(y) ? (x):(y) ) - enum { COL_BACKGROUND, COL_EMPTY, @@ -23,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))) @@ -62,24 +61,26 @@ static game_params *default_params(void) return ret; } +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); @@ -164,14 +165,10 @@ static game_params *custom_params(config_item *cfg) 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) + 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; } @@ -469,13 +466,8 @@ static unsigned char *generate_soluble(random_state *rs, int w, int h) return grid; } -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, int interactive) + char **aux, int interactive) { unsigned char *grid; int i, j, max, rowlen, *rowdata; @@ -487,15 +479,22 @@ static char *new_game_desc(game_params *params, random_state *rs, rowdata = snewn(max, int); /* - * Save the solved game in an aux_info. + * Save the solved game in aux. */ { - game_aux_info *ai = snew(game_aux_info); + 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->w = params->w; - ai->h = params->h; - ai->grid = snewn(ai->w * ai->h, unsigned char); - memcpy(ai->grid, grid, ai->w * ai->h); + 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; } @@ -550,15 +549,10 @@ static char *new_game_desc(game_params *params, random_state *rs, assert(desc[desclen-1] == '/'); desc[desclen-1] = '\0'; sfree(rowdata); + sfree(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 i, n, rowspace; @@ -668,69 +662,67 @@ static void free_game(game_state *state) 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; - - ret = dup_game(state); - ret->completed = ret->cheated = TRUE; + 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 an aux_info, copy it - * out. + * If we already have the solved state in ai, copy it out. */ - if (ai) { + if (ai) + return dupstr(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); - 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); - 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); - 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 < 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; - } - } + 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; + } + } - if (!ret->completed) { - free_game(ret); - *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; } @@ -764,16 +756,36 @@ static void free_ui(game_ui *ui) sfree(ui); } -static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds, - int x, int y, int button) { - game_state *ret; +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) +{ +} +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(from->w, x); - y = FROMCOORD(from->h, y); + x = FROMCOORD(state->w, x); + y = FROMCOORD(state->h, y); - if (x >= 0 && x < from->w && y >= 0 && y < from->h && + if (x >= 0 && x < state->w && y >= 0 && y < state->h && (button == LEFT_BUTTON || button == RIGHT_BUTTON || button == MIDDLE_BUTTON)) { @@ -796,7 +808,7 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds, 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) { @@ -819,13 +831,13 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds, 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) { @@ -839,75 +851,117 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds, 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; -}; - -static 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); } +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); @@ -942,6 +996,7 @@ static 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; @@ -976,8 +1031,8 @@ static void grid_square(frontend *fe, game_drawstate *ds, } static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, - game_state *state, int dir, game_ui *ui, - float animtime, float flashtime) + game_state *state, int dir, game_ui *ui, + float animtime, float flashtime) { int i, j; int x1, x2, y1, y2; @@ -1124,7 +1179,6 @@ const struct game thegame = { TRUE, game_configure, custom_params, validate_params, new_game_desc, - game_free_aux_info, validate_desc, new_game, dup_game, @@ -1133,8 +1187,12 @@ const struct game thegame = { FALSE, game_text_format, new_ui, free_ui, - make_move, - game_size, + 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, @@ -1143,6 +1201,7 @@ const struct game thegame = { game_flash_length, game_wants_statusbar, FALSE, game_timing_state, + 0, /* mouse_priorities */ }; #ifdef STANDALONE_SOLVER @@ -1158,8 +1217,6 @@ 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) {} @@ -1186,15 +1243,12 @@ 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]); + fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); return 1; } else { id = p; @@ -1220,7 +1274,7 @@ int main(int argc, char **argv) fprintf(stderr, "%s: %s\n", argv[0], err); return 1; } - s = new_game(p, desc); + s = new_game(NULL, p, desc); { int w = p->w, h = p->h, i, j, done_any, max;