summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/unequal.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/unequal.c')
-rw-r--r--apps/plugins/puzzles/src/unequal.c193
1 files changed, 129 insertions, 64 deletions
diff --git a/apps/plugins/puzzles/src/unequal.c b/apps/plugins/puzzles/src/unequal.c
index 153954e510..ab32e4ab79 100644
--- a/apps/plugins/puzzles/src/unequal.c
+++ b/apps/plugins/puzzles/src/unequal.c
@@ -21,7 +21,11 @@
21#include <string.h> 21#include <string.h>
22#include <assert.h> 22#include <assert.h>
23#include <ctype.h> 23#include <ctype.h>
24#include <math.h> 24#ifdef NO_TGMATH_H
25# include <math.h>
26#else
27# include <tgmath.h>
28#endif
25 29
26#include "puzzles.h" 30#include "puzzles.h"
27#include "latin.h" /* contains typedef for digit */ 31#include "latin.h" /* contains typedef for digit */
@@ -84,6 +88,7 @@ struct game_params {
84#define ADJ_TO_SPENT(x) ((x) << 9) 88#define ADJ_TO_SPENT(x) ((x) << 9)
85 89
86#define F_ERROR_MASK (F_ERROR|F_ERROR_UP|F_ERROR_RIGHT|F_ERROR_DOWN|F_ERROR_LEFT) 90#define F_ERROR_MASK (F_ERROR|F_ERROR_UP|F_ERROR_RIGHT|F_ERROR_DOWN|F_ERROR_LEFT)
91#define F_SPENT_MASK (F_SPENT_UP|F_SPENT_RIGHT|F_SPENT_DOWN|F_SPENT_LEFT)
87 92
88struct game_state { 93struct game_state {
89 int order; 94 int order;
@@ -890,13 +895,14 @@ static int solver_state(game_state *state, int maxdiff)
890 struct latin_solver solver; 895 struct latin_solver solver;
891 int diff; 896 int diff;
892 897
893 latin_solver_alloc(&solver, state->nums, state->order); 898 if (latin_solver_alloc(&solver, state->nums, state->order))
894 899 diff = latin_solver_main(&solver, maxdiff,
895 diff = latin_solver_main(&solver, maxdiff, 900 DIFF_LATIN, DIFF_SET, DIFF_EXTREME,
896 DIFF_LATIN, DIFF_SET, DIFF_EXTREME, 901 DIFF_EXTREME, DIFF_RECURSIVE,
897 DIFF_EXTREME, DIFF_RECURSIVE, 902 unequal_solvers, unequal_valid, ctx,
898 unequal_solvers, unequal_valid, ctx, 903 clone_ctx, free_ctx);
899 clone_ctx, free_ctx); 904 else
905 diff = DIFF_IMPOSSIBLE;
900 906
901 memcpy(state->hints, solver.cube, state->order*state->order*state->order); 907 memcpy(state->hints, solver.cube, state->order*state->order*state->order);
902 908
@@ -1073,7 +1079,7 @@ static int gg_best_clue(game_state *state, int *scratch, digit *latin)
1073} 1079}
1074 1080
1075#ifdef STANDALONE_SOLVER 1081#ifdef STANDALONE_SOLVER
1076int maxtries; 1082static int maxtries;
1077#define MAXTRIES maxtries 1083#define MAXTRIES maxtries
1078#else 1084#else
1079#define MAXTRIES 50 1085#define MAXTRIES 50
@@ -1430,6 +1436,17 @@ static char *solve_game(const game_state *state, const game_state *currstate,
1430struct game_ui { 1436struct game_ui {
1431 int hx, hy; /* as for solo.c, highlight pos */ 1437 int hx, hy; /* as for solo.c, highlight pos */
1432 bool hshow, hpencil, hcursor; /* show state, type, and ?cursor. */ 1438 bool hshow, hpencil, hcursor; /* show state, type, and ?cursor. */
1439
1440 /*
1441 * User preference option: if the user right-clicks in a square
1442 * and presses a number key to add/remove a pencil mark, do we
1443 * hide the mouse highlight again afterwards?
1444 *
1445 * Historically our answer was yes. The Android port prefers no.
1446 * There are advantages both ways, depending how much you dislike
1447 * the highlight cluttering your view. So it's a preference.
1448 */
1449 bool pencil_keep_highlight;
1433}; 1450};
1434 1451
1435static game_ui *new_ui(const game_state *state) 1452static game_ui *new_ui(const game_state *state)
@@ -1438,8 +1455,9 @@ static game_ui *new_ui(const game_state *state)
1438 1455
1439 ui->hx = ui->hy = 0; 1456 ui->hx = ui->hy = 0;
1440 ui->hpencil = false; 1457 ui->hpencil = false;
1441 ui->hshow = false; 1458 ui->hshow = ui->hcursor = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1442 ui->hcursor = false; 1459
1460 ui->pencil_keep_highlight = false;
1443 1461
1444 return ui; 1462 return ui;
1445} 1463}
@@ -1449,13 +1467,26 @@ static void free_ui(game_ui *ui)
1449 sfree(ui); 1467 sfree(ui);
1450} 1468}
1451 1469
1452static char *encode_ui(const game_ui *ui) 1470static config_item *get_prefs(game_ui *ui)
1453{ 1471{
1454 return NULL; 1472 config_item *ret;
1473
1474 ret = snewn(2, config_item);
1475
1476 ret[0].name = "Keep mouse highlight after changing a pencil mark";
1477 ret[0].kw = "pencil-keep-highlight";
1478 ret[0].type = C_BOOLEAN;
1479 ret[0].u.boolean.bval = ui->pencil_keep_highlight;
1480
1481 ret[1].name = NULL;
1482 ret[1].type = C_END;
1483
1484 return ret;
1455} 1485}
1456 1486
1457static void decode_ui(game_ui *ui, const char *encoding) 1487static void set_prefs(game_ui *ui, const config_item *cfg)
1458{ 1488{
1489 ui->pencil_keep_highlight = cfg[0].u.boolean.bval;
1459} 1490}
1460 1491
1461static void game_changed_state(game_ui *ui, const game_state *oldstate, 1492static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -1470,6 +1501,14 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1470 } 1501 }
1471} 1502}
1472 1503
1504static const char *current_key_label(const game_ui *ui,
1505 const game_state *state, int button)
1506{
1507 if (ui->hshow && IS_CURSOR_SELECT(button))
1508 return ui->hpencil ? "Ink" : "Pencil";
1509 return "";
1510}
1511
1473struct game_drawstate { 1512struct game_drawstate {
1474 int tilesize, order; 1513 int tilesize, order;
1475 bool started; 1514 bool started;
@@ -1491,7 +1530,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1491 char buf[80]; 1530 char buf[80];
1492 bool shift_or_control = button & (MOD_SHFT | MOD_CTRL); 1531 bool shift_or_control = button & (MOD_SHFT | MOD_CTRL);
1493 1532
1494 button &= ~MOD_MASK; 1533 button = STRIP_BUTTON_MODIFIERS(button);
1495 1534
1496 if (x >= 0 && x < ds->order && y >= 0 && y < ds->order && IS_MOUSE_DOWN(button)) { 1535 if (x >= 0 && x < ds->order && y >= 0 && y < ds->order && IS_MOUSE_DOWN(button)) {
1497 if (oy - COORD(y) > TILE_SIZE && ox - COORD(x) > TILE_SIZE) 1536 if (oy - COORD(y) > TILE_SIZE && ox - COORD(x) > TILE_SIZE)
@@ -1527,7 +1566,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1527 ui->hshow = true; 1566 ui->hshow = true;
1528 } 1567 }
1529 ui->hcursor = false; 1568 ui->hcursor = false;
1530 return UI_UPDATE; 1569 return MOVE_UI_UPDATE;
1531 } 1570 }
1532 if (button == RIGHT_BUTTON) { 1571 if (button == RIGHT_BUTTON) {
1533 /* pencil highlighting for non-filled squares */ 1572 /* pencil highlighting for non-filled squares */
@@ -1541,7 +1580,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1541 ui->hshow = true; 1580 ui->hshow = true;
1542 } 1581 }
1543 ui->hcursor = false; 1582 ui->hcursor = false;
1544 return UI_UPDATE; 1583 return MOVE_UI_UPDATE;
1545 } 1584 }
1546 } 1585 }
1547 1586
@@ -1549,7 +1588,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1549 if (shift_or_control) { 1588 if (shift_or_control) {
1550 int nx = ui->hx, ny = ui->hy, i; 1589 int nx = ui->hx, ny = ui->hy, i;
1551 bool self; 1590 bool self;
1552 move_cursor(button, &nx, &ny, ds->order, ds->order, false); 1591 move_cursor(button, &nx, &ny, ds->order, ds->order, false, NULL);
1553 ui->hshow = true; 1592 ui->hshow = true;
1554 ui->hcursor = true; 1593 ui->hcursor = true;
1555 1594
@@ -1557,12 +1596,12 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1557 ny != ui->hy + adjthan[i].dy); ++i); 1596 ny != ui->hy + adjthan[i].dy); ++i);
1558 1597
1559 if (i == 4) 1598 if (i == 4)
1560 return UI_UPDATE; /* invalid direction, i.e. out of 1599 return MOVE_UI_UPDATE; /* invalid direction, i.e. out of
1561 * the board */ 1600 * the board */
1562 1601
1563 if (!(GRID(state, flags, ui->hx, ui->hy) & adjthan[i].f || 1602 if (!(GRID(state, flags, ui->hx, ui->hy) & adjthan[i].f ||
1564 GRID(state, flags, nx, ny ) & adjthan[i].fo)) 1603 GRID(state, flags, nx, ny ) & adjthan[i].fo))
1565 return UI_UPDATE; /* no clue to toggle */ 1604 return MOVE_UI_UPDATE; /* no clue to toggle */
1566 1605
1567 if (state->mode == MODE_ADJACENT) 1606 if (state->mode == MODE_ADJACENT)
1568 self = (adjthan[i].dx >= 0 && adjthan[i].dy >= 0); 1607 self = (adjthan[i].dx >= 0 && adjthan[i].dy >= 0);
@@ -1570,24 +1609,23 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1570 self = (GRID(state, flags, ui->hx, ui->hy) & adjthan[i].f); 1609 self = (GRID(state, flags, ui->hx, ui->hy) & adjthan[i].f);
1571 1610
1572 if (self) 1611 if (self)
1573 sprintf(buf, "F%d,%d,%d", ui->hx, ui->hy, 1612 sprintf(buf, "F%d,%d,%u", ui->hx, ui->hy,
1574 ADJ_TO_SPENT(adjthan[i].f)); 1613 ADJ_TO_SPENT(adjthan[i].f));
1575 else 1614 else
1576 sprintf(buf, "F%d,%d,%d", nx, ny, 1615 sprintf(buf, "F%d,%d,%u", nx, ny,
1577 ADJ_TO_SPENT(adjthan[i].fo)); 1616 ADJ_TO_SPENT(adjthan[i].fo));
1578 1617
1579 return dupstr(buf); 1618 return dupstr(buf);
1580 } else { 1619 } else {
1581 move_cursor(button, &ui->hx, &ui->hy, ds->order, ds->order, false);
1582 ui->hshow = true;
1583 ui->hcursor = true; 1620 ui->hcursor = true;
1584 return UI_UPDATE; 1621 return move_cursor(button, &ui->hx, &ui->hy, ds->order, ds->order,
1622 false, &ui->hshow);
1585 } 1623 }
1586 } 1624 }
1587 if (ui->hshow && IS_CURSOR_SELECT(button)) { 1625 if (ui->hshow && IS_CURSOR_SELECT(button)) {
1588 ui->hpencil = !ui->hpencil; 1626 ui->hpencil = !ui->hpencil;
1589 ui->hcursor = true; 1627 ui->hcursor = true;
1590 return UI_UPDATE; 1628 return MOVE_UI_UPDATE;
1591 } 1629 }
1592 1630
1593 n = c2n(button, state->order); 1631 n = c2n(button, state->order);
@@ -1604,11 +1642,37 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1604 if (ui->hpencil && GRID(state, nums, ui->hx, ui->hy) > 0) 1642 if (ui->hpencil && GRID(state, nums, ui->hx, ui->hy) > 0)
1605 return NULL; /* can't change hints on filled square (!) */ 1643 return NULL; /* can't change hints on filled square (!) */
1606 1644
1645 /*
1646 * If you ask to fill a square with what it already contains,
1647 * or blank it when it's already empty, that has no effect...
1648 */
1649 if ((!ui->hpencil || n == 0) &&
1650 GRID(state, nums, ui->hx, ui->hy) == n) {
1651 bool anypencil = false;
1652 int i;
1653 for (i = 0; i < state->order; i++)
1654 anypencil = anypencil || HINT(state, ui->hx, ui->hy, i);
1655 if (!anypencil) {
1656 /* ... expect to remove the cursor in mouse mode. */
1657 if (!ui->hcursor) {
1658 ui->hshow = false;
1659 return MOVE_UI_UPDATE;
1660 }
1661 return NULL;
1662 }
1663 }
1607 1664
1608 sprintf(buf, "%c%d,%d,%d", 1665 sprintf(buf, "%c%d,%d,%d",
1609 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n); 1666 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n);
1610 1667
1611 if (!ui->hcursor) ui->hshow = false; 1668 /*
1669 * Hide the highlight after a keypress, if it was mouse-
1670 * generated. Also, don't hide it if this move has changed
1671 * pencil marks and the user preference says not to hide the
1672 * highlight in that situation.
1673 */
1674 if (!ui->hcursor && !(ui->hpencil && ui->pencil_keep_highlight))
1675 ui->hshow = false;
1612 1676
1613 return dupstr(buf); 1677 return dupstr(buf);
1614 } 1678 }
@@ -1624,7 +1688,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1624static game_state *execute_move(const game_state *state, const char *move) 1688static game_state *execute_move(const game_state *state, const char *move)
1625{ 1689{
1626 game_state *ret = NULL; 1690 game_state *ret = NULL;
1627 int x, y, n, i, rc; 1691 int x, y, n, i;
1628 1692
1629 debug(("execute_move: %s", move)); 1693 debug(("execute_move: %s", move));
1630 1694
@@ -1649,7 +1713,7 @@ static game_state *execute_move(const game_state *state, const char *move)
1649 const char *p; 1713 const char *p;
1650 1714
1651 ret = dup_game(state); 1715 ret = dup_game(state);
1652 ret->completed = ret->cheated = true; 1716 ret->cheated = true;
1653 1717
1654 p = move+1; 1718 p = move+1;
1655 for (i = 0; i < state->order*state->order; i++) { 1719 for (i = 0; i < state->order*state->order; i++) {
@@ -1660,8 +1724,8 @@ static game_state *execute_move(const game_state *state, const char *move)
1660 p++; 1724 p++;
1661 } 1725 }
1662 if (*p) goto badmove; 1726 if (*p) goto badmove;
1663 rc = check_complete(ret->nums, ret, true); 1727 if (!ret->completed && check_complete(ret->nums, ret, true) > 0)
1664 assert(rc > 0); 1728 ret->completed = true;
1665 return ret; 1729 return ret;
1666 } else if (move[0] == 'M') { 1730 } else if (move[0] == 'M') {
1667 ret = dup_game(state); 1731 ret = dup_game(state);
@@ -1678,7 +1742,8 @@ static game_state *execute_move(const game_state *state, const char *move)
1678 check_complete(ret->nums, ret, true); 1742 check_complete(ret->nums, ret, true);
1679 return ret; 1743 return ret;
1680 } else if (move[0] == 'F' && sscanf(move+1, "%d,%d,%d", &x, &y, &n) == 3 && 1744 } else if (move[0] == 'F' && sscanf(move+1, "%d,%d,%d", &x, &y, &n) == 3 &&
1681 x >= 0 && x < state->order && y >= 0 && y < state->order) { 1745 x >= 0 && x < state->order && y >= 0 && y < state->order &&
1746 (n & ~F_SPENT_MASK) == 0) {
1682 ret = dup_game(state); 1747 ret = dup_game(state);
1683 GRID(ret, flags, x, y) ^= n; 1748 GRID(ret, flags, x, y) ^= n;
1684 return ret; 1749 return ret;
@@ -1696,7 +1761,7 @@ badmove:
1696#define DRAW_SIZE (TILE_SIZE*ds->order + GAP_SIZE*(ds->order-1) + BORDER*2) 1761#define DRAW_SIZE (TILE_SIZE*ds->order + GAP_SIZE*(ds->order-1) + BORDER*2)
1697 1762
1698static void game_compute_size(const game_params *params, int tilesize, 1763static void game_compute_size(const game_params *params, int tilesize,
1699 int *x, int *y) 1764 const game_ui *ui, int *x, int *y)
1700{ 1765{
1701 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1766 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1702 struct { int tilesize, order; } ads, *ds = &ads; 1767 struct { int tilesize, order; } ads, *ds = &ads;
@@ -1779,18 +1844,18 @@ static void draw_gt(drawing *dr, int ox, int oy,
1779{ 1844{
1780 int coords[12]; 1845 int coords[12];
1781 int xdx = (dx1+dx2 ? 0 : 1), xdy = (dx1+dx2 ? 1 : 0); 1846 int xdx = (dx1+dx2 ? 0 : 1), xdy = (dx1+dx2 ? 1 : 0);
1782 coords[0] = ox + xdx + dx1; 1847 coords[0] = ox + xdx;
1783 coords[1] = oy + xdy + dy1; 1848 coords[1] = oy + xdy;
1784 coords[2] = ox + xdx + dx1 + dx2; 1849 coords[2] = ox + xdx + dx1;
1785 coords[3] = oy + xdy + dy1 + dy2; 1850 coords[3] = oy + xdy + dy1;
1786 coords[4] = ox - xdx + dx1 + dx2; 1851 coords[4] = ox + xdx + dx1 + dx2;
1787 coords[5] = oy - xdy + dy1 + dy2; 1852 coords[5] = oy + xdy + dy1 + dy2;
1788 coords[6] = ox - xdx + dx1; 1853 coords[6] = ox - xdx + dx1 + dx2;
1789 coords[7] = oy - xdy + dy1; 1854 coords[7] = oy - xdy + dy1 + dy2;
1790 coords[8] = ox - xdx; 1855 coords[8] = ox - xdx + dx1;
1791 coords[9] = oy - xdy; 1856 coords[9] = oy - xdy + dy1;
1792 coords[10] = ox + xdx; 1857 coords[10] = ox - xdx;
1793 coords[11] = oy + xdy; 1858 coords[11] = oy - xdy;
1794 draw_polygon(dr, coords, 6, col, col); 1859 draw_polygon(dr, coords, 6, col, col);
1795} 1860}
1796 1861
@@ -2059,22 +2124,19 @@ static int game_status(const game_state *state)
2059 return state->completed ? +1 : 0; 2124 return state->completed ? +1 : 0;
2060} 2125}
2061 2126
2062static bool game_timing_state(const game_state *state, game_ui *ui) 2127static void game_print_size(const game_params *params, const game_ui *ui,
2063{ 2128 float *x, float *y)
2064 return true;
2065}
2066
2067static void game_print_size(const game_params *params, float *x, float *y)
2068{ 2129{
2069 int pw, ph; 2130 int pw, ph;
2070 2131
2071 /* 10mm squares by default, roughly the same as Grauniad. */ 2132 /* 10mm squares by default, roughly the same as Grauniad. */
2072 game_compute_size(params, 1000, &pw, &ph); 2133 game_compute_size(params, 1000, ui, &pw, &ph);
2073 *x = pw / 100.0F; 2134 *x = pw / 100.0F;
2074 *y = ph / 100.0F; 2135 *y = ph / 100.0F;
2075} 2136}
2076 2137
2077static void game_print(drawing *dr, const game_state *state, int tilesize) 2138static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2139 int tilesize)
2078{ 2140{
2079 int ink = print_mono_colour(dr, 0); 2141 int ink = print_mono_colour(dr, 0);
2080 int x, y, o = state->order, ox, oy, n; 2142 int x, y, o = state->order, ox, oy, n;
@@ -2133,12 +2195,14 @@ const struct game thegame = {
2133 free_game, 2195 free_game,
2134 true, solve_game, 2196 true, solve_game,
2135 true, game_can_format_as_text_now, game_text_format, 2197 true, game_can_format_as_text_now, game_text_format,
2198 get_prefs, set_prefs,
2136 new_ui, 2199 new_ui,
2137 free_ui, 2200 free_ui,
2138 encode_ui, 2201 NULL, /* encode_ui */
2139 decode_ui, 2202 NULL, /* decode_ui */
2140 game_request_keys, 2203 game_request_keys,
2141 game_changed_state, 2204 game_changed_state,
2205 current_key_label,
2142 interpret_move, 2206 interpret_move,
2143 execute_move, 2207 execute_move,
2144 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 2208 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -2152,7 +2216,7 @@ const struct game thegame = {
2152 game_status, 2216 game_status,
2153 true, false, game_print_size, game_print, 2217 true, false, game_print_size, game_print,
2154 false, /* wants_statusbar */ 2218 false, /* wants_statusbar */
2155 false, game_timing_state, 2219 false, NULL, /* timing_state */
2156 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */ 2220 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */
2157}; 2221};
2158 2222
@@ -2165,7 +2229,7 @@ const struct game thegame = {
2165#include <time.h> 2229#include <time.h>
2166#include <stdarg.h> 2230#include <stdarg.h>
2167 2231
2168const char *quis = NULL; 2232static const char *quis = NULL;
2169 2233
2170#if 0 /* currently unused */ 2234#if 0 /* currently unused */
2171 2235
@@ -2233,13 +2297,14 @@ static int solve(game_params *p, char *desc, int debug)
2233 solver_show_working = debug; 2297 solver_show_working = debug;
2234 game_debug(state); 2298 game_debug(state);
2235 2299
2236 latin_solver_alloc(&solver, state->nums, state->order); 2300 if (latin_solver_alloc(&solver, state->nums, state->order))
2237 2301 diff = latin_solver_main(&solver, DIFF_RECURSIVE,
2238 diff = latin_solver_main(&solver, DIFF_RECURSIVE, 2302 DIFF_LATIN, DIFF_SET, DIFF_EXTREME,
2239 DIFF_LATIN, DIFF_SET, DIFF_EXTREME, 2303 DIFF_EXTREME, DIFF_RECURSIVE,
2240 DIFF_EXTREME, DIFF_RECURSIVE, 2304 unequal_solvers, unequal_valid, ctx,
2241 unequal_solvers, unequal_valid, ctx, 2305 clone_ctx, free_ctx);
2242 clone_ctx, free_ctx); 2306 else
2307 diff = DIFF_IMPOSSIBLE;
2243 2308
2244 free_ctx(ctx); 2309 free_ctx(ctx);
2245 2310