diff options
Diffstat (limited to 'apps/plugins/puzzles/src/net.c')
-rw-r--r-- | apps/plugins/puzzles/src/net.c | 231 |
1 files changed, 148 insertions, 83 deletions
diff --git a/apps/plugins/puzzles/src/net.c b/apps/plugins/puzzles/src/net.c index d3032b6fe2..3200253ef9 100644 --- a/apps/plugins/puzzles/src/net.c +++ b/apps/plugins/puzzles/src/net.c | |||
@@ -7,7 +7,12 @@ | |||
7 | #include <string.h> | 7 | #include <string.h> |
8 | #include <assert.h> | 8 | #include <assert.h> |
9 | #include <ctype.h> | 9 | #include <ctype.h> |
10 | #include <math.h> | 10 | #include <limits.h> |
11 | #ifdef NO_TGMATH_H | ||
12 | # include <math.h> | ||
13 | #else | ||
14 | # include <tgmath.h> | ||
15 | #endif | ||
11 | 16 | ||
12 | #include "puzzles.h" | 17 | #include "puzzles.h" |
13 | #include "tree234.h" | 18 | #include "tree234.h" |
@@ -249,10 +254,7 @@ static char *encode_params(const game_params *params, bool full) | |||
249 | if (params->wrapping) | 254 | if (params->wrapping) |
250 | ret[len++] = 'w'; | 255 | ret[len++] = 'w'; |
251 | if (full && params->barrier_probability) | 256 | if (full && params->barrier_probability) |
252 | { | 257 | len += sprintf(ret+len, "b%g", params->barrier_probability); |
253 | len += sprintf(ret+len, "b"); | ||
254 | len += ftoa(ret + len, params->barrier_probability); | ||
255 | } | ||
256 | if (full && !params->unique) | 258 | if (full && !params->unique) |
257 | ret[len++] = 'a'; | 259 | ret[len++] = 'a'; |
258 | assert(len < lenof(ret)); | 260 | assert(len < lenof(ret)); |
@@ -284,7 +286,7 @@ static config_item *game_configure(const game_params *params) | |||
284 | 286 | ||
285 | ret[3].name = "Barrier probability"; | 287 | ret[3].name = "Barrier probability"; |
286 | ret[3].type = C_STRING; | 288 | ret[3].type = C_STRING; |
287 | ftoa(buf, params->barrier_probability); | 289 | sprintf(buf, "%g", params->barrier_probability); |
288 | ret[3].u.string.sval = dupstr(buf); | 290 | ret[3].u.string.sval = dupstr(buf); |
289 | 291 | ||
290 | ret[4].name = "Ensure unique solution"; | 292 | ret[4].name = "Ensure unique solution"; |
@@ -316,6 +318,8 @@ static const char *validate_params(const game_params *params, bool full) | |||
316 | return "Width and height must both be greater than zero"; | 318 | return "Width and height must both be greater than zero"; |
317 | if (params->width <= 1 && params->height <= 1) | 319 | if (params->width <= 1 && params->height <= 1) |
318 | return "At least one of width and height must be greater than one"; | 320 | return "At least one of width and height must be greater than one"; |
321 | if (params->width > INT_MAX / params->height) | ||
322 | return "Width times height must not be unreasonably large"; | ||
319 | if (params->barrier_probability < 0) | 323 | if (params->barrier_probability < 0) |
320 | return "Barrier probability may not be negative"; | 324 | return "Barrier probability may not be negative"; |
321 | if (params->barrier_probability > 1) | 325 | if (params->barrier_probability > 1) |
@@ -458,7 +462,7 @@ static int net_solver(int w, int h, unsigned char *tiles, | |||
458 | unsigned char *tilestate; | 462 | unsigned char *tilestate; |
459 | unsigned char *edgestate; | 463 | unsigned char *edgestate; |
460 | int *deadends; | 464 | int *deadends; |
461 | int *equivalence; | 465 | DSF *equivalence; |
462 | struct todo *todo; | 466 | struct todo *todo; |
463 | int i, j, x, y; | 467 | int i, j, x, y; |
464 | int area; | 468 | int area; |
@@ -543,7 +547,7 @@ static int net_solver(int w, int h, unsigned char *tiles, | |||
543 | * classes) by finding the representative of each tile and | 547 | * classes) by finding the representative of each tile and |
544 | * setting equivalence[one]=the_other. | 548 | * setting equivalence[one]=the_other. |
545 | */ | 549 | */ |
546 | equivalence = snew_dsf(w * h); | 550 | equivalence = dsf_new(w * h); |
547 | 551 | ||
548 | /* | 552 | /* |
549 | * On a non-wrapping grid, we instantly know that all the edges | 553 | * On a non-wrapping grid, we instantly know that all the edges |
@@ -828,7 +832,7 @@ static int net_solver(int w, int h, unsigned char *tiles, | |||
828 | sfree(tilestate); | 832 | sfree(tilestate); |
829 | sfree(edgestate); | 833 | sfree(edgestate); |
830 | sfree(deadends); | 834 | sfree(deadends); |
831 | sfree(equivalence); | 835 | dsf_free(equivalence); |
832 | 836 | ||
833 | return j; | 837 | return j; |
834 | } | 838 | } |
@@ -1131,7 +1135,8 @@ static void perturb(int w, int h, unsigned char *tiles, bool wrapping, | |||
1131 | 1135 | ||
1132 | static int *compute_loops_inner(int w, int h, bool wrapping, | 1136 | static int *compute_loops_inner(int w, int h, bool wrapping, |
1133 | const unsigned char *tiles, | 1137 | const unsigned char *tiles, |
1134 | const unsigned char *barriers); | 1138 | const unsigned char *barriers, |
1139 | bool include_unlocked_squares); | ||
1135 | 1140 | ||
1136 | static char *new_game_desc(const game_params *params, random_state *rs, | 1141 | static char *new_game_desc(const game_params *params, random_state *rs, |
1137 | char **aux, bool interactive) | 1142 | char **aux, bool interactive) |
@@ -1460,7 +1465,8 @@ static char *new_game_desc(const game_params *params, random_state *rs, | |||
1460 | */ | 1465 | */ |
1461 | prev_loopsquares = w*h+1; | 1466 | prev_loopsquares = w*h+1; |
1462 | while (1) { | 1467 | while (1) { |
1463 | loops = compute_loops_inner(w, h, params->wrapping, tiles, NULL); | 1468 | loops = compute_loops_inner(w, h, params->wrapping, tiles, NULL, |
1469 | true); | ||
1464 | this_loopsquares = 0; | 1470 | this_loopsquares = 0; |
1465 | for (i = 0; i < w*h; i++) { | 1471 | for (i = 0; i < w*h; i++) { |
1466 | if (loops[i]) { | 1472 | if (loops[i]) { |
@@ -1848,16 +1854,6 @@ static char *solve_game(const game_state *state, const game_state *currstate, | |||
1848 | return ret; | 1854 | return ret; |
1849 | } | 1855 | } |
1850 | 1856 | ||
1851 | static bool game_can_format_as_text_now(const game_params *params) | ||
1852 | { | ||
1853 | return true; | ||
1854 | } | ||
1855 | |||
1856 | static char *game_text_format(const game_state *state) | ||
1857 | { | ||
1858 | return NULL; | ||
1859 | } | ||
1860 | |||
1861 | /* ---------------------------------------------------------------------- | 1857 | /* ---------------------------------------------------------------------- |
1862 | * Utility routine. | 1858 | * Utility routine. |
1863 | */ | 1859 | */ |
@@ -1878,6 +1874,8 @@ static unsigned char *compute_active(const game_state *state, int cx, int cy) | |||
1878 | active = snewn(state->width * state->height, unsigned char); | 1874 | active = snewn(state->width * state->height, unsigned char); |
1879 | memset(active, 0, state->width * state->height); | 1875 | memset(active, 0, state->width * state->height); |
1880 | 1876 | ||
1877 | assert(0 <= cx && cx < state->width); | ||
1878 | assert(0 <= cy && cy < state->height); | ||
1881 | /* | 1879 | /* |
1882 | * We only store (x,y) pairs in todo, but it's easier to reuse | 1880 | * We only store (x,y) pairs in todo, but it's easier to reuse |
1883 | * xyd_cmp and just store direction 0 every time. | 1881 | * xyd_cmp and just store direction 0 every time. |
@@ -1923,6 +1921,7 @@ struct net_neighbour_ctx { | |||
1923 | int w, h; | 1921 | int w, h; |
1924 | const unsigned char *tiles, *barriers; | 1922 | const unsigned char *tiles, *barriers; |
1925 | int i, n, neighbours[4]; | 1923 | int i, n, neighbours[4]; |
1924 | bool include_unlocked_squares; | ||
1926 | }; | 1925 | }; |
1927 | static int net_neighbour(int vertex, void *vctx) | 1926 | static int net_neighbour(int vertex, void *vctx) |
1928 | { | 1927 | { |
@@ -1943,6 +1942,9 @@ static int net_neighbour(int vertex, void *vctx) | |||
1943 | continue; | 1942 | continue; |
1944 | OFFSETWH(x1, y1, x, y, dir, ctx->w, ctx->h); | 1943 | OFFSETWH(x1, y1, x, y, dir, ctx->w, ctx->h); |
1945 | v1 = y1 * ctx->w + x1; | 1944 | v1 = y1 * ctx->w + x1; |
1945 | if (!ctx->include_unlocked_squares && | ||
1946 | !(tile & ctx->tiles[v1] & LOCKED)) | ||
1947 | continue; | ||
1946 | if (ctx->tiles[v1] & F(dir)) | 1948 | if (ctx->tiles[v1] & F(dir)) |
1947 | ctx->neighbours[ctx->n++] = v1; | 1949 | ctx->neighbours[ctx->n++] = v1; |
1948 | } | 1950 | } |
@@ -1956,32 +1958,39 @@ static int net_neighbour(int vertex, void *vctx) | |||
1956 | 1958 | ||
1957 | static int *compute_loops_inner(int w, int h, bool wrapping, | 1959 | static int *compute_loops_inner(int w, int h, bool wrapping, |
1958 | const unsigned char *tiles, | 1960 | const unsigned char *tiles, |
1959 | const unsigned char *barriers) | 1961 | const unsigned char *barriers, |
1962 | bool include_unlocked_squares) | ||
1960 | { | 1963 | { |
1961 | struct net_neighbour_ctx ctx; | 1964 | struct net_neighbour_ctx ctx; |
1962 | struct findloopstate *fls; | 1965 | struct findloopstate *fls; |
1963 | int *loops; | 1966 | int *loops; |
1964 | int x, y; | 1967 | int x, y, v; |
1965 | 1968 | ||
1966 | fls = findloop_new_state(w*h); | 1969 | fls = findloop_new_state(w*h); |
1967 | ctx.w = w; | 1970 | ctx.w = w; |
1968 | ctx.h = h; | 1971 | ctx.h = h; |
1969 | ctx.tiles = tiles; | 1972 | ctx.tiles = tiles; |
1970 | ctx.barriers = barriers; | 1973 | ctx.barriers = barriers; |
1974 | ctx.include_unlocked_squares = include_unlocked_squares; | ||
1971 | findloop_run(fls, w*h, net_neighbour, &ctx); | 1975 | findloop_run(fls, w*h, net_neighbour, &ctx); |
1972 | 1976 | ||
1973 | loops = snewn(w*h, int); | 1977 | loops = snewn(w*h, int); |
1974 | 1978 | ||
1975 | for (y = 0; y < h; y++) { | 1979 | for (y = 0; y < h; y++) { |
1976 | for (x = 0; x < w; x++) { | 1980 | for (x = 0; x < w; x++) { |
1977 | int x1, y1, dir; | 1981 | int x1, y1, v1, dir; |
1978 | int flags = 0; | 1982 | int flags = 0; |
1979 | 1983 | ||
1984 | v = y * w + x; | ||
1980 | for (dir = 1; dir < 0x10; dir <<= 1) { | 1985 | for (dir = 1; dir < 0x10; dir <<= 1) { |
1981 | if ((tiles[y*w+x] & dir) && | 1986 | if ((tiles[v] & dir) && |
1982 | !(barriers && (barriers[y*w+x] & dir))) { | 1987 | !(barriers && (barriers[y*w+x] & dir))) { |
1983 | OFFSETWH(x1, y1, x, y, dir, w, h); | 1988 | OFFSETWH(x1, y1, x, y, dir, w, h); |
1984 | if ((tiles[y1*w+x1] & F(dir)) && | 1989 | v1 = y1 * w + x1; |
1990 | if (!include_unlocked_squares && | ||
1991 | !(tiles[v] & tiles[v1] & LOCKED)) | ||
1992 | continue; | ||
1993 | if ((tiles[v1] & F(dir)) && | ||
1985 | findloop_is_loop_edge(fls, y*w+x, y1*w+x1)) | 1994 | findloop_is_loop_edge(fls, y*w+x, y1*w+x1)) |
1986 | flags |= ERR(dir); | 1995 | flags |= ERR(dir); |
1987 | } | 1996 | } |
@@ -1994,10 +2003,12 @@ static int *compute_loops_inner(int w, int h, bool wrapping, | |||
1994 | return loops; | 2003 | return loops; |
1995 | } | 2004 | } |
1996 | 2005 | ||
1997 | static int *compute_loops(const game_state *state) | 2006 | static int *compute_loops(const game_state *state, |
2007 | bool include_unlocked_squares) | ||
1998 | { | 2008 | { |
1999 | return compute_loops_inner(state->width, state->height, state->wrapping, | 2009 | return compute_loops_inner(state->width, state->height, state->wrapping, |
2000 | state->tiles, state->imm->barriers); | 2010 | state->tiles, state->imm->barriers, |
2011 | include_unlocked_squares); | ||
2001 | } | 2012 | } |
2002 | 2013 | ||
2003 | struct game_ui { | 2014 | struct game_ui { |
@@ -2010,6 +2021,8 @@ struct game_ui { | |||
2010 | int dragtilex, dragtiley, dragstartx, dragstarty; | 2021 | int dragtilex, dragtiley, dragstartx, dragstarty; |
2011 | bool dragged; | 2022 | bool dragged; |
2012 | #endif | 2023 | #endif |
2024 | |||
2025 | bool unlocked_loops; | ||
2013 | }; | 2026 | }; |
2014 | 2027 | ||
2015 | static game_ui *new_ui(const game_state *state) | 2028 | static game_ui *new_ui(const game_state *state) |
@@ -2017,20 +2030,31 @@ static game_ui *new_ui(const game_state *state) | |||
2017 | void *seed; | 2030 | void *seed; |
2018 | int seedsize; | 2031 | int seedsize; |
2019 | game_ui *ui = snew(game_ui); | 2032 | game_ui *ui = snew(game_ui); |
2020 | ui->org_x = ui->org_y = 0; | 2033 | |
2021 | ui->cur_x = ui->cx = state->width / 2; | 2034 | ui->unlocked_loops = true; |
2022 | ui->cur_y = ui->cy = state->height / 2; | 2035 | |
2023 | ui->cur_visible = false; | 2036 | if (state) { |
2024 | get_random_seed(&seed, &seedsize); | 2037 | ui->org_x = ui->org_y = 0; |
2025 | ui->rs = random_new(seed, seedsize); | 2038 | ui->cur_x = ui->cx = state->width / 2; |
2026 | sfree(seed); | 2039 | ui->cur_y = ui->cy = state->height / 2; |
2040 | ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false); | ||
2041 | get_random_seed(&seed, &seedsize); | ||
2042 | ui->rs = random_new(seed, seedsize); | ||
2043 | sfree(seed); | ||
2044 | #ifdef USE_DRAGGING | ||
2045 | ui->dragstartx = ui->dragstarty = ui->dragtilex = ui->dragtiley = -1; | ||
2046 | #endif | ||
2047 | } else { | ||
2048 | ui->rs = NULL; | ||
2049 | } | ||
2027 | 2050 | ||
2028 | return ui; | 2051 | return ui; |
2029 | } | 2052 | } |
2030 | 2053 | ||
2031 | static void free_ui(game_ui *ui) | 2054 | static void free_ui(game_ui *ui) |
2032 | { | 2055 | { |
2033 | random_free(ui->rs); | 2056 | if (ui->rs) |
2057 | random_free(ui->rs); | ||
2034 | sfree(ui); | 2058 | sfree(ui); |
2035 | } | 2059 | } |
2036 | 2060 | ||
@@ -2045,10 +2069,45 @@ static char *encode_ui(const game_ui *ui) | |||
2045 | return dupstr(buf); | 2069 | return dupstr(buf); |
2046 | } | 2070 | } |
2047 | 2071 | ||
2048 | static void decode_ui(game_ui *ui, const char *encoding) | 2072 | static void decode_ui(game_ui *ui, const char *encoding, |
2073 | const game_state *state) | ||
2049 | { | 2074 | { |
2050 | sscanf(encoding, "O%d,%d;C%d,%d", | 2075 | int org_x, org_y, cx, cy; |
2051 | &ui->org_x, &ui->org_y, &ui->cx, &ui->cy); | 2076 | |
2077 | if (sscanf(encoding, "O%d,%d;C%d,%d", &org_x, &org_y, &cx, &cy) == 4) { | ||
2078 | if (0 <= org_x && org_x < state->width && | ||
2079 | 0 <= org_y && org_y < state->height) { | ||
2080 | ui->org_x = org_x; | ||
2081 | ui->org_y = org_y; | ||
2082 | } | ||
2083 | if (0 <= cx && cx < state->width && | ||
2084 | 0 <= cy && cy < state->height) { | ||
2085 | ui->cx = cx; | ||
2086 | ui->cy = cy; | ||
2087 | } | ||
2088 | } | ||
2089 | } | ||
2090 | |||
2091 | static config_item *get_prefs(game_ui *ui) | ||
2092 | { | ||
2093 | config_item *ret; | ||
2094 | |||
2095 | ret = snewn(2, config_item); | ||
2096 | |||
2097 | ret[0].name = "Highlight loops involving unlocked squares"; | ||
2098 | ret[0].kw = "unlocked-loops"; | ||
2099 | ret[0].type = C_BOOLEAN; | ||
2100 | ret[0].u.boolean.bval = ui->unlocked_loops; | ||
2101 | |||
2102 | ret[1].name = NULL; | ||
2103 | ret[1].type = C_END; | ||
2104 | |||
2105 | return ret; | ||
2106 | } | ||
2107 | |||
2108 | static void set_prefs(game_ui *ui, const config_item *cfg) | ||
2109 | { | ||
2110 | ui->unlocked_loops = cfg[0].u.boolean.bval; | ||
2052 | } | 2111 | } |
2053 | 2112 | ||
2054 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | 2113 | static void game_changed_state(game_ui *ui, const game_state *oldstate, |
@@ -2056,8 +2115,19 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, | |||
2056 | { | 2115 | { |
2057 | } | 2116 | } |
2058 | 2117 | ||
2118 | static const char *current_key_label(const game_ui *ui, | ||
2119 | const game_state *state, int button) | ||
2120 | { | ||
2121 | if (tile(state, ui->cur_x, ui->cur_y) & LOCKED) { | ||
2122 | if (button == CURSOR_SELECT2) return "Unlock"; | ||
2123 | } else { | ||
2124 | if (button == CURSOR_SELECT) return "Rotate"; | ||
2125 | if (button == CURSOR_SELECT2) return "Lock"; | ||
2126 | } | ||
2127 | return ""; | ||
2128 | } | ||
2129 | |||
2059 | struct game_drawstate { | 2130 | struct game_drawstate { |
2060 | bool started; | ||
2061 | int width, height; | 2131 | int width, height; |
2062 | int tilesize; | 2132 | int tilesize; |
2063 | unsigned long *visible, *to_draw; | 2133 | unsigned long *visible, *to_draw; |
@@ -2078,7 +2148,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
2078 | MOVE_ORIGIN, MOVE_SOURCE, MOVE_ORIGIN_AND_SOURCE, MOVE_CURSOR | 2148 | MOVE_ORIGIN, MOVE_SOURCE, MOVE_ORIGIN_AND_SOURCE, MOVE_CURSOR |
2079 | } action; | 2149 | } action; |
2080 | 2150 | ||
2081 | button &= ~MOD_MASK; | 2151 | button = STRIP_BUTTON_MODIFIERS(button); |
2082 | nullret = NULL; | 2152 | nullret = NULL; |
2083 | action = NONE; | 2153 | action = NONE; |
2084 | 2154 | ||
@@ -2094,7 +2164,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
2094 | 2164 | ||
2095 | if (ui->cur_visible) { | 2165 | if (ui->cur_visible) { |
2096 | ui->cur_visible = false; | 2166 | ui->cur_visible = false; |
2097 | nullret = UI_UPDATE; | 2167 | nullret = MOVE_UI_UPDATE; |
2098 | } | 2168 | } |
2099 | 2169 | ||
2100 | /* | 2170 | /* |
@@ -2102,12 +2172,22 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
2102 | */ | 2172 | */ |
2103 | x -= WINDOW_OFFSET + LINE_THICK; | 2173 | x -= WINDOW_OFFSET + LINE_THICK; |
2104 | y -= WINDOW_OFFSET + LINE_THICK; | 2174 | y -= WINDOW_OFFSET + LINE_THICK; |
2105 | if (x < 0 || y < 0) | ||
2106 | return nullret; | ||
2107 | tx = x / TILE_SIZE; | 2175 | tx = x / TILE_SIZE; |
2108 | ty = y / TILE_SIZE; | 2176 | ty = y / TILE_SIZE; |
2109 | if (tx >= state->width || ty >= state->height) | 2177 | if (x < 0 || y < 0 || tx >= state->width || ty >= state->height) { |
2178 | #ifdef USE_DRAGGING | ||
2179 | if (IS_MOUSE_DOWN(button)) { | ||
2180 | ui->dragstartx = ui->dragstarty = ui->dragtilex = ui->dragtiley = -1; | ||
2181 | return nullret; | ||
2182 | } | ||
2183 | /* | ||
2184 | * else: Despite the mouse moving off the grid, let drags and releases | ||
2185 | * continue to manipulate the tile they started from. | ||
2186 | */ | ||
2187 | #else | ||
2110 | return nullret; | 2188 | return nullret; |
2189 | #endif | ||
2190 | } | ||
2111 | /* Transform from physical to game coords */ | 2191 | /* Transform from physical to game coords */ |
2112 | tx = (tx + ui->org_x) % state->width; | 2192 | tx = (tx + ui->org_x) % state->width; |
2113 | ty = (ty + ui->org_y) % state->height; | 2193 | ty = (ty + ui->org_y) % state->height; |
@@ -2145,6 +2225,9 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
2145 | || button == RIGHT_DRAG | 2225 | || button == RIGHT_DRAG |
2146 | #endif | 2226 | #endif |
2147 | ) { | 2227 | ) { |
2228 | if (ui->dragtilex < 0) | ||
2229 | return nullret; | ||
2230 | |||
2148 | /* | 2231 | /* |
2149 | * Find the new drag point and see if it necessitates a | 2232 | * Find the new drag point and see if it necessitates a |
2150 | * rotation. | 2233 | * rotation. |
@@ -2198,7 +2281,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
2198 | || button == RIGHT_RELEASE | 2281 | || button == RIGHT_RELEASE |
2199 | #endif | 2282 | #endif |
2200 | ) { | 2283 | ) { |
2201 | if (!ui->dragged) { | 2284 | if (!ui->dragged && ui->dragtilex >= 0) { |
2202 | /* | 2285 | /* |
2203 | * There was a click but no perceptible drag: | 2286 | * There was a click but no perceptible drag: |
2204 | * revert to single-click behaviour. | 2287 | * revert to single-click behaviour. |
@@ -2334,7 +2417,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
2334 | OFFSET(ui->cur_x, ui->cur_y, ui->cur_x, ui->cur_y, dir, state); | 2417 | OFFSET(ui->cur_x, ui->cur_y, ui->cur_x, ui->cur_y, dir, state); |
2335 | ui->cur_visible = true; | 2418 | ui->cur_visible = true; |
2336 | } | 2419 | } |
2337 | return UI_UPDATE; | 2420 | return MOVE_UI_UPDATE; |
2338 | } else { | 2421 | } else { |
2339 | return NULL; | 2422 | return NULL; |
2340 | } | 2423 | } |
@@ -2444,7 +2527,6 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) | |||
2444 | game_drawstate *ds = snew(game_drawstate); | 2527 | game_drawstate *ds = snew(game_drawstate); |
2445 | int i, ncells; | 2528 | int i, ncells; |
2446 | 2529 | ||
2447 | ds->started = false; | ||
2448 | ds->width = state->width; | 2530 | ds->width = state->width; |
2449 | ds->height = state->height; | 2531 | ds->height = state->height; |
2450 | ncells = (state->width+2) * (state->height+2); | 2532 | ncells = (state->width+2) * (state->height+2); |
@@ -2464,11 +2546,12 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) | |||
2464 | static void game_free_drawstate(drawing *dr, game_drawstate *ds) | 2546 | static void game_free_drawstate(drawing *dr, game_drawstate *ds) |
2465 | { | 2547 | { |
2466 | sfree(ds->visible); | 2548 | sfree(ds->visible); |
2549 | sfree(ds->to_draw); | ||
2467 | sfree(ds); | 2550 | sfree(ds); |
2468 | } | 2551 | } |
2469 | 2552 | ||
2470 | static void game_compute_size(const game_params *params, int tilesize, | 2553 | static void game_compute_size(const game_params *params, int tilesize, |
2471 | int *x, int *y) | 2554 | const game_ui *ui, int *x, int *y) |
2472 | { | 2555 | { |
2473 | /* Ick: fake up `ds->tilesize' for macro expansion purposes */ | 2556 | /* Ick: fake up `ds->tilesize' for macro expansion purposes */ |
2474 | struct { int tilesize; } ads, *ds = &ads; | 2557 | struct { int tilesize; } ads, *ds = &ads; |
@@ -2605,8 +2688,8 @@ static void draw_wires(drawing *dr, int cx, int cy, int radius, | |||
2605 | 2688 | ||
2606 | for (i = 0; i < npoints; i++) { | 2689 | for (i = 0; i < npoints; i++) { |
2607 | rotated_coords(&xf, &yf, matrix, cx, cy, fpoints[2*i], fpoints[2*i+1]); | 2690 | rotated_coords(&xf, &yf, matrix, cx, cy, fpoints[2*i], fpoints[2*i+1]); |
2608 | points[2*i] = 0.5 + xf; | 2691 | points[2*i] = 0.5F + xf; |
2609 | points[2*i+1] = 0.5 + yf; | 2692 | points[2*i+1] = 0.5F + yf; |
2610 | } | 2693 | } |
2611 | 2694 | ||
2612 | draw_polygon(dr, points, npoints, colour, colour); | 2695 | draw_polygon(dr, points, npoints, colour, colour); |
@@ -2746,8 +2829,8 @@ static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, | |||
2746 | * rotated by an arbitrary angle about that centre point. | 2829 | * rotated by an arbitrary angle about that centre point. |
2747 | */ | 2830 | */ |
2748 | if (tile & TILE_ROTATING) { | 2831 | if (tile & TILE_ROTATING) { |
2749 | matrix[0] = (float)cos(angle * PI / 180.0); | 2832 | matrix[0] = (float)cos(angle * (float)PI / 180.0F); |
2750 | matrix[2] = (float)sin(angle * PI / 180.0); | 2833 | matrix[2] = (float)sin(angle * (float)PI / 180.0F); |
2751 | } else { | 2834 | } else { |
2752 | matrix[0] = 1.0F; | 2835 | matrix[0] = 1.0F; |
2753 | matrix[2] = 0.0F; | 2836 | matrix[2] = 0.0F; |
@@ -2786,8 +2869,8 @@ static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, | |||
2786 | float x, y; | 2869 | float x, y; |
2787 | rotated_coords(&x, &y, matrix, cx, cy, | 2870 | rotated_coords(&x, &y, matrix, cx, cy, |
2788 | boxr * points[i], boxr * points[i+1]); | 2871 | boxr * points[i], boxr * points[i+1]); |
2789 | points[i] = x + 0.5; | 2872 | points[i] = x + 0.5F; |
2790 | points[i+1] = y + 0.5; | 2873 | points[i+1] = y + 0.5F; |
2791 | } | 2874 | } |
2792 | 2875 | ||
2793 | draw_polygon(dr, points, 4, col, COL_WIRE); | 2876 | draw_polygon(dr, points, 4, col, COL_WIRE); |
@@ -2841,23 +2924,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
2841 | int *loops; | 2924 | int *loops; |
2842 | float angle = 0.0; | 2925 | float angle = 0.0; |
2843 | 2926 | ||
2844 | /* | ||
2845 | * Clear the screen on our first call. | ||
2846 | */ | ||
2847 | if (!ds->started) { | ||
2848 | int w, h; | ||
2849 | game_params params; | ||
2850 | |||
2851 | ds->started = true; | ||
2852 | |||
2853 | params.width = ds->width; | ||
2854 | params.height = ds->height; | ||
2855 | game_compute_size(¶ms, TILE_SIZE, &w, &h); | ||
2856 | |||
2857 | draw_rect(dr, 0, 0, w, h, COL_BACKGROUND); | ||
2858 | draw_update(dr, 0, 0, w, h); | ||
2859 | } | ||
2860 | |||
2861 | tx = ty = -1; | 2927 | tx = ty = -1; |
2862 | last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir : | 2928 | last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir : |
2863 | state->last_rotate_dir; | 2929 | state->last_rotate_dir; |
@@ -2888,7 +2954,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
2888 | * of barriers. | 2954 | * of barriers. |
2889 | */ | 2955 | */ |
2890 | active = compute_active(state, ui->cx, ui->cy); | 2956 | active = compute_active(state, ui->cx, ui->cy); |
2891 | loops = compute_loops(state); | 2957 | loops = compute_loops(state, ui->unlocked_loops); |
2892 | 2958 | ||
2893 | for (dy = -1; dy < ds->height+1; dy++) { | 2959 | for (dy = -1; dy < ds->height+1; dy++) { |
2894 | for (dx = -1; dx < ds->width+1; dx++) { | 2960 | for (dx = -1; dx < ds->width+1; dx++) { |
@@ -3109,19 +3175,15 @@ static int game_status(const game_state *state) | |||
3109 | return state->completed ? +1 : 0; | 3175 | return state->completed ? +1 : 0; |
3110 | } | 3176 | } |
3111 | 3177 | ||
3112 | static bool game_timing_state(const game_state *state, game_ui *ui) | 3178 | static void game_print_size(const game_params *params, const game_ui *ui, |
3113 | { | 3179 | float *x, float *y) |
3114 | return true; | ||
3115 | } | ||
3116 | |||
3117 | static void game_print_size(const game_params *params, float *x, float *y) | ||
3118 | { | 3180 | { |
3119 | int pw, ph; | 3181 | int pw, ph; |
3120 | 3182 | ||
3121 | /* | 3183 | /* |
3122 | * I'll use 8mm squares by default. | 3184 | * I'll use 8mm squares by default. |
3123 | */ | 3185 | */ |
3124 | game_compute_size(params, 800, &pw, &ph); | 3186 | game_compute_size(params, 800, ui, &pw, &ph); |
3125 | *x = pw / 100.0F; | 3187 | *x = pw / 100.0F; |
3126 | *y = ph / 100.0F; | 3188 | *y = ph / 100.0F; |
3127 | } | 3189 | } |
@@ -3172,7 +3234,8 @@ static void draw_diagram(drawing *dr, game_drawstate *ds, int x, int y, | |||
3172 | } | 3234 | } |
3173 | } | 3235 | } |
3174 | 3236 | ||
3175 | static void game_print(drawing *dr, const game_state *state, int tilesize) | 3237 | static void game_print(drawing *dr, const game_state *state, const game_ui *ui, |
3238 | int tilesize) | ||
3176 | { | 3239 | { |
3177 | int w = state->width, h = state->height; | 3240 | int w = state->width, h = state->height; |
3178 | int ink = print_mono_colour(dr, 0); | 3241 | int ink = print_mono_colour(dr, 0); |
@@ -3269,13 +3332,15 @@ const struct game thegame = { | |||
3269 | dup_game, | 3332 | dup_game, |
3270 | free_game, | 3333 | free_game, |
3271 | true, solve_game, | 3334 | true, solve_game, |
3272 | false, game_can_format_as_text_now, game_text_format, | 3335 | false, NULL, NULL, /* can_format_as_text_now, text_format */ |
3336 | get_prefs, set_prefs, | ||
3273 | new_ui, | 3337 | new_ui, |
3274 | free_ui, | 3338 | free_ui, |
3275 | encode_ui, | 3339 | encode_ui, |
3276 | decode_ui, | 3340 | decode_ui, |
3277 | NULL, /* game_request_keys */ | 3341 | NULL, /* game_request_keys */ |
3278 | game_changed_state, | 3342 | game_changed_state, |
3343 | current_key_label, | ||
3279 | interpret_move, | 3344 | interpret_move, |
3280 | execute_move, | 3345 | execute_move, |
3281 | PREFERRED_TILE_SIZE, game_compute_size, game_set_size, | 3346 | PREFERRED_TILE_SIZE, game_compute_size, game_set_size, |
@@ -3289,6 +3354,6 @@ const struct game thegame = { | |||
3289 | game_status, | 3354 | game_status, |
3290 | true, false, game_print_size, game_print, | 3355 | true, false, game_print_size, game_print, |
3291 | true, /* wants_statusbar */ | 3356 | true, /* wants_statusbar */ |
3292 | false, game_timing_state, | 3357 | false, NULL, /* timing_state */ |
3293 | 0, /* flags */ | 3358 | 0, /* flags */ |
3294 | }; | 3359 | }; |