X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/28b5987d5dbdcb396c07b937a638590e5361361e..5b0ab052105670986ec973f035b758add4d25481:/net.c diff --git a/net.c b/net.c index 2e86700..ad970b6 100644 --- a/net.c +++ b/net.c @@ -80,7 +80,7 @@ struct game_params { struct game_state { int width, height, wrapping, completed; int last_rotate_x, last_rotate_y, last_rotate_dir; - int used_solve, just_used_solve; + int used_solve; unsigned char *tiles; unsigned char *barriers; }; @@ -293,7 +293,7 @@ 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->width <= 0 || params->height <= 0) return "Width and height must both be greater than zero"; @@ -347,7 +347,7 @@ static char *validate_params(game_params *params) * 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 && + if (full && 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"; @@ -382,29 +382,6 @@ static char *validate_params(game_params *params) * avoidance is required. */ -static int dsf_canonify(int *dsf, int val) -{ - int v2 = val; - - while (dsf[val] != val) - val = dsf[val]; - - while (v2 != val) { - int tmp = dsf[v2]; - dsf[v2] = val; - v2 = tmp; - } - - return val; -} - -static void dsf_merge(int *dsf, int v1, int v2) -{ - v1 = dsf_canonify(dsf, v1); - v2 = dsf_canonify(dsf, v2); - dsf[v2] = v1; -} - struct todo { unsigned char *marked; int *buffer; @@ -920,14 +897,7 @@ static void perturb(int w, int h, unsigned char *tiles, int wrapping, perim2 = snewn(nperim, struct xyd); memcpy(perim2, perimeter, nperim * sizeof(struct xyd)); /* Shuffle the perimeter, so as to search it without directional bias. */ - for (i = nperim; --i ;) { - int j = random_upto(rs, i+1); - struct xyd t; - - t = perim2[j]; - perim2[j] = perim2[i]; - perim2[i] = t; - } + shuffle(perim2, nperim, sizeof(*perim2), rs); for (i = 0; i < nperim; i++) { int x2, y2; @@ -1540,7 +1510,7 @@ static char *validate_desc(game_params *params, char *desc) * Construct an initial game state, given a description and parameters. */ -static game_state *new_game(midend_data *me, game_params *params, char *desc) +static game_state *new_game(midend *me, game_params *params, char *desc) { game_state *state; int w, h, x, y; @@ -1556,7 +1526,7 @@ static game_state *new_game(midend_data *me, game_params *params, char *desc) h = state->height = params->height; 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; + state->completed = state->used_solve = FALSE; state->tiles = snewn(state->width * state->height, unsigned char); memset(state->tiles, 0, state->width * state->height); state->barriers = snewn(state->width * state->height, unsigned char); @@ -1636,7 +1606,6 @@ static game_state *dup_game(game_state *state) ret->wrapping = state->wrapping; ret->completed = state->completed; ret->used_solve = state->used_solve; - ret->just_used_solve = state->just_used_solve; ret->last_rotate_dir = state->last_rotate_dir; ret->last_rotate_x = state->last_rotate_x; ret->last_rotate_y = state->last_rotate_y; @@ -1683,6 +1652,8 @@ static char *solve_game(game_state *state, game_state *currstate, tiles[i] = c - 'a' + 10; else if (c >= 'A' && c <= 'F') tiles[i] = c - 'A' + 10; + + tiles[i] |= LOCKED; } } @@ -1832,7 +1803,7 @@ static game_ui *new_ui(game_state *state) ui->cur_y = ui->cy = state->height / 2; ui->cur_visible = FALSE; get_random_seed(&seed, &seedsize); - ui->rs = random_init(seed, seedsize); + ui->rs = random_new(seed, seedsize); sfree(seed); return ui; @@ -1881,11 +1852,16 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, int x, int y, int button) { char *nullret; - int tx, ty; + int tx = -1, ty = -1, dir = 0; int shift = button & MOD_SHFT, ctrl = button & MOD_CTRL; + enum { + NONE, ROTATE_LEFT, ROTATE_180, ROTATE_RIGHT, TOGGLE_LOCK, JUMBLE, + MOVE_ORIGIN, MOVE_SOURCE, MOVE_ORIGIN_AND_SOURCE, MOVE_CURSOR + } action; button &= ~MOD_MASK; nullret = NULL; + action = NONE; if (button == LEFT_BUTTON || button == MIDDLE_BUTTON || @@ -1913,9 +1889,11 @@ static char *interpret_move(game_state *state, game_ui *ui, if (x % TILE_SIZE >= TILE_SIZE - TILE_BORDER || y % TILE_SIZE >= TILE_SIZE - TILE_BORDER) return nullret; + + action = button == LEFT_BUTTON ? ROTATE_LEFT : + button == RIGHT_BUTTON ? ROTATE_RIGHT : TOGGLE_LOCK; } else if (button == CURSOR_UP || button == CURSOR_DOWN || button == CURSOR_RIGHT || button == CURSOR_LEFT) { - int dir; switch (button) { case CURSOR_UP: dir = U; break; case CURSOR_DOWN: dir = D; break; @@ -1923,44 +1901,28 @@ static char *interpret_move(game_state *state, game_ui *ui, 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 ""; /* UI activity has occurred */ + if (shift && ctrl) action = MOVE_ORIGIN_AND_SOURCE; + else if (shift) action = MOVE_ORIGIN; + else if (ctrl) action = MOVE_SOURCE; + else action = MOVE_CURSOR; } else if (button == 'a' || button == 's' || button == 'd' || button == 'A' || button == 'S' || button == 'D' || + button == 'f' || button == 'F' || button == CURSOR_SELECT) { tx = ui->cur_x; ty = ui->cur_y; if (button == 'a' || button == 'A' || button == CURSOR_SELECT) - button = LEFT_BUTTON; + action = ROTATE_LEFT; else if (button == 's' || button == 'S') - button = MIDDLE_BUTTON; + action = TOGGLE_LOCK; else if (button == 'd' || button == 'D') - button = RIGHT_BUTTON; + action = ROTATE_RIGHT; + else if (button == 'f' || button == 'F') + action = ROTATE_180; ui->cur_visible = TRUE; } else if (button == 'j' || button == 'J') { /* XXX should we have some mouse control for this? */ - button = 'J'; /* canonify */ - tx = ty = -1; /* shut gcc up :( */ + action = JUMBLE; } else return nullret; @@ -1974,11 +1936,12 @@ static char *interpret_move(game_state *state, game_ui *ui, * accident. If they change their mind, another middle click * unlocks it.) */ - if (button == MIDDLE_BUTTON) { + if (action == TOGGLE_LOCK) { char buf[80]; sprintf(buf, "L%d,%d", tx, ty); return dupstr(buf); - } else if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { + } else if (action == ROTATE_LEFT || action == ROTATE_RIGHT || + action == ROTATE_180) { char buf[80]; /* @@ -1992,9 +1955,10 @@ static char *interpret_move(game_state *state, game_ui *ui, * Otherwise, turn the tile one way or the other. Left button * turns anticlockwise; right button turns clockwise. */ - sprintf(buf, "%c%d,%d", (button == LEFT_BUTTON ? 'A' : 'C'), tx, ty); + sprintf(buf, "%c%d,%d", (int)(action == ROTATE_LEFT ? 'A' : + action == ROTATE_RIGHT ? 'C' : 'F'), tx, ty); return dupstr(buf); - } else if (button == 'J') { + } else if (action == JUMBLE) { /* * Jumble all unlocked tiles to random orientations. */ @@ -2027,6 +1991,22 @@ static char *interpret_move(game_state *state, game_ui *ui, ret = sresize(ret, p - ret, char); return ret; + } else if (action == MOVE_ORIGIN || action == MOVE_SOURCE || + action == MOVE_ORIGIN_AND_SOURCE || action == MOVE_CURSOR) { + assert(dir != 0); + if (action == MOVE_ORIGIN || action == MOVE_ORIGIN_AND_SOURCE) { + 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 (action == MOVE_SOURCE || action == MOVE_ORIGIN_AND_SOURCE) { + OFFSET(ui->cx, ui->cy, ui->cx, ui->cy, dir, state); + } + if (action == MOVE_CURSOR) { + OFFSET(ui->cur_x, ui->cur_y, ui->cur_x, ui->cur_y, dir, state); + ui->cur_visible = TRUE; + } + return ""; } else { return NULL; } @@ -2038,11 +2018,10 @@ static game_state *execute_move(game_state *from, char *move) int tx, ty, n, noanim, orig; ret = dup_game(from); - ret->just_used_solve = FALSE; if (move[0] == 'J' || move[0] == 'S') { if (move[0] == 'S') - ret->just_used_solve = ret->used_solve = TRUE; + ret->used_solve = TRUE; move++; if (*move == ';') @@ -2066,10 +2045,8 @@ static game_state *execute_move(game_state *from, char *move) ret->last_rotate_dir = +1; } else if (move[0] == 'F') { tile(ret, tx, ty) = F(orig); - if (!noanim) { - free_game(ret); - return NULL; - } + if (!noanim) + ret->last_rotate_dir = +2; /* + for sake of argument */ } else if (move[0] == 'C') { tile(ret, tx, ty) = C(orig); if (!noanim) @@ -2125,7 +2102,7 @@ static game_state *execute_move(game_state *from, char *move) * Routines for drawing the game position on the screen. */ -static game_drawstate *game_new_drawstate(game_state *state) +static game_drawstate *game_new_drawstate(drawing *dr, game_state *state) { game_drawstate *ds = snew(game_drawstate); @@ -2140,34 +2117,26 @@ static game_drawstate *game_new_drawstate(game_state *state) return ds; } -static void game_free_drawstate(game_drawstate *ds) +static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->visible); sfree(ds); } -static void game_size(game_params *params, game_drawstate *ds, int *x, int *y, - int expand) +static void game_compute_size(game_params *params, int tilesize, + int *x, int *y) { - int tsx, tsy, ts; - /* - * Each window dimension equals the tile size times the grid - * dimension, plus TILE_BORDER, plus twice WINDOW_OFFSET. - */ - tsx = (*x - 2*WINDOW_OFFSET - TILE_BORDER) / params->width; - tsy = (*y - 2*WINDOW_OFFSET - TILE_BORDER) / params->height; - ts = min(tsx, tsy); - - if (expand) - ds->tilesize = ts; - else - ds->tilesize = min(ts, PREFERRED_TILE_SIZE); + *x = WINDOW_OFFSET * 2 + tilesize * params->width + TILE_BORDER; + *y = WINDOW_OFFSET * 2 + tilesize * params->height + TILE_BORDER; +} - *x = WINDOW_OFFSET * 2 + TILE_SIZE * params->width + TILE_BORDER; - *y = WINDOW_OFFSET * 2 + TILE_SIZE * params->height + TILE_BORDER; +static void game_set_size(drawing *dr, game_drawstate *ds, + game_params *params, int tilesize) +{ + ds->tilesize = tilesize; } -static float *game_colours(frontend *fe, game_state *state, int *ncolours) +static float *game_colours(frontend *fe, int *ncolours) { float *ret; @@ -2225,17 +2194,17 @@ static float *game_colours(frontend *fe, game_state *state, int *ncolours) return ret; } -static void draw_thick_line(frontend *fe, int x1, int y1, int x2, int y2, +static void draw_thick_line(drawing *dr, int x1, int y1, int x2, int y2, int colour) { - draw_line(fe, x1-1, y1, x2-1, y2, COL_WIRE); - draw_line(fe, x1+1, y1, x2+1, y2, COL_WIRE); - draw_line(fe, x1, y1-1, x2, y2-1, COL_WIRE); - draw_line(fe, x1, y1+1, x2, y2+1, COL_WIRE); - draw_line(fe, x1, y1, x2, y2, colour); + draw_line(dr, x1-1, y1, x2-1, y2, COL_WIRE); + draw_line(dr, x1+1, y1, x2+1, y2, COL_WIRE); + draw_line(dr, x1, y1-1, x2, y2-1, COL_WIRE); + draw_line(dr, x1, y1+1, x2, y2+1, COL_WIRE); + draw_line(dr, x1, y1, x2, y2, colour); } -static void draw_rect_coords(frontend *fe, int x1, int y1, int x2, int y2, +static void draw_rect_coords(drawing *dr, int x1, int y1, int x2, int y2, int colour) { int mx = (x1 < x2 ? x1 : x2); @@ -2243,13 +2212,13 @@ static void draw_rect_coords(frontend *fe, int x1, int y1, int x2, int y2, int dx = (x2 + x1 - 2*mx + 1); int dy = (y2 + y1 - 2*my + 1); - draw_rect(fe, mx, my, dx, dy, colour); + draw_rect(dr, mx, my, dx, dy, colour); } /* * draw_barrier_corner() and draw_barrier() are passed physical coords */ -static void draw_barrier_corner(frontend *fe, game_drawstate *ds, +static void draw_barrier_corner(drawing *dr, game_drawstate *ds, int x, int y, int dx, int dy, int phase) { int bx = WINDOW_OFFSET + TILE_SIZE * x; @@ -2260,20 +2229,20 @@ static void draw_barrier_corner(frontend *fe, game_drawstate *ds, y1 = (dy > 0 ? TILE_SIZE+TILE_BORDER-1 : 0); if (phase == 0) { - draw_rect_coords(fe, bx+x1+dx, by+y1, + draw_rect_coords(dr, 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+dy, + draw_rect_coords(dr, bx+x1, by+y1+dy, bx+x1-(TILE_BORDER-1)*dx, by+y1-TILE_BORDER*dy, COL_WIRE); } else { - draw_rect_coords(fe, bx+x1, by+y1, + draw_rect_coords(dr, bx+x1, by+y1, bx+x1-(TILE_BORDER-1)*dx, by+y1-(TILE_BORDER-1)*dy, COL_BARRIER); } } -static void draw_barrier(frontend *fe, game_drawstate *ds, +static void draw_barrier(drawing *dr, game_drawstate *ds, int x, int y, int dir, int phase) { int bx = WINDOW_OFFSET + TILE_SIZE * x; @@ -2286,16 +2255,16 @@ static void draw_barrier(frontend *fe, game_drawstate *ds, h = (Y(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER); if (phase == 0) { - draw_rect(fe, bx+x1-X(dir), by+y1-Y(dir), w, h, COL_WIRE); + draw_rect(dr, bx+x1-X(dir), by+y1-Y(dir), w, h, COL_WIRE); } else { - draw_rect(fe, bx+x1, by+y1, w, h, COL_BARRIER); + draw_rect(dr, bx+x1, by+y1, w, h, COL_BARRIER); } } /* * draw_tile() is passed physical coordinates */ -static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds, +static void draw_tile(drawing *dr, 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; @@ -2311,16 +2280,16 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds, * we must draw those connections on the borders themselves. */ - clip(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER); + clip(dr, 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 * background colour to fill it in. */ - draw_rect(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER, + draw_rect(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER, COL_BORDER); - draw_rect(fe, bx+TILE_BORDER, by+TILE_BORDER, + draw_rect(dr, bx+TILE_BORDER, by+TILE_BORDER, TILE_SIZE-TILE_BORDER, TILE_SIZE-TILE_BORDER, tile & LOCKED ? COL_LOCKED : COL_BACKGROUND); @@ -2330,16 +2299,16 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds, * in. */ if (cursor) { - draw_line(fe, bx+TILE_SIZE/8, by+TILE_SIZE/8, + draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE/8, bx+TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8, tile & LOCKED ? COL_BACKGROUND : COL_LOCKED); - draw_line(fe, bx+TILE_SIZE/8, by+TILE_SIZE/8, + draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE/8, bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE/8, tile & LOCKED ? COL_BACKGROUND : COL_LOCKED); - draw_line(fe, bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE/8, + draw_line(dr, bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE/8, bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8, tile & LOCKED ? COL_BACKGROUND : COL_LOCKED); - draw_line(fe, bx+TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8, + draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8, bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8, tile & LOCKED ? COL_BACKGROUND : COL_LOCKED); } @@ -2362,7 +2331,7 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds, ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir); ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir); MATMUL(tx, ty, matrix, ex, ey); - draw_thick_line(fe, bx+(int)cx, by+(int)cy, + draw_thick_line(dr, bx+(int)cx, by+(int)cy, bx+(int)(cx+tx), by+(int)(cy+ty), COL_WIRE); } @@ -2372,7 +2341,7 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds, ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir); ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir); MATMUL(tx, ty, matrix, ex, ey); - draw_line(fe, bx+(int)cx, by+(int)cy, + draw_line(dr, bx+(int)cx, by+(int)cy, bx+(int)(cx+tx), by+(int)(cy+ty), col); } } @@ -2405,7 +2374,7 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds, points[i+1] = by+(int)(cy+ty); } - draw_polygon(fe, points, 4, col, COL_WIRE); + draw_polygon(dr, points, 4, col, COL_WIRE); } /* @@ -2442,8 +2411,8 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds, * in: if we are fully connected to the other tile then * the two ACTIVE states will be the same.) */ - draw_rect_coords(fe, px-vx, py-vy, px+lx+vx, py+ly+vy, COL_WIRE); - draw_rect_coords(fe, px, py, px+lx, py+ly, + draw_rect_coords(dr, px-vx, py-vy, px+lx+vx, py+ly+vy, COL_WIRE); + draw_rect_coords(dr, px, py, px+lx, py+ly, (tile & ACTIVE) ? COL_POWERED : COL_WIRE); } else { /* @@ -2451,7 +2420,7 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds, * actually connected to us. Just draw a single black * dot. */ - draw_rect_coords(fe, px, py, px, py, COL_WIRE); + draw_rect_coords(dr, px, py, px, py, COL_WIRE); } } @@ -2495,7 +2464,7 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds, * At least one barrier terminates here. Draw a * corner. */ - draw_barrier_corner(fe, ds, x, y, + draw_barrier_corner(dr, ds, x, y, X(dir)+X(A(dir)), Y(dir)+Y(A(dir)), phase); } @@ -2503,15 +2472,15 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds, for (dir = 1; dir < 0x10; dir <<= 1) if (barrier(state, GX(x), GY(y)) & dir) - draw_barrier(fe, ds, x, y, dir, phase); + draw_barrier(dr, ds, x, y, dir, phase); } - unclip(fe); + unclip(dr); - draw_update(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER); + draw_update(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER); } -static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, +static void game_redraw(drawing *dr, 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, moved_origin = FALSE; @@ -2527,7 +2496,7 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, ds->started = TRUE; - draw_rect(fe, 0, 0, + draw_rect(dr, 0, 0, WINDOW_OFFSET * 2 + TILE_SIZE * state->width + TILE_BORDER, WINDOW_OFFSET * 2 + TILE_SIZE * state->height + TILE_BORDER, COL_BACKGROUND); @@ -2536,7 +2505,7 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, ds->org_y = ui->org_y; moved_origin = TRUE; - draw_update(fe, 0, 0, + draw_update(dr, 0, 0, WINDOW_OFFSET*2 + TILE_SIZE*state->width + TILE_BORDER, WINDOW_OFFSET*2 + TILE_SIZE*state->height + TILE_BORDER); @@ -2545,38 +2514,38 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, for (x = 0; x < ds->width; x++) { if (x+1 < ds->width) { if (barrier(state, GX(x), GY(0)) & R) - draw_barrier_corner(fe, ds, x, -1, +1, +1, phase); + draw_barrier_corner(dr, ds, x, -1, +1, +1, phase); if (barrier(state, GX(x), GY(ds->height-1)) & R) - draw_barrier_corner(fe, ds, x, ds->height, +1, -1, phase); + draw_barrier_corner(dr, ds, x, ds->height, +1, -1, phase); } if (barrier(state, GX(x), GY(0)) & U) { - draw_barrier_corner(fe, ds, x, -1, -1, +1, phase); - draw_barrier_corner(fe, ds, x, -1, +1, +1, phase); - draw_barrier(fe, ds, x, -1, D, phase); + draw_barrier_corner(dr, ds, x, -1, -1, +1, phase); + draw_barrier_corner(dr, ds, x, -1, +1, +1, phase); + draw_barrier(dr, ds, x, -1, D, phase); } if (barrier(state, GX(x), GY(ds->height-1)) & D) { - draw_barrier_corner(fe, ds, x, ds->height, -1, -1, phase); - draw_barrier_corner(fe, ds, x, ds->height, +1, -1, phase); - draw_barrier(fe, ds, x, ds->height, U, phase); + draw_barrier_corner(dr, ds, x, ds->height, -1, -1, phase); + draw_barrier_corner(dr, ds, x, ds->height, +1, -1, phase); + draw_barrier(dr, ds, x, ds->height, U, phase); } } for (y = 0; y < ds->height; y++) { if (y+1 < ds->height) { if (barrier(state, GX(0), GY(y)) & D) - draw_barrier_corner(fe, ds, -1, y, +1, +1, phase); + draw_barrier_corner(dr, ds, -1, y, +1, +1, phase); if (barrier(state, GX(ds->width-1), GY(y)) & D) - draw_barrier_corner(fe, ds, ds->width, y, -1, +1, phase); + draw_barrier_corner(dr, ds, ds->width, y, -1, +1, phase); } if (barrier(state, GX(0), GY(y)) & L) { - draw_barrier_corner(fe, ds, -1, y, +1, -1, phase); - draw_barrier_corner(fe, ds, -1, y, +1, +1, phase); - draw_barrier(fe, ds, -1, y, R, phase); + draw_barrier_corner(dr, ds, -1, y, +1, -1, phase); + draw_barrier_corner(dr, ds, -1, y, +1, +1, phase); + draw_barrier(dr, ds, -1, y, R, phase); } if (barrier(state, GX(ds->width-1), GY(y)) & R) { - draw_barrier_corner(fe, ds, ds->width, y, -1, -1, phase); - draw_barrier_corner(fe, ds, ds->width, y, -1, +1, phase); - draw_barrier(fe, ds, ds->width, y, L, phase); + draw_barrier_corner(dr, ds, ds->width, y, -1, -1, phase); + draw_barrier_corner(dr, ds, ds->width, y, -1, +1, phase); + draw_barrier(dr, ds, ds->width, y, L, phase); } } } @@ -2642,7 +2611,7 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, index(state, ds->visible, x, y) != c || index(state, ds->visible, x, y) == 0xFF || is_src || is_anim || is_cursor) { - draw_tile(fe, state, ds, x, y, c, + draw_tile(dr, 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; @@ -2670,7 +2639,7 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, (state->used_solve ? "Auto-solved. " : state->completed ? "COMPLETED! " : ""), a, n2); - status_bar(fe, statusbuf); + status_bar(dr, statusbuf); } sfree(active); @@ -2682,13 +2651,6 @@ static float game_anim_length(game_state *oldstate, int last_rotate_dir; /* - * Don't animate an auto-solve move. - */ - if ((dir > 0 && newstate->just_used_solve) || - (dir < 0 && oldstate->just_used_solve)) - return 0.0F; - - /* * Don't animate if last_rotate_dir is zero. */ last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir : @@ -2719,14 +2681,144 @@ static float game_flash_length(game_state *oldstate, return 0.0F; } -static int game_wants_statusbar(void) +static int game_timing_state(game_state *state, game_ui *ui) { return TRUE; } -static int game_timing_state(game_state *state) +static void game_print_size(game_params *params, float *x, float *y) { - return TRUE; + int pw, ph; + + /* + * I'll use 8mm squares by default. + */ + game_compute_size(params, 800, &pw, &ph); + *x = pw / 100.0; + *y = ph / 100.0; +} + +static void draw_diagram(drawing *dr, game_drawstate *ds, int x, int y, + int topleft, int v, int drawlines, int ink) +{ + int tx, ty, cx, cy, r, br, k, thick; + + tx = WINDOW_OFFSET + TILE_SIZE * x; + ty = WINDOW_OFFSET + TILE_SIZE * y; + + /* + * Find our centre point. + */ + if (topleft) { + cx = tx + (v & L ? TILE_SIZE / 4 : TILE_SIZE / 6); + cy = ty + (v & U ? TILE_SIZE / 4 : TILE_SIZE / 6); + r = TILE_SIZE / 8; + br = TILE_SIZE / 32; + } else { + cx = tx + TILE_SIZE / 2; + cy = ty + TILE_SIZE / 2; + r = TILE_SIZE / 2; + br = TILE_SIZE / 8; + } + thick = r / 20; + + /* + * Draw the square block if we have an endpoint. + */ + if (v == 1 || v == 2 || v == 4 || v == 8) + draw_rect(dr, cx - br, cy - br, br*2, br*2, ink); + + /* + * Draw each radial line. + */ + if (drawlines) { + for (k = 1; k < 16; k *= 2) + if (v & k) { + int x1 = min(cx, cx + (r-thick) * X(k)); + int x2 = max(cx, cx + (r-thick) * X(k)); + int y1 = min(cy, cy + (r-thick) * Y(k)); + int y2 = max(cy, cy + (r-thick) * Y(k)); + draw_rect(dr, x1 - thick, y1 - thick, + (x2 - x1) + 2*thick, (y2 - y1) + 2*thick, ink); + } + } +} + +static void game_print(drawing *dr, game_state *state, int tilesize) +{ + int w = state->width, h = state->height; + int ink = print_mono_colour(dr, 0); + int x, y; + + /* Ick: fake up `ds->tilesize' for macro expansion purposes */ + game_drawstate ads, *ds = &ads; + game_set_size(dr, ds, NULL, tilesize); + + /* + * Border. + */ + print_line_width(dr, TILE_SIZE / (state->wrapping ? 128 : 12)); + draw_rect_outline(dr, WINDOW_OFFSET, WINDOW_OFFSET, + TILE_SIZE * w, TILE_SIZE * h, ink); + + /* + * Grid. + */ + print_line_width(dr, TILE_SIZE / 128); + for (x = 1; x < w; x++) + draw_line(dr, WINDOW_OFFSET + TILE_SIZE * x, WINDOW_OFFSET, + WINDOW_OFFSET + TILE_SIZE * x, WINDOW_OFFSET + TILE_SIZE * h, + ink); + for (y = 1; y < h; y++) + draw_line(dr, WINDOW_OFFSET, WINDOW_OFFSET + TILE_SIZE * y, + WINDOW_OFFSET + TILE_SIZE * w, WINDOW_OFFSET + TILE_SIZE * y, + ink); + + /* + * Barriers. + */ + for (y = 0; y <= h; y++) + for (x = 0; x <= w; x++) { + int b = barrier(state, x % w, y % h); + if (x < w && (b & U)) + draw_rect(dr, WINDOW_OFFSET + TILE_SIZE * x - TILE_SIZE/24, + WINDOW_OFFSET + TILE_SIZE * y - TILE_SIZE/24, + TILE_SIZE + TILE_SIZE/24 * 2, TILE_SIZE/24 * 2, ink); + if (y < h && (b & L)) + draw_rect(dr, WINDOW_OFFSET + TILE_SIZE * x - TILE_SIZE/24, + WINDOW_OFFSET + TILE_SIZE * y - TILE_SIZE/24, + TILE_SIZE/24 * 2, TILE_SIZE + TILE_SIZE/24 * 2, ink); + } + + /* + * Grid contents. + */ + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) { + int vx, v = tile(state, x, y); + int locked = v & LOCKED; + + v &= 0xF; + + /* + * Rotate into a standard orientation for the top left + * corner diagram. + */ + vx = v; + while (vx != 0 && vx != 15 && vx != 1 && vx != 9 && vx != 13 && + vx != 5) + vx = A(vx); + + /* + * Draw the top left corner diagram. + */ + draw_diagram(dr, ds, x, y, TRUE, vx, TRUE, ink); + + /* + * Draw the real solution diagram, if we're doing so. + */ + draw_diagram(dr, ds, x, y, FALSE, v, locked, ink); + } } #ifdef COMBINED @@ -2757,14 +2849,15 @@ const struct game thegame = { game_changed_state, interpret_move, execute_move, - game_size, + PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, - game_wants_statusbar, + TRUE, FALSE, game_print_size, game_print, + TRUE, /* wants_statusbar */ FALSE, game_timing_state, - 0, /* mouse_priorities */ + 0, /* flags */ };