X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=tracks.c;h=9830387de178494ebfacf6afb8e2b8721ed69d81;hb=3aa4516a680f52e2e6c8a16234a7ecca0ec77233;hp=c6b3d8e148c70787dc917481712ff47cb23a16ef;hpb=44e2690abb523aa60558ca6326eaeb9ce6287454;p=sgt-puzzles.git diff --git a/tracks.c b/tracks.c index c6b3d8e..9830387 100644 --- a/tracks.c +++ b/tracks.c @@ -1072,7 +1072,7 @@ static int solve_check_single_sub(game_state *state, int si, int id, int n, x = i%w; y = i/w; if (abs(ox-x) > 1 || abs(oy-y) > 1) { - if (!state->sflags[i] & S_TRACK) + if (!(state->sflags[i] & S_TRACK)) did += solve_set_sflag(state, x, y, S_NOTRACK, what); } } @@ -1490,11 +1490,10 @@ static void debug_state(game_state *state, const char *what) { sfree(sstring); } -static void dsf_update_completion(game_state *state, int *loopclass, - int ax, int ay, char dir, - int *dsf) +static void dsf_update_completion(game_state *state, int ax, int ay, + char dir, int *dsf) { - int w = state->p.w, ai = ay*w+ax, bx, by, bi, ac, bc; + int w = state->p.w, ai = ay*w+ax, bx, by, bi; if (!(S_E_DIRS(state, ax, ay, E_TRACK) & dir)) return; bx = ax + DX(dir); @@ -1503,22 +1502,47 @@ static void dsf_update_completion(game_state *state, int *loopclass, if (!INGRID(state, bx, by)) return; bi = by*w+bx; - ac = dsf_canonify(dsf, ai); - bc = dsf_canonify(dsf, bi); + dsf_merge(dsf, ai, bi); +} - if (ac == bc) { - /* loop detected */ - *loopclass = ac; - } else { - dsf_merge(dsf, ai, bi); +struct tracks_neighbour_ctx { + game_state *state; + int i, n, neighbours[4]; +}; +static int tracks_neighbour(int vertex, void *vctx) +{ + struct tracks_neighbour_ctx *ctx = (struct tracks_neighbour_ctx *)vctx; + if (vertex >= 0) { + game_state *state = ctx->state; + int w = state->p.w, x = vertex % w, y = vertex / w; + int dirs = S_E_DIRS(state, x, y, E_TRACK); + int j; + + ctx->i = ctx->n = 0; + + for (j = 0; j < 4; j++) { + int dir = 1<neighbours[ctx->n++] = ny * w + nx; + } + } } + + if (ctx->i < ctx->n) + return ctx->neighbours[ctx->i++]; + else + return -1; } static int check_completion(game_state *state, int mark) { int w = state->p.w, h = state->p.h, x, y, i, target, ret = TRUE; - int ntrack, nnotrack; - int *dsf, loopclass, pathclass; + int ntrack, nnotrack, ntrackcomplete; + int *dsf, pathclass; + struct findloopstate *fls; + struct tracks_neighbour_ctx ctx; if (mark) { for (i = 0; i < w+h; i++) { @@ -1527,20 +1551,28 @@ static int check_completion(game_state *state, int mark) for (i = 0; i < w*h; i++) { state->sflags[i] &= ~S_ERROR; if (S_E_COUNT(state, i%w, i/w, E_TRACK) > 0) { - if (S_E_COUNT(state, i%w, i/w, E_TRACK) > 2) + if (S_E_COUNT(state, i%w, i/w, E_TRACK) > 2) { + ret = FALSE; state->sflags[i] |= S_ERROR; + } } } } - /* A cell is 'complete' if it has any edges marked as TRACK. */ + /* A cell is 'complete', for the purposes of marking the game as + * finished, if it has two edges marked as TRACK. But it only has + * to have one edge marked as TRACK, or be filled in as trackful + * without any specific edges known, to count towards checking + * row/column clue errors. */ for (x = 0; x < w; x++) { target = state->numbers->numbers[x]; - ntrack = nnotrack = 0; + ntrack = nnotrack = ntrackcomplete = 0; for (y = 0; y < h; y++) { if (S_E_COUNT(state, x, y, E_TRACK) > 0 || state->sflags[y*w+x] & S_TRACK) ntrack++; + if (S_E_COUNT(state, x, y, E_TRACK) == 2) + ntrackcomplete++; if (state->sflags[y*w+x] & S_NOTRACK) nnotrack++; } @@ -1549,18 +1581,21 @@ static int check_completion(game_state *state, int mark) debug(("col %d error: target %d, track %d, notrack %d", x, target, ntrack, nnotrack)); state->num_errors[x] = 1; + ret = FALSE; } } - if (ntrack != target) + if (ntrackcomplete != target) ret = FALSE; } for (y = 0; y < h; y++) { target = state->numbers->numbers[w+y]; - ntrack = nnotrack = 0; + ntrack = nnotrack = ntrackcomplete = 0; for (x = 0; x < w; x++) { if (S_E_COUNT(state, x, y, E_TRACK) > 0 || state->sflags[y*w+x] & S_TRACK) ntrack++; + if (S_E_COUNT(state, x, y, E_TRACK) == 2) + ntrackcomplete++; if (state->sflags[y*w+x] & S_NOTRACK) nnotrack++; } @@ -1569,36 +1604,44 @@ static int check_completion(game_state *state, int mark) debug(("row %d error: target %d, track %d, notrack %d", y, target, ntrack, nnotrack)); state->num_errors[w+y] = 1; + ret = FALSE; } } - if (ntrack != target) + if (ntrackcomplete != target) ret = FALSE; } dsf = snewn(w*h, int); dsf_init(dsf, w*h); - loopclass = -1; for (x = 0; x < w; x++) { for (y = 0; y < h; y++) { - dsf_update_completion(state, &loopclass, x, y, R, dsf); - dsf_update_completion(state, &loopclass, x, y, D, dsf); + dsf_update_completion(state, x, y, R, dsf); + dsf_update_completion(state, x, y, D, dsf); } } - if (loopclass != -1) { + + fls = findloop_new_state(w*h); + ctx.state = state; + if (findloop_run(fls, w*h, tracks_neighbour, &ctx)) { debug(("loop detected, not complete")); ret = FALSE; /* no loop allowed */ if (mark) { for (x = 0; x < w; x++) { for (y = 0; y < h; y++) { - /* TODO this will only highlight the first loop found */ - if (dsf_canonify(dsf, y*w + x) == loopclass) { - state->sflags[y*w+x] |= S_ERROR; - } + int u, v; + + u = y*w + x; + for (v = tracks_neighbour(u, &ctx); v >= 0; + v = tracks_neighbour(-1, &ctx)) + if (findloop_is_loop_edge(fls, u, v)) + state->sflags[y*w+x] |= S_ERROR; } } } } + findloop_free_state(fls); + if (mark) { pathclass = dsf_canonify(dsf, state->numbers->row_s*w); if (pathclass == dsf_canonify(dsf, (h-1)*w + state->numbers->col_s)) { @@ -1607,9 +1650,17 @@ static int check_completion(game_state *state, int mark) for (i = 0; i < w*h; i++) { if ((dsf_canonify(dsf, i) != pathclass) && ((state->sflags[i] & S_TRACK) || - (S_E_COUNT(state, i%w, i/w, E_TRACK) > 0))) + (S_E_COUNT(state, i%w, i/w, E_TRACK) > 0))) { + ret = FALSE; state->sflags[i] |= S_ERROR; + } } + } else { + /* If we _don't_ have such a path, then certainly the game + * can't be in a winning state. So even if we're not + * highlighting any _errors_, we certainly shouldn't + * return true. */ + ret = FALSE; } } @@ -1667,7 +1718,10 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, #define TILE_SIZE (ds->sz6*6) #define BORDER (TILE_SIZE/8) -#define BORDER_WIDTH (max(TILE_SIZE / 32, 1)) +#define LINE_THICK (TILE_SIZE/16) +#define GRID_LINE_TL (ds->grid_line_tl) +#define GRID_LINE_BR (ds->grid_line_br) +#define GRID_LINE_ALL (ds->grid_line_all) #define COORD(x) ( (x+1) * TILE_SIZE + BORDER ) #define CENTERED_COORD(x) ( COORD(x) + TILE_SIZE/2 ) @@ -1687,7 +1741,7 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, #define DS_CSHIFT 20 /* R/U/L/D shift, for cursor-on-edge */ struct game_drawstate { - int sz6; + int sz6, grid_line_all, grid_line_tl, grid_line_br; int started; int w, h, sz; @@ -2067,7 +2121,6 @@ static void game_compute_size(const game_params *params, int tilesize, int sz6; } ads, *ds = &ads; ads.sz6 = tilesize/6; - *x = (params->w+2) * TILE_SIZE + 2 * BORDER; *y = (params->h+2) * TILE_SIZE + 2 * BORDER; } @@ -2076,6 +2129,9 @@ static void game_set_size(drawing *dr, game_drawstate *ds, const game_params *params, int tilesize) { ds->sz6 = tilesize/6; + ds->grid_line_all = max(LINE_THICK, 1); + ds->grid_line_br = ds->grid_line_all / 2; + ds->grid_line_tl = ds->grid_line_all - ds->grid_line_br; } enum { @@ -2295,14 +2351,13 @@ static void draw_square(drawing *dr, game_drawstate *ds, /* Clip to the grid square. */ clip(dr, ox, oy, TILE_SIZE, TILE_SIZE); - /* Clear the square. */ + /* Clear the square so that it's got an appropriately-sized border + * in COL_GRID and a central area in the right background colour. */ best_bits((flags & DS_TRACK) == DS_TRACK, (flags_drag & DS_TRACK) == DS_TRACK, &bg); - draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, bg); - - /* Draw outline of grid square */ - draw_line(dr, ox, oy, COORD(x+1), oy, COL_GRID); - draw_line(dr, ox, oy, ox, COORD(y+1), COL_GRID); + draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, COL_GRID); + draw_rect(dr, ox + GRID_LINE_TL, oy + GRID_LINE_TL, + TILE_SIZE - GRID_LINE_ALL, TILE_SIZE - GRID_LINE_ALL, bg); /* More outlines for clue squares. */ if (flags & DS_CURSOR) { @@ -2338,8 +2393,8 @@ static void draw_square(drawing *dr, game_drawstate *ds, (flags_drag & DS_NOTRACK) == DS_NOTRACK, &c); if (flags_best) { off = HALFSZ/2; - draw_line(dr, cx - off, cy - off, cx + off, cy + off, c); - draw_line(dr, cx - off, cy + off, cx + off, cy - off, c); + draw_thick_line(dr, LINE_THICK, cx - off, cy - off, cx + off, cy + off, c); + draw_thick_line(dr, LINE_THICK, cx - off, cy + off, cx + off, cy - off, c); } c = COL_TRACK; @@ -2353,8 +2408,8 @@ static void draw_square(drawing *dr, game_drawstate *ds, cx += (d == R) ? t2 : (d == L) ? -t2 : 0; cy += (d == D) ? t2 : (d == U) ? -t2 : 0; - draw_line(dr, cx - off, cy - off, cx + off, cy + off, c); - draw_line(dr, cx - off, cy + off, cx + off, cy - off, c); + draw_thick_line(dr, LINE_THICK, cx - off, cy - off, cx + off, cy + off, c); + draw_thick_line(dr, LINE_THICK, cx - off, cy + off, cx + off, cy - off, c); } } @@ -2375,12 +2430,14 @@ static void draw_clue(drawing *dr, game_drawstate *ds, int w, int clue, int i, i cy = CENTERED_COORD(i-w); } - draw_rect(dr, cx - tsz + BORDER, cy - tsz + BORDER, - TILE_SIZE - BORDER, TILE_SIZE - BORDER, COL_BACKGROUND); + draw_rect(dr, cx - tsz + GRID_LINE_TL, cy - tsz + GRID_LINE_TL, + TILE_SIZE - GRID_LINE_ALL, TILE_SIZE - GRID_LINE_ALL, + COL_BACKGROUND); sprintf(buf, "%d", clue); draw_text(dr, cx, cy, FONT_VARIABLE, tsz, ALIGN_VCENTRE|ALIGN_HCENTRE, col, buf); - draw_update(dr, cx - tsz, cy - tsz, TILE_SIZE, TILE_SIZE); + draw_update(dr, cx - tsz + GRID_LINE_TL, cy - tsz + GRID_LINE_TL, + TILE_SIZE - GRID_LINE_ALL, TILE_SIZE - GRID_LINE_ALL); } static void draw_loop_ends(drawing *dr, game_drawstate *ds, @@ -2447,8 +2504,9 @@ static void game_redraw(drawing *dr, game_drawstate *ds, const game_state *oldst draw_loop_ends(dr, ds, state, COL_CLUE); - draw_line(dr, COORD(ds->w), COORD(0), COORD(ds->w), COORD(ds->h), COL_GRID); - draw_line(dr, COORD(0), COORD(ds->h), COORD(ds->w), COORD(ds->h), COL_GRID); + draw_rect(dr, COORD(0) - GRID_LINE_BR, COORD(0) - GRID_LINE_BR, + ds->w * TILE_SIZE + GRID_LINE_ALL, + ds->h * TILE_SIZE + GRID_LINE_ALL, COL_GRID); draw_update(dr, 0, 0, (w+2)*TILE_SIZE + 2*BORDER, (h+2)*TILE_SIZE + 2*BORDER); @@ -2571,7 +2629,7 @@ static void game_print(drawing *dr, const game_state *state, int tilesize) const struct game thegame = { "Train Tracks", "games.tracks", "tracks", default_params, - game_fetch_preset, + game_fetch_preset, NULL, decode_params, encode_params, free_params,