X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/4a29930e1b9db1332c93244365db1642fcfac1cd..11aeddcc37ecb38c8cfb5e2ca78c2ee8819bf9f3:/solo.c diff --git a/solo.c b/solo.c index 4255c02..cfbb869 100644 --- a/solo.c +++ b/solo.c @@ -1419,13 +1419,51 @@ static int symmetries(game_params *params, int x, int y, int *output, int s) 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; @@ -1493,25 +1531,19 @@ static char *new_game_desc(game_params *params, random_state *rs, 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); } /* @@ -1652,12 +1684,6 @@ static char *new_game_desc(game_params *params, random_state *rs, 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; @@ -1759,39 +1785,38 @@ static void free_game(game_state *state) sfree(state); } -static game_state *solve_game(game_state *state, game_state *currstate, - 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; } @@ -1887,6 +1912,15 @@ static void free_ui(game_ui *ui) 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) { @@ -1914,12 +1948,12 @@ struct game_drawstate { 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; @@ -1928,7 +1962,7 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds, 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; @@ -1937,13 +1971,13 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds, 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 { @@ -1954,7 +1988,7 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds, } else { ui->hx = ui->hy = -1; } - return from; /* UI activity occurred */ + return ""; /* UI activity occurred */ } } @@ -1977,7 +2011,7 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds, * 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; /* @@ -1986,16 +2020,57 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds, * 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", + (char)(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 @@ -2005,12 +2080,9 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds, 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 */ } /* ---------------------------------------------------------------------- @@ -2018,19 +2090,19 @@ static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds, */ #define SIZE(cr) ((cr) * TILE_SIZE + 2*BORDER + 1) -#define GETTILESIZE(cr, w) ( (w-1) / (cr+1) ) +#define GETTILESIZE(cr, w) ( (double)(w-1) / (double)(cr+1) ) static void game_size(game_params *params, game_drawstate *ds, int *x, int *y, int expand) { int c = params->c, r = params->r, cr = c*r; - int ts; + double ts; ts = min(GETTILESIZE(cr, *x), GETTILESIZE(cr, *y)); if (expand) - ds->tilesize = ts; + ds->tilesize = (int)(ts+0.5); else - ds->tilesize = min(ts, PREFERRED_TILE_SIZE); + ds->tilesize = min((int)ts, PREFERRED_TILE_SIZE); *x = SIZE(cr); *y = SIZE(cr); @@ -2143,7 +2215,7 @@ static void draw_number(frontend *fe, game_drawstate *ds, game_state *state, coords[3] = cy; coords[4] = cx; coords[5] = cy+ch/2; - draw_polygon(fe, coords, 3, TRUE, COL_HIGHLIGHT); + draw_polygon(fe, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); } /* new number needs drawing? */ @@ -2329,7 +2401,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, @@ -2338,8 +2409,11 @@ const struct game thegame = { 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, @@ -2364,7 +2438,7 @@ void draw_text(frontend *fe, int x, int y, int fonttype, int fontsize, 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) {} + int fillcolour, int outlinecolour) {} void clip(frontend *fe, int x, int y, int w, int h) {} void unclip(frontend *fe) {} void start_draw(frontend *fe) {}