X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/84942c65e87429222a1cbb28d00134d2367aa6b3..b498c53924c571d997f97c7ff3dd81c3e66894a1:/net.c diff --git a/net.c b/net.c index b5134a9..0df502f 100644 --- a/net.c +++ b/net.c @@ -28,11 +28,6 @@ #define D 0x08 #define LOCKED 0x10 #define ACTIVE 0x20 -/* Corner flags go in the barriers array */ -#define RU 0x10 -#define UL 0x20 -#define LD 0x40 -#define DR 0x80 /* Rotations: Anticlockwise, Clockwise, Flip, general rotate */ #define A(x) ( (((x) & 0x07) << 1) | (((x) & 0x08) >> 3) ) @@ -57,6 +52,13 @@ #define ROTATE_TIME 0.13F #define FLASH_FRAME 0.07F +/* Transform physical coords to game coords using game_drawstate ds */ +#define GX(x) (((x) + ds->org_x) % ds->width) +#define GY(y) (((y) + ds->org_y) % ds->height) +/* ...and game coords to physical coords */ +#define RX(x) (((x) + ds->width - ds->org_x) % ds->width) +#define RY(y) (((y) + ds->height - ds->org_y) % ds->height) + enum { COL_BACKGROUND, COL_LOCKED, @@ -82,7 +84,7 @@ struct game_aux_info { }; struct game_state { - int width, height, cx, cy, wrapping, completed; + int width, height, wrapping, completed; int last_rotate_x, last_rotate_y, last_rotate_dir; int used_solve, just_used_solve; unsigned char *tiles; @@ -314,6 +316,55 @@ static char *validate_params(game_params *params) return "Barrier probability may not be negative"; if (params->barrier_probability > 1) return "Barrier probability may not be greater than 1"; + + /* + * Specifying either grid dimension as 2 in a wrapping puzzle + * makes it actually impossible to ensure a unique puzzle + * solution. + * + * Proof: + * + * Without loss of generality, let us assume the puzzle _width_ + * is 2, so we can conveniently discuss rows without having to + * say `rows/columns' all the time. (The height may be 2 as + * well, but that doesn't matter.) + * + * In each row, there are two edges between tiles: the inner + * edge (running down the centre of the grid) and the outer + * edge (the identified left and right edges of the grid). + * + * Lemma: In any valid 2xn puzzle there must be at least one + * row in which _exactly one_ of the inner edge and outer edge + * is connected. + * + * Proof: No row can have _both_ inner and outer edges + * connected, because this would yield a loop. So the only + * other way to falsify the lemma is for every row to have + * _neither_ the inner nor outer edge connected. But this + * means there is no connection at all between the left and + * right columns of the puzzle, so there are two disjoint + * subgraphs, which is also disallowed. [] + * + * Given such a row, it is always possible to make the + * disconnected edge connected and the connected edge + * disconnected without changing the state of any other edge. + * (This is easily seen by case analysis on the various tiles: + * left-pointing and right-pointing endpoints can be exchanged, + * likewise T-pieces, and a corner piece can select its + * horizontal connectivity independently of its vertical.) This + * yields a distinct valid solution. + * + * Thus, for _every_ row in which exactly one of the inner and + * outer edge is connected, there are two valid states for that + * row, and hence the total number of solutions of the puzzle + * is at least 2^(number of such rows), and in particular is at + * least 2 since there must be at least one such row. [] + */ + if (params->unique && params->wrapping && + (params->width == 2 || params->height == 2)) + return "No wrapping puzzle with a width or height of 2 can have" + " a unique solution"; + return NULL; } @@ -657,7 +708,7 @@ static int net_solver(int w, int h, unsigned char *tiles, * dead ends of size 2 and 3 forms a subnetwork * with a total area of 6, not 5.) */ - if (deadendtotal+1 < area) + if (deadendtotal > 0 && deadendtotal+1 < area) valid = FALSE; } else if (nnondeadends == 1) { /* @@ -694,7 +745,6 @@ static int net_solver(int w, int h, unsigned char *tiles, assert(j > 0); /* we can't lose _all_ possibilities! */ if (j < i) { - int a, o; done_something = TRUE; /* @@ -703,12 +753,16 @@ static int net_solver(int w, int h, unsigned char *tiles, */ while (j < 4) tilestate[(y*w+x) * 4 + j++] = 255; + } - /* - * Now go through them again and see if we've - * deduced anything new about any edges. - */ + /* + * Now go through the tile orientations again and see + * if we've deduced anything new about any edges. + */ + { + int a, o; a = 0xF; o = 0; + for (i = 0; i < 4 && tilestate[(y*w+x) * 4 + i] != 255; i++) { a &= tilestate[(y*w+x) * 4 + i]; o |= tilestate[(y*w+x) * 4 + i]; @@ -1518,8 +1572,6 @@ static game_state *new_game(game_params *params, char *desc) state = snew(game_state); w = state->width = params->width; h = state->height = params->height; - state->cx = state->width / 2; - state->cy = state->height / 2; state->wrapping = params->wrapping; state->last_rotate_dir = state->last_rotate_x = state->last_rotate_y = 0; state->completed = state->used_solve = state->just_used_solve = FALSE; @@ -1571,54 +1623,22 @@ static game_state *new_game(game_params *params, char *desc) barrier(state, 0, y) |= L; barrier(state, state->width-1, y) |= R; } - } - - /* - * Set up the barrier corner flags, for drawing barriers - * prettily when they meet. - */ - for (y = 0; y < state->height; y++) { - for (x = 0; x < state->width; x++) { - int dir; - - for (dir = 1; dir < 0x10; dir <<= 1) { - int dir2 = A(dir); - int x1, y1, x2, y2, x3, y3; - int corner = FALSE; - - if (!(barrier(state, x, y) & dir)) - continue; - - if (barrier(state, x, y) & dir2) - corner = TRUE; - - x1 = x + X(dir), y1 = y + Y(dir); - if (x1 >= 0 && x1 < state->width && - y1 >= 0 && y1 < state->height && - (barrier(state, x1, y1) & dir2)) - corner = TRUE; - - x2 = x + X(dir2), y2 = y + Y(dir2); - if (x2 >= 0 && x2 < state->width && - y2 >= 0 && y2 < state->height && - (barrier(state, x2, y2) & dir)) - corner = TRUE; - - if (corner) { - barrier(state, x, y) |= (dir << 4); - if (x1 >= 0 && x1 < state->width && - y1 >= 0 && y1 < state->height) - barrier(state, x1, y1) |= (A(dir) << 4); - if (x2 >= 0 && x2 < state->width && - y2 >= 0 && y2 < state->height) - barrier(state, x2, y2) |= (C(dir) << 4); - x3 = x + X(dir) + X(dir2), y3 = y + Y(dir) + Y(dir2); - if (x3 >= 0 && x3 < state->width && - y3 >= 0 && y3 < state->height) - barrier(state, x3, y3) |= (F(dir) << 4); - } - } - } + } else { + /* + * We check whether this is de-facto a non-wrapping game + * despite the parameters, in case we were passed the + * description of a non-wrapping game. This is so that we + * can change some aspects of the UI behaviour. + */ + state->wrapping = FALSE; + for (x = 0; x < state->width; x++) + if (!(barrier(state, x, 0) & U) || + !(barrier(state, x, state->height-1) & D)) + state->wrapping = TRUE; + for (y = 0; y < state->width; y++) + if (!(barrier(state, 0, y) & L) || + !(barrier(state, state->width-1, y) & R)) + state->wrapping = TRUE; } return state; @@ -1631,8 +1651,6 @@ static game_state *dup_game(game_state *state) ret = snew(game_state); ret->width = state->width; ret->height = state->height; - ret->cx = state->cx; - ret->cy = state->cy; ret->wrapping = state->wrapping; ret->completed = state->completed; ret->used_solve = state->used_solve; @@ -1696,7 +1714,7 @@ static char *game_text_format(game_state *state) * completed - just call this function and see whether every square * is marked active. */ -static unsigned char *compute_active(game_state *state) +static unsigned char *compute_active(game_state *state, int cx, int cy) { unsigned char *active; tree234 *todo; @@ -1710,8 +1728,8 @@ static unsigned char *compute_active(game_state *state) * xyd_cmp and just store direction 0 every time. */ todo = newtree234(xyd_cmp_nc); - index(state, active, state->cx, state->cy) = ACTIVE; - add234(todo, new_xyd(state->cx, state->cy, 0)); + index(state, active, cx, cy) = ACTIVE; + add234(todo, new_xyd(cx, cy, 0)); while ( (xyd = delpos234(todo, 0)) != NULL) { int x1, y1, d1, x2, y2, d2; @@ -1747,6 +1765,8 @@ static unsigned char *compute_active(game_state *state) } struct game_ui { + int org_x, org_y; /* origin */ + int cx, cy; /* source tile (game coordinates) */ int cur_x, cur_y; int cur_visible; random_state *rs; /* used for jumbling */ @@ -1757,8 +1777,9 @@ static game_ui *new_ui(game_state *state) void *seed; int seedsize; game_ui *ui = snew(game_ui); - ui->cur_x = state->width / 2; - ui->cur_y = state->height / 2; + ui->org_x = ui->org_y = 0; + ui->cur_x = ui->cx = state->width / 2; + ui->cur_y = ui->cy = state->height / 2; ui->cur_visible = FALSE; get_random_seed(&seed, &seedsize); ui->rs = random_init(seed, seedsize); @@ -1781,7 +1802,9 @@ static game_state *make_move(game_state *state, game_ui *ui, { game_state *ret, *nullret; int tx, ty, orig; + int shift = button & MOD_SHFT, ctrl = button & MOD_CTRL; + button &= ~MOD_MASK; nullret = NULL; if (button == LEFT_BUTTON || @@ -1804,23 +1827,44 @@ static game_state *make_move(game_state *state, game_ui *ui, ty = y / TILE_SIZE; if (tx >= state->width || ty >= state->height) return nullret; + /* Transform from physical to game coords */ + tx = (tx + ui->org_x) % state->width; + ty = (ty + ui->org_y) % state->height; if (x % TILE_SIZE >= TILE_SIZE - TILE_BORDER || y % TILE_SIZE >= TILE_SIZE - TILE_BORDER) return nullret; } else if (button == CURSOR_UP || button == CURSOR_DOWN || button == CURSOR_RIGHT || button == CURSOR_LEFT) { - if (button == CURSOR_UP && ui->cur_y > 0) - ui->cur_y--; - else if (button == CURSOR_DOWN && ui->cur_y < state->height-1) - ui->cur_y++; - else if (button == CURSOR_LEFT && ui->cur_x > 0) - ui->cur_x--; - else if (button == CURSOR_RIGHT && ui->cur_x < state->width-1) - ui->cur_x++; - else - return nullret; /* no cursor movement */ - ui->cur_visible = TRUE; - return state; /* UI activity has occurred */ + int dir; + switch (button) { + case CURSOR_UP: dir = U; break; + case CURSOR_DOWN: dir = D; break; + case CURSOR_LEFT: dir = L; break; + case CURSOR_RIGHT: dir = R; break; + default: return nullret; + } + if (shift) { + /* + * Move origin. + */ + if (state->wrapping) { + OFFSET(ui->org_x, ui->org_y, ui->org_x, ui->org_y, dir, state); + } else return nullret; /* disallowed for non-wrapping grids */ + } + if (ctrl) { + /* + * Change source tile. + */ + OFFSET(ui->cx, ui->cy, ui->cx, ui->cy, dir, state); + } + if (!shift && !ctrl) { + /* + * Move keyboard cursor. + */ + OFFSET(ui->cur_x, ui->cur_y, ui->cur_x, ui->cur_y, dir, state); + ui->cur_visible = TRUE; + } + return state; /* UI activity has occurred */ } else if (button == 'a' || button == 's' || button == 'd' || button == 'A' || button == 'S' || button == 'D') { tx = ui->cur_x; @@ -1909,7 +1953,7 @@ static game_state *make_move(game_state *state, game_ui *ui, * Check whether the game has been completed. */ { - unsigned char *active = compute_active(ret); + unsigned char *active = compute_active(ret, ui->cx, ui->cy); int x1, y1; int complete = TRUE; @@ -1937,6 +1981,7 @@ static game_state *make_move(game_state *state, game_ui *ui, struct game_drawstate { int started; int width, height; + int org_x, org_y; unsigned char *visible; }; @@ -1947,6 +1992,7 @@ static game_drawstate *game_new_drawstate(game_state *state) ds->started = FALSE; ds->width = state->width; ds->height = state->height; + ds->org_x = ds->org_y = -1; ds->visible = snewn(state->width * state->height, unsigned char); memset(ds->visible, 0xFF, state->width * state->height); @@ -2044,25 +2090,24 @@ static void draw_rect_coords(frontend *fe, int x1, int y1, int x2, int y2, draw_rect(fe, mx, my, dx, dy, colour); } -static void draw_barrier_corner(frontend *fe, int x, int y, int dir, int phase) +/* + * draw_barrier_corner() and draw_barrier() are passed physical coords + */ +static void draw_barrier_corner(frontend *fe, int x, int y, int dx, int dy, + int phase) { int bx = WINDOW_OFFSET + TILE_SIZE * x; int by = WINDOW_OFFSET + TILE_SIZE * y; - int x1, y1, dx, dy, dir2; - - dir >>= 4; + int x1, y1; - dir2 = A(dir); - dx = X(dir) + X(dir2); - dy = Y(dir) + Y(dir2); x1 = (dx > 0 ? TILE_SIZE+TILE_BORDER-1 : 0); y1 = (dy > 0 ? TILE_SIZE+TILE_BORDER-1 : 0); if (phase == 0) { - draw_rect_coords(fe, bx+x1, by+y1, + draw_rect_coords(fe, bx+x1+dx, by+y1, bx+x1-TILE_BORDER*dx, by+y1-(TILE_BORDER-1)*dy, COL_WIRE); - draw_rect_coords(fe, bx+x1, by+y1, + draw_rect_coords(fe, bx+x1, by+y1+dy, bx+x1-(TILE_BORDER-1)*dx, by+y1-TILE_BORDER*dy, COL_WIRE); } else { @@ -2090,8 +2135,11 @@ static void draw_barrier(frontend *fe, int x, int y, int dir, int phase) } } -static void draw_tile(frontend *fe, game_state *state, int x, int y, int tile, - float angle, int cursor) +/* + * draw_tile() is passed physical coordinates + */ +static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds, + int x, int y, int tile, int src, float angle, int cursor) { int bx = WINDOW_OFFSET + TILE_SIZE * x; int by = WINDOW_OFFSET + TILE_SIZE * y; @@ -2104,17 +2152,10 @@ static void draw_tile(frontend *fe, game_state *state, int x, int y, int tile, * and including the borders around the tile. This means that * if the neighbouring tiles have connections to those borders, * we must draw those connections on the borders themselves. - * - * This would be terribly fiddly if we ever had to draw a tile - * while its neighbour was in mid-rotate, because we'd have to - * arrange to _know_ that the neighbour was being rotated and - * hence had an anomalous effect on the redraw of this tile. - * Fortunately, the drawing algorithm avoids ever calling us in - * this circumstance: we're either drawing lots of straight - * tiles at game start or after a move is complete, or we're - * repeatedly drawing only the rotating tile. So no problem. */ + clip(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER); + /* * So. First blank the tile out completely: draw a big * rectangle in border colour, and a smaller rectangle in @@ -2186,7 +2227,7 @@ static void draw_tile(frontend *fe, game_state *state, int x, int y, int tile, * otherwise not at all. */ col = -1; - if (x == state->cx && y == state->cy) + if (src) col = COL_WIRE; else if (COUNT(tile) == 1) { col = (tile & ACTIVE ? COL_POWERED : COL_ENDPOINT); @@ -2227,7 +2268,7 @@ static void draw_tile(frontend *fe, game_state *state, int x, int y, int tile, if (ox < 0 || ox >= state->width || oy < 0 || oy >= state->height) continue; - if (!(tile(state, ox, oy) & F(dir))) + if (!(tile(state, GX(ox), GY(oy)) & F(dir))) continue; px = bx + (int)(dx>0 ? TILE_SIZE + TILE_BORDER - 1 : dx<0 ? 0 : cx); @@ -2262,29 +2303,70 @@ static void draw_tile(frontend *fe, game_state *state, int x, int y, int tile, * Draw barrier corners, and then barriers. */ for (phase = 0; phase < 2; phase++) { + for (dir = 1; dir < 0x10; dir <<= 1) { + int x1, y1, corner = FALSE; + /* + * If at least one barrier terminates at the corner + * between dir and A(dir), draw a barrier corner. + */ + if (barrier(state, GX(x), GY(y)) & (dir | A(dir))) { + corner = TRUE; + } else { + /* + * Only count barriers terminating at this corner + * if they're physically next to the corner. (That + * is, if they've wrapped round from the far side + * of the screen, they don't count.) + */ + x1 = x + X(dir); + y1 = y + Y(dir); + if (x1 >= 0 && x1 < state->width && + y1 >= 0 && y1 < state->height && + (barrier(state, GX(x1), GY(y1)) & A(dir))) { + corner = TRUE; + } else { + x1 = x + X(A(dir)); + y1 = y + Y(A(dir)); + if (x1 >= 0 && x1 < state->width && + y1 >= 0 && y1 < state->height && + (barrier(state, GX(x1), GY(y1)) & dir)) + corner = TRUE; + } + } + + if (corner) { + /* + * At least one barrier terminates here. Draw a + * corner. + */ + draw_barrier_corner(fe, x, y, + X(dir)+X(A(dir)), Y(dir)+Y(A(dir)), + phase); + } + } + for (dir = 1; dir < 0x10; dir <<= 1) - if (barrier(state, x, y) & (dir << 4)) - draw_barrier_corner(fe, x, y, dir << 4, phase); - for (dir = 1; dir < 0x10; dir <<= 1) - if (barrier(state, x, y) & dir) + if (barrier(state, GX(x), GY(y)) & dir) draw_barrier(fe, x, y, dir, phase); } + unclip(fe); + draw_update(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER); } static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, game_state *state, int dir, game_ui *ui, float t, float ft) { - int x, y, tx, ty, frame, last_rotate_dir; + int x, y, tx, ty, frame, last_rotate_dir, moved_origin = FALSE; unsigned char *active; float angle = 0.0; /* - * Clear the screen and draw the exterior barrier lines if this - * is our first call. + * Clear the screen, and draw the exterior barrier lines, if + * this is our first call or if the origin has changed. */ - if (!ds->started) { + if (!ds->started || ui->org_x != ds->org_x || ui->org_y != ds->org_y) { int phase; ds->started = TRUE; @@ -2293,6 +2375,11 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, WINDOW_OFFSET * 2 + TILE_SIZE * state->width + TILE_BORDER, WINDOW_OFFSET * 2 + TILE_SIZE * state->height + TILE_BORDER, COL_BACKGROUND); + + ds->org_x = ui->org_x; + ds->org_y = ui->org_y; + moved_origin = TRUE; + draw_update(fe, 0, 0, WINDOW_OFFSET*2 + TILE_SIZE*state->width + TILE_BORDER, WINDOW_OFFSET*2 + TILE_SIZE*state->height + TILE_BORDER); @@ -2300,33 +2387,41 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, for (phase = 0; phase < 2; phase++) { for (x = 0; x < ds->width; x++) { - if (barrier(state, x, 0) & UL) - draw_barrier_corner(fe, x, -1, LD, phase); - if (barrier(state, x, 0) & RU) - draw_barrier_corner(fe, x, -1, DR, phase); - if (barrier(state, x, 0) & U) + if (x+1 < ds->width) { + if (barrier(state, GX(x), GY(0)) & R) + draw_barrier_corner(fe, x, -1, +1, +1, phase); + if (barrier(state, GX(x), GY(ds->height-1)) & R) + draw_barrier_corner(fe, x, ds->height, +1, -1, phase); + } + if (barrier(state, GX(x), GY(0)) & U) { + draw_barrier_corner(fe, x, -1, -1, +1, phase); + draw_barrier_corner(fe, x, -1, +1, +1, phase); draw_barrier(fe, x, -1, D, phase); - if (barrier(state, x, ds->height-1) & DR) - draw_barrier_corner(fe, x, ds->height, RU, phase); - if (barrier(state, x, ds->height-1) & LD) - draw_barrier_corner(fe, x, ds->height, UL, phase); - if (barrier(state, x, ds->height-1) & D) + } + if (barrier(state, GX(x), GY(ds->height-1)) & D) { + draw_barrier_corner(fe, x, ds->height, -1, -1, phase); + draw_barrier_corner(fe, x, ds->height, +1, -1, phase); draw_barrier(fe, x, ds->height, U, phase); + } } for (y = 0; y < ds->height; y++) { - if (barrier(state, 0, y) & UL) - draw_barrier_corner(fe, -1, y, RU, phase); - if (barrier(state, 0, y) & LD) - draw_barrier_corner(fe, -1, y, DR, phase); - if (barrier(state, 0, y) & L) + if (y+1 < ds->height) { + if (barrier(state, GX(0), GY(y)) & D) + draw_barrier_corner(fe, -1, y, +1, +1, phase); + if (barrier(state, GX(ds->width-1), GY(y)) & D) + draw_barrier_corner(fe, ds->width, y, -1, +1, phase); + } + if (barrier(state, GX(0), GY(y)) & L) { + draw_barrier_corner(fe, -1, y, +1, -1, phase); + draw_barrier_corner(fe, -1, y, +1, +1, phase); draw_barrier(fe, -1, y, R, phase); - if (barrier(state, ds->width-1, y) & RU) - draw_barrier_corner(fe, ds->width, y, UL, phase); - if (barrier(state, ds->width-1, y) & DR) - draw_barrier_corner(fe, ds->width, y, LD, phase); - if (barrier(state, ds->width-1, y) & R) + } + if (barrier(state, GX(ds->width-1), GY(y)) & R) { + draw_barrier_corner(fe, ds->width, y, -1, -1, phase); + draw_barrier_corner(fe, ds->width, y, -1, +1, phase); draw_barrier(fe, ds->width, y, L, phase); + } } } } @@ -2357,11 +2452,16 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, /* * Draw any tile which differs from the way it was last drawn. */ - active = compute_active(state); + active = compute_active(state, ui->cx, ui->cy); for (x = 0; x < ds->width; x++) for (y = 0; y < ds->height; y++) { - unsigned char c = tile(state, x, y) | index(state, active, x, y); + unsigned char c = tile(state, GX(x), GY(y)) | + index(state, active, GX(x), GY(y)); + int is_src = GX(x) == ui->cx && GY(y) == ui->cy; + int is_anim = GX(x) == tx && GY(y) == ty; + int is_cursor = ui->cur_visible && + GX(x) == ui->cur_x && GY(y) == ui->cur_y; /* * In a completion flash, we adjust the LOCKED bit @@ -2369,9 +2469,10 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, * the frame number. */ if (frame >= 0) { + int rcx = RX(ui->cx), rcy = RY(ui->cy); int xdist, ydist, dist; - xdist = (x < state->cx ? state->cx - x : x - state->cx); - ydist = (y < state->cy ? state->cy - y : y - state->cy); + xdist = (x < rcx ? rcx - x : x - rcx); + ydist = (y < rcy ? rcy - y : y - rcy); dist = (xdist > ydist ? xdist : ydist); if (frame >= dist && frame < dist+4) { @@ -2381,15 +2482,13 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, } } - if (index(state, ds->visible, x, y) != c || + if (moved_origin || + index(state, ds->visible, x, y) != c || index(state, ds->visible, x, y) == 0xFF || - (x == tx && y == ty) || - (ui->cur_visible && x == ui->cur_x && y == ui->cur_y)) { - draw_tile(fe, state, x, y, c, - (x == tx && y == ty ? angle : 0.0F), - (ui->cur_visible && x == ui->cur_x && y == ui->cur_y)); - if ((x == tx && y == ty) || - (ui->cur_visible && x == ui->cur_x && y == ui->cur_y)) + is_src || is_anim || is_cursor) { + draw_tile(fe, state, ds, x, y, c, + is_src, (is_anim ? angle : 0.0F), is_cursor); + if (is_src || is_anim || is_cursor) index(state, ds->visible, x, y) = 0xFF; else index(state, ds->visible, x, y) = c; @@ -2422,7 +2521,7 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, } static float game_anim_length(game_state *oldstate, - game_state *newstate, int dir) + game_state *newstate, int dir, game_ui *ui) { int last_rotate_dir; @@ -2445,7 +2544,7 @@ static float game_anim_length(game_state *oldstate, } static float game_flash_length(game_state *oldstate, - game_state *newstate, int dir) + game_state *newstate, int dir, game_ui *ui) { /* * If the game has just been completed, we display a completion @@ -2453,16 +2552,11 @@ static float game_flash_length(game_state *oldstate, */ if (!oldstate->completed && newstate->completed && !oldstate->used_solve && !newstate->used_solve) { - int size; - size = 0; - if (size < newstate->cx+1) - size = newstate->cx+1; - if (size < newstate->cy+1) - size = newstate->cy+1; - if (size < newstate->width - newstate->cx) - size = newstate->width - newstate->cx; - if (size < newstate->height - newstate->cy) - size = newstate->height - newstate->cy; + int size = 0; + if (size < newstate->width) + size = newstate->width; + if (size < newstate->height) + size = newstate->height; return FLASH_FRAME * (size+4); }