X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=tents.c;h=859b13eae50beb452033bac556999445c70a5e39;hb=3ce69e84cad15844282d691fa03e711c5353c05e;hp=c0ea0ebfe21380ff7a905fac11f2b62c2851eedd;hpb=ddfa693b0fd4a0902ba85414dc5bbefc5bb384cf;p=sgt-puzzles.git diff --git a/tents.c b/tents.c index c0ea0eb..859b13e 100644 --- a/tents.c +++ b/tents.c @@ -324,7 +324,7 @@ static void free_params(game_params *params) 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 */ @@ -350,7 +350,7 @@ static void decode_params(game_params *params, char const *string) } } -static char *encode_params(game_params *params, int full) +static char *encode_params(const game_params *params, int full) { char buf[120]; @@ -361,7 +361,7 @@ static char *encode_params(game_params *params, int full) return dupstr(buf); } -static config_item *game_configure(game_params *params) +static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; @@ -393,7 +393,7 @@ static config_item *game_configure(game_params *params) 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); @@ -404,7 +404,7 @@ static game_params *custom_params(config_item *cfg) return ret; } -static char *validate_params(game_params *params, int full) +static char *validate_params(const game_params *params, int full) { /* * Generating anything under 4x4 runs into trouble of one kind @@ -459,7 +459,7 @@ static int tents_solve(int w, int h, const char *grid, int *numbers, char *soln, struct solver_scratch *sc, int diff) { int x, y, d, i, j; - char *mrow, *mrow1, *mrow2, *trow, *trow1, *trow2; + char *mrow, *trow, *trow1, *trow2; /* * Set up solver data. @@ -746,8 +746,6 @@ static int tents_solve(int w, int h, const char *grid, int *numbers, * hasn't been set up yet. */ mrow = sc->mrows; - mrow1 = sc->mrows + len; - mrow2 = sc->mrows + 2*len; trow = sc->trows; trow1 = sc->trows + len; trow2 = sc->trows + 2*len; @@ -902,9 +900,11 @@ static int tents_solve(int w, int h, const char *grid, int *numbers, return 1; } -static char *new_game_desc(game_params *params, random_state *rs, +static char *new_game_desc(const game_params *params_in, random_state *rs, char **aux, int interactive) { + game_params params_copy = *params_in; /* structure copy */ + game_params *params = ¶ms_copy; int w = params->w, h = params->h; int ntrees = w * h / 5; char *grid = snewn(w*h, char); @@ -1063,7 +1063,7 @@ static char *new_game_desc(game_params *params, random_state *rs, j = maxflow(w*h+2, w*h+1, w*h, nedges, edges, capacity, flow, NULL); if (j < ntrees) - continue; /* couldn't place all the tents */ + continue; /* couldn't place all the trees */ /* * We've placed the trees. Now we need to work out _where_ @@ -1190,7 +1190,7 @@ static char *new_game_desc(game_params *params, random_state *rs, return ret; } -static char *validate_desc(game_params *params, char *desc) +static char *validate_desc(const game_params *params, const char *desc) { int w = params->w, h = params->h; int area, i; @@ -1210,6 +1210,10 @@ static char *validate_desc(game_params *params, char *desc) desc++; } + if (area < w * h + 1) + return "Not enough data to fill grid"; + else if (area > w * h + 1) + return "Too much data to fill grid"; for (i = 0; i < w+h; i++) { if (!*desc) @@ -1225,7 +1229,8 @@ static char *validate_desc(game_params *params, char *desc) 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) { int w = params->w, h = params->h; game_state *state = snew(game_state); @@ -1284,7 +1289,7 @@ static game_state *new_game(midend *me, game_params *params, char *desc) return state; } -static game_state *dup_game(game_state *state) +static game_state *dup_game(const game_state *state) { int w = state->p.w, h = state->p.h; game_state *ret = snew(game_state); @@ -1310,8 +1315,8 @@ static void free_game(game_state *state) sfree(state); } -static char *solve_game(game_state *state, game_state *currstate, - char *aux, char **error) +static char *solve_game(const game_state *state, const game_state *currstate, + const char *aux, char **error) { int w = state->p.w, h = state->p.h; @@ -1360,44 +1365,59 @@ static char *solve_game(game_state *state, game_state *currstate, } } -static int game_can_format_as_text_now(game_params *params) +static int game_can_format_as_text_now(const game_params *params) { - return TRUE; + return params->w <= 1998 && params->h <= 1998; /* 999 tents */ } -static char *game_text_format(game_state *state) +static char *game_text_format(const game_state *state) { - int w = state->p.w, h = state->p.h; - char *ret, *p; - int x, y; + int w = state->p.w, h = state->p.h, r, c; + int cw = 4, ch = 2, gw = (w+1)*cw + 2, gh = (h+1)*ch + 1, len = gw * gh; + char *board = snewn(len + 1, char); + + sprintf(board, "%*s\n", len - 2, ""); + for (r = 0; r <= h; ++r) { + for (c = 0; c <= w; ++c) { + int cell = r*ch*gw + cw*c, center = cell + gw*ch/2 + cw/2; + int i = r*w + c, n = 1000; + + if (r == h && c == w) /* NOP */; + else if (c == w) n = state->numbers->numbers[w + r]; + else if (r == h) n = state->numbers->numbers[c]; + else switch (state->grid[i]) { + case BLANK: board[center] = '.'; break; + case TREE: board[center] = 'T'; break; + case TENT: memcpy(board + center - 1, "//\\", 3); break; + case NONTENT: break; + default: memcpy(board + center - 1, "wtf", 3); + } - /* - * FIXME: We currently do not print the numbers round the edges - * of the grid. I need to work out a sensible way of doing this - * even when the column numbers exceed 9. - * - * In the absence of those numbers, the result size is h lines - * of w+1 characters each, plus a NUL. - * - * This function is currently only used by the standalone - * solver; until I make it look more sensible, I won't enable - * it in the main game structure. - */ - ret = snewn(h*(w+1) + 1, char); - p = ret; - for (y = 0; y < h; y++) { - for (x = 0; x < w; x++) { - *p = (state->grid[y*w+x] == BLANK ? '.' : - state->grid[y*w+x] == TREE ? 'T' : - state->grid[y*w+x] == TENT ? '*' : - state->grid[y*w+x] == NONTENT ? '-' : '?'); - p++; + if (n < 100) { + board[center] = '0' + n % 10; + if (n >= 10) board[center - 1] = '0' + n / 10; + } else if (n < 1000) { + board[center + 1] = '0' + n % 10; + board[center] = '0' + n / 10 % 10; + board[center - 1] = '0' + n / 100; + } + + board[cell] = '+'; + memset(board + cell + 1, '-', cw - 1); + for (i = 1; i < ch; ++i) board[cell + i*gw] = '|'; + } + + for (c = 0; c < ch; ++c) { + board[(r*ch+c)*gw + gw - 2] = + c == 0 ? '+' : r < h ? '|' : ' '; + board[(r*ch+c)*gw + gw - 1] = '\n'; } - *p++ = '\n'; } - *p++ = '\0'; - return ret; + memset(board + len - gw, '-', gw - 2 - cw); + for (c = 0; c <= w; ++c) board[len - gw + cw*c] = '+'; + + return board; } struct game_ui { @@ -1409,7 +1429,7 @@ struct game_ui { int cx, cy, cdisp; /* cursor position, and ?display. */ }; -static game_ui *new_ui(game_state *state) +static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->dsx = ui->dsy = -1; @@ -1425,17 +1445,17 @@ static void free_ui(game_ui *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) { } @@ -1456,7 +1476,7 @@ struct game_drawstate { #define FLASH_TIME 0.30F -static int drag_xform(game_ui *ui, int x, int y, int v) +static int drag_xform(const game_ui *ui, int x, int y, int v) { int xmin, ymin, xmax, ymax; @@ -1465,6 +1485,7 @@ static int drag_xform(game_ui *ui, int x, int y, int v) ymin = min(ui->dsy, ui->dey); ymax = max(ui->dsy, ui->dey); +#ifndef STYLUS_BASED /* * Left-dragging has no effect, so we treat a left-drag as a * single click on dsx,dsy. @@ -1473,6 +1494,7 @@ static int drag_xform(game_ui *ui, int x, int y, int v) xmin = xmax = ui->dsx; ymin = ymax = ui->dsy; } +#endif if (x < xmin || x > xmax || y < ymin || y > ymax) return v; /* no change outside drag area */ @@ -1485,11 +1507,18 @@ static int drag_xform(game_ui *ui, int x, int y, int v) * Results of a simple click. Left button sets blanks to * tents; right button sets blanks to non-tents; either * button clears a non-blank square. + * If stylus-based however, it loops instead. */ if (ui->drag_button == LEFT_BUTTON) +#ifdef STYLUS_BASED + v = (v == BLANK ? TENT : (v == TENT ? NONTENT : BLANK)); + else + v = (v == BLANK ? NONTENT : (v == NONTENT ? TENT : BLANK)); +#else v = (v == BLANK ? TENT : BLANK); else v = (v == BLANK ? NONTENT : BLANK); +#endif } else { /* * Results of a drag. Left-dragging has no effect. @@ -1499,17 +1528,25 @@ static int drag_xform(game_ui *ui, int x, int y, int v) if (ui->drag_button == RIGHT_BUTTON) v = (v == BLANK ? NONTENT : v); else +#ifdef STYLUS_BASED + v = (v == BLANK ? NONTENT : v); +#else /* do nothing */; +#endif } return v; } -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 w = state->p.w, h = state->p.h; char tmpbuf[80]; + int shift = button & MOD_SHFT, control = button & MOD_CTRL; + + button &= ~MOD_MASK; if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { x = FROMCOORD(x); @@ -1606,8 +1643,26 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, } if (IS_CURSOR_MOVE(button)) { - move_cursor(button, &ui->cx, &ui->cy, w, h, 0); ui->cdisp = 1; + if (shift || control) { + int len = 0, i, indices[2]; + indices[0] = ui->cx + w * ui->cy; + move_cursor(button, &ui->cx, &ui->cy, w, h, 0); + indices[1] = ui->cx + w * ui->cy; + + /* NONTENTify all unique traversed eligible squares */ + for (i = 0; i <= (indices[0] != indices[1]); ++i) + if (state->grid[indices[i]] == BLANK || + (control && state->grid[indices[i]] == TENT)) { + len += sprintf(tmpbuf + len, "%sN%d,%d", len ? ";" : "", + indices[i] % w, indices[i] / w); + assert(len < lenof(tmpbuf)); + } + + tmpbuf[len] = '\0'; + if (len) return dupstr(tmpbuf); + } else + move_cursor(button, &ui->cx, &ui->cy, w, h, 0); return ""; } if (ui->cdisp) { @@ -1641,7 +1696,7 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, return NULL; } -static game_state *execute_move(game_state *state, char *move) +static game_state *execute_move(const game_state *state, const char *move) { int w = state->p.w, h = state->p.h; char c; @@ -1829,8 +1884,8 @@ static game_state *execute_move(game_state *state, char *move) * Drawing routines. */ -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) { /* fool the macros */ struct dummy { int tilesize; } dummy, *ds = &dummy; @@ -1841,7 +1896,7 @@ static void game_compute_size(game_params *params, int tilesize, } static void game_set_size(drawing *dr, game_drawstate *ds, - game_params *params, int tilesize) + const game_params *params, int tilesize) { ds->tilesize = tilesize; } @@ -1888,7 +1943,7 @@ static float *game_colours(frontend *fe, int *ncolours) 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) { int w = state->p.w, h = state->p.h; struct game_drawstate *ds = snew(struct game_drawstate); @@ -1927,7 +1982,7 @@ enum { ERR_OVERCOMMITTED }; -static int *find_errors(game_state *state, char *grid) +static int *find_errors(const game_state *state, char *grid) { int w = state->p.w, h = state->p.h; int *ret = snewn(w*h + w + h, int); @@ -1964,11 +2019,14 @@ static int *find_errors(game_state *state, char *grid) * Also, that approach fares badly when you introduce the * additional requirement that incomplete grids should have * errors highlighted only when they can be proved to be errors - * - so that a tree surrounded by BLANK squares should not be - * marked as erroneous (it would be patronising, since the - * overwhelming likelihood is not that the player has forgotten - * to put a tree there but that they have merely not put one - * there _yet), but one surrounded by NONTENTs should. + * - so that trees should not be marked as having too few tents + * if there are enough BLANK squares remaining around them that + * could be turned into the missing tents (to do so would be + * patronising, since the overwhelming likelihood is not that + * the player has forgotten to put a tree there but that they + * have merely not put one there _yet_). However, tents with too + * few trees can be marked immediately, since those are + * definitely player error. * * So I adopt an alternative approach, which is to consider the * bipartite adjacency graph between trees and tents @@ -2345,8 +2403,9 @@ static void draw_tile(drawing *dr, game_drawstate *ds, /* * Internal redraw function, used for printing as well as drawing. */ -static void int_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, - game_state *state, int dir, game_ui *ui, +static void int_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 printing) { int w = state->p.w, h = state->p.h; @@ -2392,7 +2451,7 @@ static void int_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, * currently active drag: we transform dsx,dsy but not anything * else. (This seems to strike a good compromise between having * the error highlights respond instantly to single clicks, but - * not give constant feedback during a right-drag.) + * not giving constant feedback during a right-drag.) */ if (ui && ui->drag_button >= 0) { tmpgrid = snewn(w*h, char); @@ -2445,7 +2504,7 @@ static void int_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, * changed) the numbers. */ for (x = 0; x < w; x++) { - if (ds->numbersdrawn[x] != errors[w*h+x]) { + if (printing || ds->numbersdrawn[x] != errors[w*h+x]) { char buf[80]; draw_rect(dr, COORD(x), COORD(h)+1, TILESIZE, BRBORDER-1, COL_BACKGROUND); @@ -2454,11 +2513,12 @@ static void int_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, FONT_VARIABLE, TILESIZE/2, ALIGN_HCENTRE|ALIGN_VNORMAL, (errors[w*h+x] ? COL_ERROR : COL_GRID), buf); draw_update(dr, COORD(x), COORD(h)+1, TILESIZE, BRBORDER-1); - ds->numbersdrawn[x] = errors[w*h+x]; + if (!printing) + ds->numbersdrawn[x] = errors[w*h+x]; } } for (y = 0; y < h; y++) { - if (ds->numbersdrawn[w+y] != errors[w*h+w+y]) { + if (printing || ds->numbersdrawn[w+y] != errors[w*h+w+y]) { char buf[80]; draw_rect(dr, COORD(w)+1, COORD(y), BRBORDER-1, TILESIZE, COL_BACKGROUND); @@ -2467,7 +2527,8 @@ static void int_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, FONT_VARIABLE, TILESIZE/2, ALIGN_HRIGHT|ALIGN_VCENTRE, (errors[w*h+w+y] ? COL_ERROR : COL_GRID), buf); draw_update(dr, COORD(w)+1, COORD(y), BRBORDER-1, TILESIZE); - ds->numbersdrawn[w+y] = errors[w*h+w+y]; + if (!printing) + ds->numbersdrawn[w+y] = errors[w*h+w+y]; } } @@ -2479,21 +2540,22 @@ static void int_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, sfree(errors); } -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_redraw(dr, ds, oldstate, state, dir, ui, animtime, flashtime, FALSE); } -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->used_solve && !newstate->used_solve) @@ -2502,12 +2564,17 @@ static float game_flash_length(game_state *oldstate, game_state *newstate, return 0.0F; } -static int game_timing_state(game_state *state, game_ui *ui) +static int game_status(const game_state *state) +{ + return state->completed ? +1 : 0; +} + +static int game_timing_state(const game_state *state, game_ui *ui) { 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; @@ -2519,7 +2586,7 @@ static void game_print_size(game_params *params, float *x, float *y) *y = ph / 100.0F; } -static void game_print(drawing *dr, game_state *state, int tilesize) +static void game_print(drawing *dr, const game_state *state, int tilesize) { int c; @@ -2557,7 +2624,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - FALSE, game_can_format_as_text_now, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, @@ -2572,6 +2639,7 @@ const struct game thegame = { game_redraw, game_anim_length, game_flash_length, + game_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state,