diff options
author | Franklin Wei <franklin@rockbox.org> | 2024-07-22 21:43:25 -0400 |
---|---|---|
committer | Franklin Wei <franklin@rockbox.org> | 2024-07-22 21:44:08 -0400 |
commit | 09aa8de52cb962f1ceebfb1fd44f2c54a924fc5c (patch) | |
tree | 182bd4efb2dc8ca4fcb369d8cccab0c0f290d054 /apps/plugins/puzzles/src/singles.c | |
parent | c72030f98c953a82ed6f5c7132ad000c3d5f4a16 (diff) | |
download | rockbox-09aa8de52cb962f1ceebfb1fd44f2c54a924fc5c.tar.gz rockbox-09aa8de52cb962f1ceebfb1fd44f2c54a924fc5c.zip |
puzzles: resync with upstream
This brings the puzzles source in sync with Simon's branch, commit fd304c5
(from March 2024), with some added Rockbox-specific compatibility changes:
https://www.franklinwei.com/git/puzzles/commit/?h=rockbox-devel&id=516830d9d76bdfe64fe5ccf2a9b59c33f5c7c078
There are quite a lot of backend changes, including a new "Mosaic" puzzle.
In addition, some new frontend changes were necessary:
- New "Preferences" menu to access the user preferences system.
- Enabled spacebar input for several games.
Change-Id: I94c7df674089c92f32d5f07025f6a1059068af1e
Diffstat (limited to 'apps/plugins/puzzles/src/singles.c')
-rw-r--r-- | apps/plugins/puzzles/src/singles.c | 111 |
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 |
67 | bool verbose = false; | 71 | static 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 | ||
84 | enum { | 88 | enum { |
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) | |||
254 | static const char *validate_params(const game_params *params, bool full) | 258 | static 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 | ||
420 | static void connect_if_same(game_state *state, int *dsf, int i1, int i2) | 424 | static 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 | ||
432 | static void connect_dsf(game_state *state, int *dsf) | 436 | static 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 | ||
495 | static bool check_complete(game_state *state, unsigned flags) | 499 | static 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 | ||
1307 | static char *new_game_desc(const game_params *params, random_state *rs, | 1311 | static 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 | ||
1455 | static void free_ui(game_ui *ui) | 1467 | static 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 | ||
1460 | static char *encode_ui(const game_ui *ui) | 1484 | static 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 | ||
1465 | static void decode_ui(game_ui *ui, const char *encoding) | 1489 | static void free_ui(game_ui *ui) |
1466 | { | 1490 | { |
1491 | sfree(ui); | ||
1467 | } | 1492 | } |
1468 | 1493 | ||
1469 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | 1494 | static 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 | ||
1501 | static 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 | ||
1589 | static void game_compute_size(const game_params *params, int tilesize, | 1625 | static 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 | ||
1779 | static bool game_timing_state(const game_state *state, game_ui *ui) | 1815 | static void game_print_size(const game_params *params, const game_ui *ui, |
1780 | { | 1816 | float *x, float *y) |
1781 | return true; | ||
1782 | } | ||
1783 | |||
1784 | static 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 | ||
1794 | static void game_print(drawing *dr, const game_state *state, int tilesize) | 1826 | static 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 | ||