summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/singles.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/singles.c')
-rw-r--r--apps/plugins/puzzles/src/singles.c111
1 files changed, 73 insertions, 38 deletions
diff --git a/apps/plugins/puzzles/src/singles.c b/apps/plugins/puzzles/src/singles.c
index 202ce08b20..07db8361ae 100644
--- a/apps/plugins/puzzles/src/singles.c
+++ b/apps/plugins/puzzles/src/singles.c
@@ -58,13 +58,17 @@
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#include "latin.h" 68#include "latin.h"
65 69
66#ifdef STANDALONE_SOLVER 70#ifdef STANDALONE_SOLVER
67bool verbose = false; 71static bool verbose = false;
68#endif 72#endif
69 73
70#define PREFERRED_TILE_SIZE 32 74#define PREFERRED_TILE_SIZE 32
@@ -82,7 +86,7 @@ bool verbose = false;
82#define FLASH_TIME 0.7F 86#define FLASH_TIME 0.7F
83 87
84enum { 88enum {
85 COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT, 89 COL_BACKGROUND, COL_UNUSED1, COL_LOWLIGHT,
86 COL_BLACK, COL_WHITE, COL_BLACKNUM, COL_GRID, 90 COL_BLACK, COL_WHITE, COL_BLACKNUM, COL_GRID,
87 COL_CURSOR, COL_ERROR, 91 COL_CURSOR, COL_ERROR,
88 NCOLOURS 92 NCOLOURS
@@ -254,7 +258,7 @@ static game_params *custom_params(const config_item *cfg)
254static const char *validate_params(const game_params *params, bool full) 258static const char *validate_params(const game_params *params, bool full)
255{ 259{
256 if (params->w < 2 || params->h < 2) 260 if (params->w < 2 || params->h < 2)
257 return "Width and neight must be at least two"; 261 return "Width and height must be at least two";
258 if (params->w > 10+26+26 || params->h > 10+26+26) 262 if (params->w > 10+26+26 || params->h > 10+26+26)
259 return "Puzzle is too large"; 263 return "Puzzle is too large";
260 if (full) { 264 if (full) {
@@ -417,7 +421,7 @@ static void debug_state(const char *desc, game_state *state) {
417 sfree(dbg); 421 sfree(dbg);
418} 422}
419 423
420static void connect_if_same(game_state *state, int *dsf, int i1, int i2) 424static void connect_if_same(game_state *state, DSF *dsf, int i1, int i2)
421{ 425{
422 int c1, c2; 426 int c1, c2;
423 427
@@ -429,13 +433,13 @@ static void connect_if_same(game_state *state, int *dsf, int i1, int i2)
429 dsf_merge(dsf, c1, c2); 433 dsf_merge(dsf, c1, c2);
430} 434}
431 435
432static void connect_dsf(game_state *state, int *dsf) 436static void connect_dsf(game_state *state, DSF *dsf)
433{ 437{
434 int x, y, i; 438 int x, y, i;
435 439
436 /* Construct a dsf array for connected blocks; connections 440 /* Construct a dsf array for connected blocks; connections
437 * tracked to right and down. */ 441 * tracked to right and down. */
438 dsf_init(dsf, state->n); 442 dsf_reinit(dsf);
439 for (x = 0; x < state->w; x++) { 443 for (x = 0; x < state->w; x++) {
440 for (y = 0; y < state->h; y++) { 444 for (y = 0; y < state->h; y++) {
441 i = y*state->w + x; 445 i = y*state->w + x;
@@ -494,7 +498,7 @@ static int check_rowcol(game_state *state, int starti, int di, int sz, unsigned
494 498
495static bool check_complete(game_state *state, unsigned flags) 499static bool check_complete(game_state *state, unsigned flags)
496{ 500{
497 int *dsf = snewn(state->n, int); 501 DSF *dsf = dsf_new(state->n);
498 int x, y, i, error = 0, nwhite, w = state->w, h = state->h; 502 int x, y, i, error = 0, nwhite, w = state->w, h = state->h;
499 503
500 if (flags & CC_MARK_ERRORS) { 504 if (flags & CC_MARK_ERRORS) {
@@ -543,7 +547,7 @@ static bool check_complete(game_state *state, unsigned flags)
543 int size = dsf_size(dsf, i); 547 int size = dsf_size(dsf, i);
544 if (largest < size) { 548 if (largest < size) {
545 largest = size; 549 largest = size;
546 canonical = i; 550 canonical = dsf_canonify(dsf, i);
547 } 551 }
548 } 552 }
549 553
@@ -558,7 +562,7 @@ static bool check_complete(game_state *state, unsigned flags)
558 } 562 }
559 } 563 }
560 564
561 sfree(dsf); 565 dsf_free(dsf);
562 return !(error > 0); 566 return !(error > 0);
563} 567}
564 568
@@ -1304,9 +1308,10 @@ found:
1304 return j; 1308 return j;
1305} 1309}
1306 1310
1307static char *new_game_desc(const game_params *params, random_state *rs, 1311static char *new_game_desc(const game_params *params_orig, random_state *rs,
1308 char **aux, bool interactive) 1312 char **aux, bool interactive)
1309{ 1313{
1314 game_params *params = dup_params(params_orig);
1310 game_state *state = blank_game(params->w, params->h); 1315 game_state *state = blank_game(params->w, params->h);
1311 game_state *tosolve = blank_game(params->w, params->h); 1316 game_state *tosolve = blank_game(params->w, params->h);
1312 int i, j, *scratch, *rownums, *colnums, x, y, ntries; 1317 int i, j, *scratch, *rownums, *colnums, x, y, ntries;
@@ -1315,6 +1320,12 @@ static char *new_game_desc(const game_params *params, random_state *rs,
1315 digit *latin; 1320 digit *latin;
1316 struct solver_state *ss = solver_state_new(state); 1321 struct solver_state *ss = solver_state_new(state);
1317 1322
1323 /* Downgrade difficulty to Easy for puzzles so tiny that they aren't
1324 * possible to generate at Tricky. These are 2x2, 2x3 and 3x3, i.e.
1325 * any puzzle that doesn't have one dimension at least 4. */
1326 if ((w < 4 || h < 4) && params->diff > DIFF_EASY)
1327 params->diff = DIFF_EASY;
1328
1318 scratch = snewn(state->n, int); 1329 scratch = snewn(state->n, int);
1319 rownums = snewn(h*o, int); 1330 rownums = snewn(h*o, int);
1320 colnums = snewn(w*o, int); 1331 colnums = snewn(w*o, int);
@@ -1408,6 +1419,7 @@ randomise:
1408 1419
1409 free_game(tosolve); 1420 free_game(tosolve);
1410 free_game(state); 1421 free_game(state);
1422 free_params(params);
1411 solver_state_free(ss); 1423 solver_state_free(ss);
1412 sfree(scratch); 1424 sfree(scratch);
1413 sfree(rownums); 1425 sfree(rownums);
@@ -1446,24 +1458,37 @@ static game_ui *new_ui(const game_state *state)
1446 game_ui *ui = snew(game_ui); 1458 game_ui *ui = snew(game_ui);
1447 1459
1448 ui->cx = ui->cy = 0; 1460 ui->cx = ui->cy = 0;
1449 ui->cshow = false; 1461 ui->cshow = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1450 ui->show_black_nums = false; 1462 ui->show_black_nums = false;
1451 1463
1452 return ui; 1464 return ui;
1453} 1465}
1454 1466
1455static void free_ui(game_ui *ui) 1467static config_item *get_prefs(game_ui *ui)
1456{ 1468{
1457 sfree(ui); 1469 config_item *ret;
1470
1471 ret = snewn(2, config_item);
1472
1473 ret[0].name = "Show numbers on black squares";
1474 ret[0].kw = "show-black-nums";
1475 ret[0].type = C_BOOLEAN;
1476 ret[0].u.boolean.bval = ui->show_black_nums;
1477
1478 ret[1].name = NULL;
1479 ret[1].type = C_END;
1480
1481 return ret;
1458} 1482}
1459 1483
1460static char *encode_ui(const game_ui *ui) 1484static void set_prefs(game_ui *ui, const config_item *cfg)
1461{ 1485{
1462 return NULL; 1486 ui->show_black_nums = cfg[0].u.boolean.bval;
1463} 1487}
1464 1488
1465static void decode_ui(game_ui *ui, const char *encoding) 1489static void free_ui(game_ui *ui)
1466{ 1490{
1491 sfree(ui);
1467} 1492}
1468 1493
1469static void game_changed_state(game_ui *ui, const game_state *oldstate, 1494static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -1473,6 +1498,18 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1473 ui->cshow = false; 1498 ui->cshow = false;
1474} 1499}
1475 1500
1501static const char *current_key_label(const game_ui *ui,
1502 const game_state *state, int button)
1503{
1504 if (IS_CURSOR_SELECT(button) && ui->cshow) {
1505 unsigned int f = state->flags[ui->cy * state->w + ui->cx];
1506 if (f & F_BLACK) return "Restore";
1507 if (f & F_CIRCLE) return "Remove";
1508 return button == CURSOR_SELECT ? "Black" : "Circle";
1509 }
1510 return "";
1511}
1512
1476#define DS_BLACK 0x1 1513#define DS_BLACK 0x1
1477#define DS_CIRCLE 0x2 1514#define DS_CIRCLE 0x2
1478#define DS_CURSOR 0x4 1515#define DS_CURSOR 0x4
@@ -1497,11 +1534,10 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1497 int i, x = FROMCOORD(mx), y = FROMCOORD(my); 1534 int i, x = FROMCOORD(mx), y = FROMCOORD(my);
1498 enum { NONE, TOGGLE_BLACK, TOGGLE_CIRCLE, UI } action = NONE; 1535 enum { NONE, TOGGLE_BLACK, TOGGLE_CIRCLE, UI } action = NONE;
1499 1536
1500 if (IS_CURSOR_MOVE(button)) { 1537 if (IS_CURSOR_MOVE(button))
1501 move_cursor(button, &ui->cx, &ui->cy, state->w, state->h, true); 1538 return move_cursor(button, &ui->cx, &ui->cy, state->w, state->h, true,
1502 ui->cshow = true; 1539 &ui->cshow);
1503 action = UI; 1540 else if (IS_CURSOR_SELECT(button)) {
1504 } else if (IS_CURSOR_SELECT(button)) {
1505 x = ui->cx; y = ui->cy; 1541 x = ui->cx; y = ui->cy;
1506 if (!ui->cshow) { 1542 if (!ui->cshow) {
1507 action = UI; 1543 action = UI;
@@ -1519,14 +1555,14 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1519 } 1555 }
1520 if (!INGRID(state, x, y)) { 1556 if (!INGRID(state, x, y)) {
1521 ui->show_black_nums = !ui->show_black_nums; 1557 ui->show_black_nums = !ui->show_black_nums;
1522 action = UI; /* this wants to be a per-game option. */ 1558 action = UI;
1523 } else if (button == LEFT_BUTTON) { 1559 } else if (button == LEFT_BUTTON) {
1524 action = TOGGLE_BLACK; 1560 action = TOGGLE_BLACK;
1525 } else if (button == RIGHT_BUTTON) { 1561 } else if (button == RIGHT_BUTTON) {
1526 action = TOGGLE_CIRCLE; 1562 action = TOGGLE_CIRCLE;
1527 } 1563 }
1528 } 1564 }
1529 if (action == UI) return UI_UPDATE; 1565 if (action == UI) return MOVE_UI_UPDATE;
1530 1566
1531 if (action == TOGGLE_BLACK || action == TOGGLE_CIRCLE) { 1567 if (action == TOGGLE_BLACK || action == TOGGLE_CIRCLE) {
1532 i = y * state->w + x; 1568 i = y * state->w + x;
@@ -1587,7 +1623,7 @@ badmove:
1587 */ 1623 */
1588 1624
1589static void game_compute_size(const game_params *params, int tilesize, 1625static void game_compute_size(const game_params *params, int tilesize,
1590 int *x, int *y) 1626 const game_ui *ui, int *x, int *y)
1591{ 1627{
1592 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1628 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1593 struct { int tilesize; } ads, *ds = &ads; 1629 struct { int tilesize; } ads, *ds = &ads;
@@ -1608,12 +1644,13 @@ static float *game_colours(frontend *fe, int *ncolours)
1608 float *ret = snewn(3 * NCOLOURS, float); 1644 float *ret = snewn(3 * NCOLOURS, float);
1609 int i; 1645 int i;
1610 1646
1611 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); 1647 game_mkhighlight(fe, ret, COL_BACKGROUND, -1, COL_LOWLIGHT);
1612 for (i = 0; i < 3; i++) { 1648 for (i = 0; i < 3; i++) {
1613 ret[COL_BLACK * 3 + i] = 0.0F; 1649 ret[COL_BLACK * 3 + i] = 0.0F;
1614 ret[COL_BLACKNUM * 3 + i] = 0.4F; 1650 ret[COL_BLACKNUM * 3 + i] = 0.4F;
1615 ret[COL_WHITE * 3 + i] = 1.0F; 1651 ret[COL_WHITE * 3 + i] = 1.0F;
1616 ret[COL_GRID * 3 + i] = ret[COL_LOWLIGHT * 3 + i]; 1652 ret[COL_GRID * 3 + i] = ret[COL_LOWLIGHT * 3 + i];
1653 ret[COL_UNUSED1 * 3 + i] = 0.0F; /* To placate an assertion. */
1617 } 1654 }
1618 ret[COL_CURSOR * 3 + 0] = 0.2F; 1655 ret[COL_CURSOR * 3 + 0] = 0.2F;
1619 ret[COL_CURSOR * 3 + 1] = 0.8F; 1656 ret[COL_CURSOR * 3 + 1] = 0.8F;
@@ -1708,7 +1745,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1708 if (!ds->started) { 1745 if (!ds->started) {
1709 int wsz = TILE_SIZE * state->w + 2 * BORDER; 1746 int wsz = TILE_SIZE * state->w + 2 * BORDER;
1710 int hsz = TILE_SIZE * state->h + 2 * BORDER; 1747 int hsz = TILE_SIZE * state->h + 2 * BORDER;
1711 draw_rect(dr, 0, 0, wsz, hsz, COL_BACKGROUND);
1712 draw_rect_outline(dr, COORD(0)-1, COORD(0)-1, 1748 draw_rect_outline(dr, COORD(0)-1, COORD(0)-1,
1713 TILE_SIZE * state->w + 2, TILE_SIZE * state->h + 2, 1749 TILE_SIZE * state->w + 2, TILE_SIZE * state->h + 2,
1714 COL_GRID); 1750 COL_GRID);
@@ -1776,22 +1812,19 @@ static int game_status(const game_state *state)
1776 return state->completed ? +1 : 0; 1812 return state->completed ? +1 : 0;
1777} 1813}
1778 1814
1779static bool game_timing_state(const game_state *state, game_ui *ui) 1815static void game_print_size(const game_params *params, const game_ui *ui,
1780{ 1816 float *x, float *y)
1781 return true;
1782}
1783
1784static void game_print_size(const game_params *params, float *x, float *y)
1785{ 1817{
1786 int pw, ph; 1818 int pw, ph;
1787 1819
1788 /* 8mm squares by default. */ 1820 /* 8mm squares by default. */
1789 game_compute_size(params, 800, &pw, &ph); 1821 game_compute_size(params, 800, ui, &pw, &ph);
1790 *x = pw / 100.0F; 1822 *x = pw / 100.0F;
1791 *y = ph / 100.0F; 1823 *y = ph / 100.0F;
1792} 1824}
1793 1825
1794static void game_print(drawing *dr, const game_state *state, int tilesize) 1826static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
1827 int tilesize)
1795{ 1828{
1796 int ink = print_mono_colour(dr, 0); 1829 int ink = print_mono_colour(dr, 0);
1797 int paper = print_mono_colour(dr, 1); 1830 int paper = print_mono_colour(dr, 1);
@@ -1848,12 +1881,14 @@ const struct game thegame = {
1848 free_game, 1881 free_game,
1849 true, solve_game, 1882 true, solve_game,
1850 true, game_can_format_as_text_now, game_text_format, 1883 true, game_can_format_as_text_now, game_text_format,
1884 get_prefs, set_prefs,
1851 new_ui, 1885 new_ui,
1852 free_ui, 1886 free_ui,
1853 encode_ui, 1887 NULL, /* encode_ui */
1854 decode_ui, 1888 NULL, /* decode_ui */
1855 NULL, /* game_request_keys */ 1889 NULL, /* game_request_keys */
1856 game_changed_state, 1890 game_changed_state,
1891 current_key_label,
1857 interpret_move, 1892 interpret_move,
1858 execute_move, 1893 execute_move,
1859 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 1894 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -1867,7 +1902,7 @@ const struct game thegame = {
1867 game_status, 1902 game_status,
1868 true, false, game_print_size, game_print, 1903 true, false, game_print_size, game_print,
1869 false, /* wants_statusbar */ 1904 false, /* wants_statusbar */
1870 false, game_timing_state, 1905 false, NULL, /* timing_state */
1871 REQUIRE_RBUTTON, /* flags */ 1906 REQUIRE_RBUTTON, /* flags */
1872}; 1907};
1873 1908