diff options
Diffstat (limited to 'apps/plugins/puzzles/src/range.c')
-rw-r--r-- | apps/plugins/puzzles/src/range.c | 182 |
1 files changed, 111 insertions, 71 deletions
diff --git a/apps/plugins/puzzles/src/range.c b/apps/plugins/puzzles/src/range.c index fc0a5405f1..bc29f7604b 100644 --- a/apps/plugins/puzzles/src/range.c +++ b/apps/plugins/puzzles/src/range.c | |||
@@ -58,7 +58,11 @@ | |||
58 | #include <string.h> | 58 | #include <string.h> |
59 | #include <assert.h> | 59 | #include <assert.h> |
60 | #include <ctype.h> | 60 | #include <ctype.h> |
61 | #include <math.h> | 61 | #ifdef NO_TGMATH_H |
62 | # include <math.h> | ||
63 | #else | ||
64 | # include <tgmath.h> | ||
65 | #endif | ||
62 | 66 | ||
63 | #include "puzzles.h" | 67 | #include "puzzles.h" |
64 | 68 | ||
@@ -911,8 +915,8 @@ static const char *validate_params(const game_params *params, bool full) | |||
911 | int const w = params->w, h = params->h; | 915 | int const w = params->w, h = params->h; |
912 | if (w < 1) return "Error: width is less than 1"; | 916 | if (w < 1) return "Error: width is less than 1"; |
913 | if (h < 1) return "Error: height is less than 1"; | 917 | if (h < 1) return "Error: height is less than 1"; |
918 | if (w > SCHAR_MAX - (h - 1)) return "Error: w + h is too big"; | ||
914 | if (w * h < 1) return "Error: size is less than 1"; | 919 | if (w * h < 1) return "Error: size is less than 1"; |
915 | if (w + h - 1 > SCHAR_MAX) return "Error: w + h is too big"; | ||
916 | /* I might be unable to store clues in my puzzle_size *grid; */ | 920 | /* I might be unable to store clues in my puzzle_size *grid; */ |
917 | if (full) { | 921 | if (full) { |
918 | if (w == 2 && h == 2) return "Error: can't create 2x2 puzzles"; | 922 | if (w == 2 && h == 2) return "Error: can't create 2x2 puzzles"; |
@@ -1221,28 +1225,101 @@ static char *game_text_format(const game_state *state) | |||
1221 | struct game_ui { | 1225 | struct game_ui { |
1222 | puzzle_size r, c; /* cursor position */ | 1226 | puzzle_size r, c; /* cursor position */ |
1223 | bool cursor_show; | 1227 | bool cursor_show; |
1228 | |||
1229 | /* | ||
1230 | * User preference option to swap the left and right mouse | ||
1231 | * buttons. | ||
1232 | * | ||
1233 | * The original puzzle submitter thought it would be more useful | ||
1234 | * to have the left button turn an empty square into a dotted one, | ||
1235 | * on the grounds that that was what you did most often; I (SGT) | ||
1236 | * felt instinctively that the left button ought to place black | ||
1237 | * squares and the right button place dots, on the grounds that | ||
1238 | * that was consistent with many other puzzles in which the left | ||
1239 | * button fills in the data used by the solution checker while the | ||
1240 | * right button places pencil marks for the user's convenience. | ||
1241 | * | ||
1242 | * My first beta-player wasn't sure either, so I thought I'd | ||
1243 | * pre-emptively put in a 'configuration' mechanism just in case. | ||
1244 | */ | ||
1245 | bool swap_buttons; | ||
1224 | }; | 1246 | }; |
1225 | 1247 | ||
1248 | static void legacy_prefs_override(struct game_ui *ui_out) | ||
1249 | { | ||
1250 | static int initialised = false; | ||
1251 | static int swap_buttons = -1; | ||
1252 | |||
1253 | if (!initialised) { | ||
1254 | initialised = true; | ||
1255 | swap_buttons = getenv_bool("RANGE_SWAP_BUTTONS", -1); | ||
1256 | } | ||
1257 | |||
1258 | if (swap_buttons != -1) | ||
1259 | ui_out->swap_buttons = swap_buttons; | ||
1260 | } | ||
1261 | |||
1226 | static game_ui *new_ui(const game_state *state) | 1262 | static game_ui *new_ui(const game_state *state) |
1227 | { | 1263 | { |
1228 | struct game_ui *ui = snew(game_ui); | 1264 | struct game_ui *ui = snew(game_ui); |
1229 | ui->r = ui->c = 0; | 1265 | ui->r = ui->c = 0; |
1230 | ui->cursor_show = false; | 1266 | ui->cursor_show = getenv_bool("PUZZLES_SHOW_CURSOR", false); |
1267 | |||
1268 | ui->swap_buttons = false; | ||
1269 | legacy_prefs_override(ui); | ||
1270 | |||
1231 | return ui; | 1271 | return ui; |
1232 | } | 1272 | } |
1233 | 1273 | ||
1234 | static void free_ui(game_ui *ui) | 1274 | static config_item *get_prefs(game_ui *ui) |
1235 | { | 1275 | { |
1236 | sfree(ui); | 1276 | config_item *ret; |
1277 | |||
1278 | ret = snewn(2, config_item); | ||
1279 | |||
1280 | ret[0].name = "Mouse button order"; | ||
1281 | ret[0].kw = "left-mouse-button"; | ||
1282 | ret[0].type = C_CHOICES; | ||
1283 | ret[0].u.choices.choicenames = | ||
1284 | ":Left to fill, right to dot:Left to dot, right to fill"; | ||
1285 | ret[0].u.choices.choicekws = ":fill:dot"; | ||
1286 | ret[0].u.choices.selected = ui->swap_buttons; | ||
1287 | |||
1288 | ret[1].name = NULL; | ||
1289 | ret[1].type = C_END; | ||
1290 | |||
1291 | return ret; | ||
1237 | } | 1292 | } |
1238 | 1293 | ||
1239 | static char *encode_ui(const game_ui *ui) | 1294 | static void set_prefs(game_ui *ui, const config_item *cfg) |
1240 | { | 1295 | { |
1241 | return NULL; | 1296 | ui->swap_buttons = cfg[0].u.choices.selected; |
1297 | } | ||
1298 | |||
1299 | static void free_ui(game_ui *ui) | ||
1300 | { | ||
1301 | sfree(ui); | ||
1242 | } | 1302 | } |
1243 | 1303 | ||
1244 | static void decode_ui(game_ui *ui, const char *encoding) | 1304 | static const char *current_key_label(const game_ui *ui, |
1305 | const game_state *state, int button) | ||
1245 | { | 1306 | { |
1307 | int cell; | ||
1308 | |||
1309 | if (IS_CURSOR_SELECT(button)) { | ||
1310 | cell = state->grid[idx(ui->r, ui->c, state->params.w)]; | ||
1311 | if (!ui->cursor_show || cell > 0) return ""; | ||
1312 | switch (cell) { | ||
1313 | case EMPTY: | ||
1314 | return button == CURSOR_SELECT ? "Fill" : "Dot"; | ||
1315 | case WHITE: | ||
1316 | return button == CURSOR_SELECT ? "Empty" : "Fill"; | ||
1317 | case BLACK: | ||
1318 | return button == CURSOR_SELECT ? "Dot" : "Empty"; | ||
1319 | } | ||
1320 | } | ||
1321 | return ""; | ||
1322 | |||
1246 | } | 1323 | } |
1247 | 1324 | ||
1248 | typedef struct drawcell { | 1325 | typedef struct drawcell { |
@@ -1253,7 +1330,6 @@ typedef struct drawcell { | |||
1253 | struct game_drawstate { | 1330 | struct game_drawstate { |
1254 | int tilesize; | 1331 | int tilesize; |
1255 | drawcell *grid; | 1332 | drawcell *grid; |
1256 | bool started; | ||
1257 | }; | 1333 | }; |
1258 | 1334 | ||
1259 | #define TILESIZE (ds->tilesize) | 1335 | #define TILESIZE (ds->tilesize) |
@@ -1283,38 +1359,12 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1283 | } | 1359 | } |
1284 | 1360 | ||
1285 | if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { | 1361 | if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { |
1286 | /* | 1362 | if (ui->swap_buttons) { |
1287 | * Utterly awful hack, exactly analogous to the one in Slant, | 1363 | if (button == LEFT_BUTTON) |
1288 | * to configure the left and right mouse buttons the opposite | 1364 | button = RIGHT_BUTTON; |
1289 | * way round. | 1365 | else |
1290 | * | 1366 | button = LEFT_BUTTON; |
1291 | * The original puzzle submitter thought it would be more | 1367 | } |
1292 | * useful to have the left button turn an empty square into a | ||
1293 | * dotted one, on the grounds that that was what you did most | ||
1294 | * often; I (SGT) felt instinctively that the left button | ||
1295 | * ought to place black squares and the right button place | ||
1296 | * dots, on the grounds that that was consistent with many | ||
1297 | * other puzzles in which the left button fills in the data | ||
1298 | * used by the solution checker while the right button places | ||
1299 | * pencil marks for the user's convenience. | ||
1300 | * | ||
1301 | * My first beta-player wasn't sure either, so I thought I'd | ||
1302 | * pre-emptively put in a 'configuration' mechanism just in | ||
1303 | * case. | ||
1304 | */ | ||
1305 | { | ||
1306 | static int swap_buttons = -1; | ||
1307 | if (swap_buttons < 0) { | ||
1308 | char *env = getenv("RANGE_SWAP_BUTTONS"); | ||
1309 | swap_buttons = (env && (env[0] == 'y' || env[0] == 'Y')); | ||
1310 | } | ||
1311 | if (swap_buttons) { | ||
1312 | if (button == LEFT_BUTTON) | ||
1313 | button = RIGHT_BUTTON; | ||
1314 | else | ||
1315 | button = LEFT_BUTTON; | ||
1316 | } | ||
1317 | } | ||
1318 | } | 1368 | } |
1319 | 1369 | ||
1320 | switch (button) { | 1370 | switch (button) { |
@@ -1355,14 +1405,14 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1355 | else if (do_post) | 1405 | else if (do_post) |
1356 | return nfmtstr(40, "W,%d,%d", ui->r, ui->c); | 1406 | return nfmtstr(40, "W,%d,%d", ui->r, ui->c); |
1357 | else | 1407 | else |
1358 | return UI_UPDATE; | 1408 | return MOVE_UI_UPDATE; |
1359 | 1409 | ||
1360 | } else if (!out_of_bounds(ui->r + dr[i], ui->c + dc[i], w, h)) { | 1410 | } else if (!out_of_bounds(ui->r + dr[i], ui->c + dc[i], w, h)) { |
1361 | ui->r += dr[i]; | 1411 | ui->r += dr[i]; |
1362 | ui->c += dc[i]; | 1412 | ui->c += dc[i]; |
1363 | } | 1413 | } |
1364 | } else ui->cursor_show = true; | 1414 | } else ui->cursor_show = true; |
1365 | return UI_UPDATE; | 1415 | return MOVE_UI_UPDATE; |
1366 | } | 1416 | } |
1367 | 1417 | ||
1368 | if (action == hint) { | 1418 | if (action == hint) { |
@@ -1408,7 +1458,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1408 | static bool find_errors(const game_state *state, bool *report) | 1458 | static bool find_errors(const game_state *state, bool *report) |
1409 | { | 1459 | { |
1410 | int const w = state->params.w, h = state->params.h, n = w * h; | 1460 | int const w = state->params.w, h = state->params.h, n = w * h; |
1411 | int *dsf; | 1461 | DSF *dsf; |
1412 | 1462 | ||
1413 | int r, c, i; | 1463 | int r, c, i; |
1414 | 1464 | ||
@@ -1463,7 +1513,7 @@ static bool find_errors(const game_state *state, bool *report) | |||
1463 | /* | 1513 | /* |
1464 | * Check that all the white cells form a single connected component. | 1514 | * Check that all the white cells form a single connected component. |
1465 | */ | 1515 | */ |
1466 | dsf = snew_dsf(n); | 1516 | dsf = dsf_new(n); |
1467 | for (r = 0; r < h-1; ++r) | 1517 | for (r = 0; r < h-1; ++r) |
1468 | for (c = 0; c < w; ++c) | 1518 | for (c = 0; c < w; ++c) |
1469 | if (state->grid[r*w+c] != BLACK && | 1519 | if (state->grid[r*w+c] != BLACK && |
@@ -1474,11 +1524,12 @@ static bool find_errors(const game_state *state, bool *report) | |||
1474 | if (state->grid[r*w+c] != BLACK && | 1524 | if (state->grid[r*w+c] != BLACK && |
1475 | state->grid[r*w+(c+1)] != BLACK) | 1525 | state->grid[r*w+(c+1)] != BLACK) |
1476 | dsf_merge(dsf, r*w+c, r*w+(c+1)); | 1526 | dsf_merge(dsf, r*w+c, r*w+(c+1)); |
1477 | if (nblack + dsf_size(dsf, any_white_cell) < n) { | 1527 | if (any_white_cell != -1 && |
1528 | nblack + dsf_size(dsf, any_white_cell) < n) { | ||
1478 | int biggest, canonical; | 1529 | int biggest, canonical; |
1479 | 1530 | ||
1480 | if (!report) { | 1531 | if (!report) { |
1481 | sfree(dsf); | 1532 | dsf_free(dsf); |
1482 | goto found_error; | 1533 | goto found_error; |
1483 | } | 1534 | } |
1484 | 1535 | ||
@@ -1503,7 +1554,7 @@ static bool find_errors(const game_state *state, bool *report) | |||
1503 | if (state->grid[i] != BLACK && dsf_canonify(dsf, i) != canonical) | 1554 | if (state->grid[i] != BLACK && dsf_canonify(dsf, i) != canonical) |
1504 | report[i] = true; | 1555 | report[i] = true; |
1505 | } | 1556 | } |
1506 | sfree(dsf); | 1557 | dsf_free(dsf); |
1507 | 1558 | ||
1508 | free_game(dup); | 1559 | free_game(dup); |
1509 | return false; /* if report != NULL, this is ignored */ | 1560 | return false; /* if report != NULL, this is ignored */ |
@@ -1604,13 +1655,12 @@ enum { | |||
1604 | COL_USER = COL_GRID, | 1655 | COL_USER = COL_GRID, |
1605 | COL_ERROR, | 1656 | COL_ERROR, |
1606 | COL_LOWLIGHT, | 1657 | COL_LOWLIGHT, |
1607 | COL_HIGHLIGHT = COL_ERROR, /* mkhighlight needs it, I don't */ | ||
1608 | COL_CURSOR = COL_LOWLIGHT, | 1658 | COL_CURSOR = COL_LOWLIGHT, |
1609 | NCOLOURS | 1659 | NCOLOURS |
1610 | }; | 1660 | }; |
1611 | 1661 | ||
1612 | static void game_compute_size(const game_params *params, int tilesize, | 1662 | static void game_compute_size(const game_params *params, int tilesize, |
1613 | int *x, int *y) | 1663 | const game_ui *ui, int *x, int *y) |
1614 | { | 1664 | { |
1615 | *x = (1 + params->w) * tilesize; | 1665 | *x = (1 + params->w) * tilesize; |
1616 | *y = (1 + params->h) * tilesize; | 1666 | *y = (1 + params->h) * tilesize; |
@@ -1629,7 +1679,7 @@ static float *game_colours(frontend *fe, int *ncolours) | |||
1629 | { | 1679 | { |
1630 | float *ret = snewn(3 * NCOLOURS, float); | 1680 | float *ret = snewn(3 * NCOLOURS, float); |
1631 | 1681 | ||
1632 | game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); | 1682 | game_mkhighlight(fe, ret, COL_BACKGROUND, -1, COL_LOWLIGHT); |
1633 | COLOUR(ret, COL_GRID, 0.0F, 0.0F, 0.0F); | 1683 | COLOUR(ret, COL_GRID, 0.0F, 0.0F, 0.0F); |
1634 | COLOUR(ret, COL_ERROR, 1.0F, 0.0F, 0.0F); | 1684 | COLOUR(ret, COL_ERROR, 1.0F, 0.0F, 0.0F); |
1635 | 1685 | ||
@@ -1655,7 +1705,6 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) | |||
1655 | int i; | 1705 | int i; |
1656 | 1706 | ||
1657 | ds->tilesize = 0; | 1707 | ds->tilesize = 0; |
1658 | ds->started = false; | ||
1659 | 1708 | ||
1660 | ds->grid = snewn(n, drawcell); | 1709 | ds->grid = snewn(n, drawcell); |
1661 | for (i = 0; i < n; ++i) | 1710 | for (i = 0; i < n; ++i) |
@@ -1690,7 +1739,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
1690 | float animtime, float flashtime) | 1739 | float animtime, float flashtime) |
1691 | { | 1740 | { |
1692 | int const w = state->params.w, h = state->params.h, n = w * h; | 1741 | int const w = state->params.w, h = state->params.h, n = w * h; |
1693 | int const wpx = (w+1) * ds->tilesize, hpx = (h+1) * ds->tilesize; | ||
1694 | int const flash = ((int) (flashtime * 5 / FLASH_TIME)) % 2; | 1742 | int const flash = ((int) (flashtime * 5 / FLASH_TIME)) % 2; |
1695 | 1743 | ||
1696 | int r, c, i; | 1744 | int r, c, i; |
@@ -1701,12 +1749,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
1701 | 1749 | ||
1702 | assert (oldstate == NULL); /* only happens if animating moves */ | 1750 | assert (oldstate == NULL); /* only happens if animating moves */ |
1703 | 1751 | ||
1704 | if (!ds->started) { | ||
1705 | ds->started = true; | ||
1706 | draw_rect(dr, 0, 0, wpx, hpx, COL_BACKGROUND); | ||
1707 | draw_update(dr, 0, 0, wpx, hpx); | ||
1708 | } | ||
1709 | |||
1710 | for (i = r = 0; r < h; ++r) { | 1752 | for (i = r = 0; r < h; ++r) { |
1711 | for (c = 0; c < w; ++c, ++i) { | 1753 | for (c = 0; c < w; ++c, ++i) { |
1712 | drawcell cell = makecell(state->grid[i], errors[i], false, flash); | 1754 | drawcell cell = makecell(state->grid[i], errors[i], false, flash); |
@@ -1757,25 +1799,21 @@ static void draw_cell(drawing *draw, game_drawstate *ds, int r, int c, | |||
1757 | draw_update(draw, x, y, ts + 1, ts + 1); | 1799 | draw_update(draw, x, y, ts + 1, ts + 1); |
1758 | } | 1800 | } |
1759 | 1801 | ||
1760 | static bool game_timing_state(const game_state *state, game_ui *ui) | ||
1761 | { | ||
1762 | puts("warning: game_timing_state was called (this shouldn't happen)"); | ||
1763 | return false; /* the (non-existing) timer should not be running */ | ||
1764 | } | ||
1765 | |||
1766 | /* ---------------------------------------------------------------------- | 1802 | /* ---------------------------------------------------------------------- |
1767 | * User interface: print | 1803 | * User interface: print |
1768 | */ | 1804 | */ |
1769 | 1805 | ||
1770 | static void game_print_size(const game_params *params, float *x, float *y) | 1806 | static void game_print_size(const game_params *params, const game_ui *ui, |
1807 | float *x, float *y) | ||
1771 | { | 1808 | { |
1772 | int print_width, print_height; | 1809 | int print_width, print_height; |
1773 | game_compute_size(params, 800, &print_width, &print_height); | 1810 | game_compute_size(params, 800, ui, &print_width, &print_height); |
1774 | *x = print_width / 100.0F; | 1811 | *x = print_width / 100.0F; |
1775 | *y = print_height / 100.0F; | 1812 | *y = print_height / 100.0F; |
1776 | } | 1813 | } |
1777 | 1814 | ||
1778 | static void game_print(drawing *dr, const game_state *state, int tilesize) | 1815 | static void game_print(drawing *dr, const game_state *state, const game_ui *ui, |
1816 | int tilesize) | ||
1779 | { | 1817 | { |
1780 | int const w = state->params.w, h = state->params.h; | 1818 | int const w = state->params.w, h = state->params.h; |
1781 | game_drawstate ds_obj, *ds = &ds_obj; | 1819 | game_drawstate ds_obj, *ds = &ds_obj; |
@@ -1821,12 +1859,14 @@ struct game const thegame = { | |||
1821 | free_game, | 1859 | free_game, |
1822 | true, solve_game, | 1860 | true, solve_game, |
1823 | true, game_can_format_as_text_now, game_text_format, | 1861 | true, game_can_format_as_text_now, game_text_format, |
1862 | get_prefs, set_prefs, | ||
1824 | new_ui, | 1863 | new_ui, |
1825 | free_ui, | 1864 | free_ui, |
1826 | encode_ui, | 1865 | NULL, /* encode_ui */ |
1827 | decode_ui, | 1866 | NULL, /* decode_ui */ |
1828 | NULL, /* game_request_keys */ | 1867 | NULL, /* game_request_keys */ |
1829 | game_changed_state, | 1868 | game_changed_state, |
1869 | current_key_label, | ||
1830 | interpret_move, | 1870 | interpret_move, |
1831 | execute_move, | 1871 | execute_move, |
1832 | PREFERRED_TILE_SIZE, game_compute_size, game_set_size, | 1872 | PREFERRED_TILE_SIZE, game_compute_size, game_set_size, |
@@ -1840,6 +1880,6 @@ struct game const thegame = { | |||
1840 | game_status, | 1880 | game_status, |
1841 | true, false, game_print_size, game_print, | 1881 | true, false, game_print_size, game_print, |
1842 | false, /* wants_statusbar */ | 1882 | false, /* wants_statusbar */ |
1843 | false, game_timing_state, | 1883 | false, NULL, /* timing_state */ |
1844 | 0, /* flags */ | 1884 | 0, /* flags */ |
1845 | }; | 1885 | }; |