summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/keen.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/keen.c')
-rw-r--r--apps/plugins/puzzles/src/keen.c250
1 files changed, 148 insertions, 102 deletions
diff --git a/apps/plugins/puzzles/src/keen.c b/apps/plugins/puzzles/src/keen.c
index 6b9610dbcd..7e2f71c8f9 100644
--- a/apps/plugins/puzzles/src/keen.c
+++ b/apps/plugins/puzzles/src/keen.c
@@ -8,7 +8,11 @@
8#include <string.h> 8#include <string.h>
9#include <assert.h> 9#include <assert.h>
10#include <ctype.h> 10#include <ctype.h>
11#include <math.h> 11#ifdef NO_TGMATH_H
12# include <math.h>
13#else
14# include <tgmath.h>
15#endif
12 16
13#include "puzzles.h" 17#include "puzzles.h"
14#include "latin.h" 18#include "latin.h"
@@ -69,7 +73,7 @@ struct game_params {
69struct clues { 73struct clues {
70 int refcount; 74 int refcount;
71 int w; 75 int w;
72 int *dsf; 76 DSF *dsf;
73 long *clues; 77 long *clues;
74}; 78};
75 79
@@ -677,7 +681,7 @@ static bool keen_valid(struct latin_solver *solver, void *vctx)
677 return true; 681 return true;
678} 682}
679 683
680static int solver(int w, int *dsf, long *clues, digit *soln, int maxdiff) 684static int solver(int w, DSF *dsf, long *clues, digit *soln, int maxdiff)
681{ 685{
682 int a = w*w; 686 int a = w*w;
683 struct solver_ctx ctx; 687 struct solver_ctx ctx;
@@ -704,11 +708,11 @@ static int solver(int w, int *dsf, long *clues, digit *soln, int maxdiff)
704 ctx.clues = snewn(ctx.nboxes, long); 708 ctx.clues = snewn(ctx.nboxes, long);
705 ctx.whichbox = snewn(a, int); 709 ctx.whichbox = snewn(a, int);
706 for (n = m = i = 0; i < a; i++) 710 for (n = m = i = 0; i < a; i++)
707 if (dsf_canonify(dsf, i) == i) { 711 if (dsf_minimal(dsf, i) == i) {
708 ctx.clues[n] = clues[i]; 712 ctx.clues[n] = clues[i];
709 ctx.boxes[n] = m; 713 ctx.boxes[n] = m;
710 for (j = 0; j < a; j++) 714 for (j = 0; j < a; j++)
711 if (dsf_canonify(dsf, j) == i) { 715 if (dsf_minimal(dsf, j) == i) {
712 ctx.boxlist[m++] = (j % w) * w + (j / w); /* transpose */ 716 ctx.boxlist[m++] = (j % w) * w + (j / w); /* transpose */
713 ctx.whichbox[ctx.boxlist[m-1]] = n; 717 ctx.whichbox[ctx.boxlist[m-1]] = n;
714 } 718 }
@@ -740,7 +744,7 @@ static int solver(int w, int *dsf, long *clues, digit *soln, int maxdiff)
740 * Grid generation. 744 * Grid generation.
741 */ 745 */
742 746
743static char *encode_block_structure(char *p, int w, int *dsf) 747static char *encode_block_structure(char *p, int w, DSF *dsf)
744{ 748{
745 int i, currrun = 0; 749 int i, currrun = 0;
746 char *orig, *q, *r, c; 750 char *orig, *q, *r, c;
@@ -777,7 +781,7 @@ static char *encode_block_structure(char *p, int w, int *dsf)
777 p0 = y*w+x; 781 p0 = y*w+x;
778 p1 = (y+1)*w+x; 782 p1 = (y+1)*w+x;
779 } 783 }
780 edge = (dsf_canonify(dsf, p0) != dsf_canonify(dsf, p1)); 784 edge = !dsf_equivalent(dsf, p0, p1);
781 } 785 }
782 786
783 if (edge) { 787 if (edge) {
@@ -815,13 +819,12 @@ static char *encode_block_structure(char *p, int w, int *dsf)
815 return q; 819 return q;
816} 820}
817 821
818static const char *parse_block_structure(const char **p, int w, int *dsf) 822static const char *parse_block_structure(const char **p, int w, DSF *dsf)
819{ 823{
820 int a = w*w;
821 int pos = 0; 824 int pos = 0;
822 int repc = 0, repn = 0; 825 int repc = 0, repn = 0;
823 826
824 dsf_init(dsf, a); 827 dsf_reinit(dsf);
825 828
826 while (**p && (repn > 0 || **p != ',')) { 829 while (**p && (repn > 0 || **p != ',')) {
827 int c; 830 int c;
@@ -890,7 +893,8 @@ static char *new_game_desc(const game_params *params, random_state *rs,
890{ 893{
891 int w = params->w, a = w*w; 894 int w = params->w, a = w*w;
892 digit *grid, *soln; 895 digit *grid, *soln;
893 int *order, *revorder, *singletons, *dsf; 896 int *order, *revorder, *singletons;
897 DSF *dsf;
894 long *clues, *cluevals; 898 long *clues, *cluevals;
895 int i, j, k, n, x, y, ret; 899 int i, j, k, n, x, y, ret;
896 int diff = params->diff; 900 int diff = params->diff;
@@ -927,7 +931,7 @@ done
927 order = snewn(a, int); 931 order = snewn(a, int);
928 revorder = snewn(a, int); 932 revorder = snewn(a, int);
929 singletons = snewn(a, int); 933 singletons = snewn(a, int);
930 dsf = snew_dsf(a); 934 dsf = dsf_new_min(a);
931 clues = snewn(a, long); 935 clues = snewn(a, long);
932 cluevals = snewn(a, long); 936 cluevals = snewn(a, long);
933 soln = snewn(a, digit); 937 soln = snewn(a, digit);
@@ -955,7 +959,7 @@ done
955 for (i = 0; i < a; i++) 959 for (i = 0; i < a; i++)
956 singletons[i] = true; 960 singletons[i] = true;
957 961
958 dsf_init(dsf, a); 962 dsf_reinit(dsf);
959 963
960 /* Place dominoes. */ 964 /* Place dominoes. */
961 for (i = 0; i < a; i++) { 965 for (i = 0; i < a; i++) {
@@ -1037,11 +1041,10 @@ done
1037 * integer quotient, of course), but we rule out (or try to 1041 * integer quotient, of course), but we rule out (or try to
1038 * avoid) some clues because they're of low quality. 1042 * avoid) some clues because they're of low quality.
1039 * 1043 *
1040 * Hence, we iterate once over the grid, stopping at the 1044 * Hence, we iterate once over the grid, stopping at the first
1041 * canonical element of every >2 block and the _non_- 1045 * element in every >2 block and the _last_ element of every
1042 * canonical element of every 2-block; the latter means that 1046 * 2-block; the latter means that we can make our decision
1043 * we can make our decision about a 2-block in the knowledge 1047 * about a 2-block in the knowledge of both numbers in it.
1044 * of both numbers in it.
1045 * 1048 *
1046 * We reuse the 'singletons' array (finished with in the 1049 * We reuse the 'singletons' array (finished with in the
1047 * above loop) to hold information about which blocks are 1050 * above loop) to hold information about which blocks are
@@ -1055,7 +1058,7 @@ done
1055 1058
1056 for (i = 0; i < a; i++) { 1059 for (i = 0; i < a; i++) {
1057 singletons[i] = 0; 1060 singletons[i] = 0;
1058 j = dsf_canonify(dsf, i); 1061 j = dsf_minimal(dsf, i);
1059 k = dsf_size(dsf, j); 1062 k = dsf_size(dsf, j);
1060 if (params->multiplication_only) 1063 if (params->multiplication_only)
1061 singletons[j] = F_MUL; 1064 singletons[j] = F_MUL;
@@ -1178,7 +1181,7 @@ done
1178 * Having chosen the clue types, calculate the clue values. 1181 * Having chosen the clue types, calculate the clue values.
1179 */ 1182 */
1180 for (i = 0; i < a; i++) { 1183 for (i = 0; i < a; i++) {
1181 j = dsf_canonify(dsf, i); 1184 j = dsf_minimal(dsf, i);
1182 if (j == i) { 1185 if (j == i) {
1183 cluevals[j] = grid[i]; 1186 cluevals[j] = grid[i];
1184 } else { 1187 } else {
@@ -1206,7 +1209,7 @@ done
1206 } 1209 }
1207 1210
1208 for (i = 0; i < a; i++) { 1211 for (i = 0; i < a; i++) {
1209 j = dsf_canonify(dsf, i); 1212 j = dsf_minimal(dsf, i);
1210 if (j == i) { 1213 if (j == i) {
1211 clues[j] |= cluevals[j]; 1214 clues[j] |= cluevals[j];
1212 } 1215 }
@@ -1252,7 +1255,7 @@ done
1252 p = encode_block_structure(p, w, dsf); 1255 p = encode_block_structure(p, w, dsf);
1253 *p++ = ','; 1256 *p++ = ',';
1254 for (i = 0; i < a; i++) { 1257 for (i = 0; i < a; i++) {
1255 j = dsf_canonify(dsf, i); 1258 j = dsf_minimal(dsf, i);
1256 if (j == i) { 1259 if (j == i) {
1257 switch (clues[j] & CMASK) { 1260 switch (clues[j] & CMASK) {
1258 case C_ADD: *p++ = 'a'; break; 1261 case C_ADD: *p++ = 'a'; break;
@@ -1280,7 +1283,7 @@ done
1280 sfree(order); 1283 sfree(order);
1281 sfree(revorder); 1284 sfree(revorder);
1282 sfree(singletons); 1285 sfree(singletons);
1283 sfree(dsf); 1286 dsf_free(dsf);
1284 sfree(clues); 1287 sfree(clues);
1285 sfree(cluevals); 1288 sfree(cluevals);
1286 sfree(soln); 1289 sfree(soln);
@@ -1295,7 +1298,7 @@ done
1295static const char *validate_desc(const game_params *params, const char *desc) 1298static const char *validate_desc(const game_params *params, const char *desc)
1296{ 1299{
1297 int w = params->w, a = w*w; 1300 int w = params->w, a = w*w;
1298 int *dsf; 1301 DSF *dsf;
1299 const char *ret; 1302 const char *ret;
1300 const char *p = desc; 1303 const char *p = desc;
1301 int i; 1304 int i;
@@ -1303,15 +1306,17 @@ static const char *validate_desc(const game_params *params, const char *desc)
1303 /* 1306 /*
1304 * Verify that the block structure makes sense. 1307 * Verify that the block structure makes sense.
1305 */ 1308 */
1306 dsf = snew_dsf(a); 1309 dsf = dsf_new_min(a);
1307 ret = parse_block_structure(&p, w, dsf); 1310 ret = parse_block_structure(&p, w, dsf);
1308 if (ret) { 1311 if (ret) {
1309 sfree(dsf); 1312 dsf_free(dsf);
1310 return ret; 1313 return ret;
1311 } 1314 }
1312 1315
1313 if (*p != ',') 1316 if (*p != ',') {
1317 dsf_free(dsf);
1314 return "Expected ',' after block structure description"; 1318 return "Expected ',' after block structure description";
1319 }
1315 p++; 1320 p++;
1316 1321
1317 /* 1322 /*
@@ -1319,21 +1324,26 @@ static const char *validate_desc(const game_params *params, const char *desc)
1319 * and DIV clues don't apply to blocks of the wrong size. 1324 * and DIV clues don't apply to blocks of the wrong size.
1320 */ 1325 */
1321 for (i = 0; i < a; i++) { 1326 for (i = 0; i < a; i++) {
1322 if (dsf_canonify(dsf, i) == i) { 1327 if (dsf_minimal(dsf, i) == i) {
1323 if (*p == 'a' || *p == 'm') { 1328 if (*p == 'a' || *p == 'm') {
1324 /* these clues need no validation */ 1329 /* these clues need no validation */
1325 } else if (*p == 'd' || *p == 's') { 1330 } else if (*p == 'd' || *p == 's') {
1326 if (dsf_size(dsf, i) != 2) 1331 if (dsf_size(dsf, i) != 2) {
1332 dsf_free(dsf);
1327 return "Subtraction and division blocks must have area 2"; 1333 return "Subtraction and division blocks must have area 2";
1334 }
1328 } else if (!*p) { 1335 } else if (!*p) {
1336 dsf_free(dsf);
1329 return "Too few clues for block structure"; 1337 return "Too few clues for block structure";
1330 } else { 1338 } else {
1339 dsf_free(dsf);
1331 return "Unrecognised clue type"; 1340 return "Unrecognised clue type";
1332 } 1341 }
1333 p++; 1342 p++;
1334 while (*p && isdigit((unsigned char)*p)) p++; 1343 while (*p && isdigit((unsigned char)*p)) p++;
1335 } 1344 }
1336 } 1345 }
1346 dsf_free(dsf);
1337 if (*p) 1347 if (*p)
1338 return "Too many clues for block structure"; 1348 return "Too many clues for block structure";
1339 1349
@@ -1373,7 +1383,7 @@ static game_state *new_game(midend *me, const game_params *params,
1373 state->clues = snew(struct clues); 1383 state->clues = snew(struct clues);
1374 state->clues->refcount = 1; 1384 state->clues->refcount = 1;
1375 state->clues->w = w; 1385 state->clues->w = w;
1376 state->clues->dsf = snew_dsf(a); 1386 state->clues->dsf = dsf_new_min(a);
1377 parse_block_structure(&p, w, state->clues->dsf); 1387 parse_block_structure(&p, w, state->clues->dsf);
1378 1388
1379 assert(*p == ','); 1389 assert(*p == ',');
@@ -1381,7 +1391,7 @@ static game_state *new_game(midend *me, const game_params *params,
1381 1391
1382 state->clues->clues = snewn(a, long); 1392 state->clues->clues = snewn(a, long);
1383 for (i = 0; i < a; i++) { 1393 for (i = 0; i < a; i++) {
1384 if (dsf_canonify(state->clues->dsf, i) == i) { 1394 if (dsf_minimal(state->clues->dsf, i) == i) {
1385 long clue = 0; 1395 long clue = 0;
1386 switch (*p) { 1396 switch (*p) {
1387 case 'a': 1397 case 'a':
@@ -1448,7 +1458,7 @@ static void free_game(game_state *state)
1448 sfree(state->grid); 1458 sfree(state->grid);
1449 sfree(state->pencil); 1459 sfree(state->pencil);
1450 if (--state->clues->refcount <= 0) { 1460 if (--state->clues->refcount <= 0) {
1451 sfree(state->clues->dsf); 1461 dsf_free(state->clues->dsf);
1452 sfree(state->clues->clues); 1462 sfree(state->clues->clues);
1453 sfree(state->clues); 1463 sfree(state->clues);
1454 } 1464 }
@@ -1490,16 +1500,6 @@ static char *solve_game(const game_state *state, const game_state *currstate,
1490 return out; 1500 return out;
1491} 1501}
1492 1502
1493static bool game_can_format_as_text_now(const game_params *params)
1494{
1495 return true;
1496}
1497
1498static char *game_text_format(const game_state *state)
1499{
1500 return NULL;
1501}
1502
1503struct game_ui { 1503struct game_ui {
1504 /* 1504 /*
1505 * These are the coordinates of the currently highlighted 1505 * These are the coordinates of the currently highlighted
@@ -1525,6 +1525,17 @@ struct game_ui {
1525 * allowed on immutable squares. 1525 * allowed on immutable squares.
1526 */ 1526 */
1527 bool hcursor; 1527 bool hcursor;
1528
1529 /*
1530 * User preference option: if the user right-clicks in a square
1531 * and presses a number key to add/remove a pencil mark, do we
1532 * hide the mouse highlight again afterwards?
1533 *
1534 * Historically our answer was yes. The Android port prefers no.
1535 * There are advantages both ways, depending how much you dislike
1536 * the highlight cluttering your view. So it's a preference.
1537 */
1538 bool pencil_keep_highlight;
1528}; 1539};
1529 1540
1530static game_ui *new_ui(const game_state *state) 1541static game_ui *new_ui(const game_state *state)
@@ -1533,8 +1544,9 @@ static game_ui *new_ui(const game_state *state)
1533 1544
1534 ui->hx = ui->hy = 0; 1545 ui->hx = ui->hy = 0;
1535 ui->hpencil = false; 1546 ui->hpencil = false;
1536 ui->hshow = false; 1547 ui->hshow = ui->hcursor = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1537 ui->hcursor = false; 1548
1549 ui->pencil_keep_highlight = false;
1538 1550
1539 return ui; 1551 return ui;
1540} 1552}
@@ -1544,13 +1556,26 @@ static void free_ui(game_ui *ui)
1544 sfree(ui); 1556 sfree(ui);
1545} 1557}
1546 1558
1547static char *encode_ui(const game_ui *ui) 1559static config_item *get_prefs(game_ui *ui)
1548{ 1560{
1549 return NULL; 1561 config_item *ret;
1562
1563 ret = snewn(2, config_item);
1564
1565 ret[0].name = "Keep mouse highlight after changing a pencil mark";
1566 ret[0].kw = "pencil-keep-highlight";
1567 ret[0].type = C_BOOLEAN;
1568 ret[0].u.boolean.bval = ui->pencil_keep_highlight;
1569
1570 ret[1].name = NULL;
1571 ret[1].type = C_END;
1572
1573 return ret;
1550} 1574}
1551 1575
1552static void decode_ui(game_ui *ui, const char *encoding) 1576static void set_prefs(game_ui *ui, const config_item *cfg)
1553{ 1577{
1578 ui->pencil_keep_highlight = cfg[0].u.boolean.bval;
1554} 1579}
1555 1580
1556static void game_changed_state(game_ui *ui, const game_state *oldstate, 1581static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -1569,6 +1594,14 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1569 } 1594 }
1570} 1595}
1571 1596
1597static const char *current_key_label(const game_ui *ui,
1598 const game_state *state, int button)
1599{
1600 if (ui->hshow && (button == CURSOR_SELECT))
1601 return ui->hpencil ? "Ink" : "Pencil";
1602 return "";
1603}
1604
1572#define PREFERRED_TILESIZE 48 1605#define PREFERRED_TILESIZE 48
1573#define TILESIZE (ds->tilesize) 1606#define TILESIZE (ds->tilesize)
1574#define BORDER (TILESIZE / 2) 1607#define BORDER (TILESIZE / 2)
@@ -1613,7 +1646,7 @@ static bool check_errors(const game_state *state, long *errors)
1613 for (i = 0; i < a; i++) { 1646 for (i = 0; i < a; i++) {
1614 long clue; 1647 long clue;
1615 1648
1616 j = dsf_canonify(state->clues->dsf, i); 1649 j = dsf_minimal(state->clues->dsf, i);
1617 if (j == i) { 1650 if (j == i) {
1618 cluevals[i] = state->grid[i]; 1651 cluevals[i] = state->grid[i];
1619 } else { 1652 } else {
@@ -1647,7 +1680,7 @@ static bool check_errors(const game_state *state, long *errors)
1647 } 1680 }
1648 1681
1649 for (i = 0; i < a; i++) { 1682 for (i = 0; i < a; i++) {
1650 j = dsf_canonify(state->clues->dsf, i); 1683 j = dsf_minimal(state->clues->dsf, i);
1651 if (j == i) { 1684 if (j == i) {
1652 if ((state->clues->clues[j] & ~CMASK) != cluevals[i]) { 1685 if ((state->clues->clues[j] & ~CMASK) != cluevals[i]) {
1653 errs = true; 1686 errs = true;
@@ -1709,7 +1742,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1709 int tx, ty; 1742 int tx, ty;
1710 char buf[80]; 1743 char buf[80];
1711 1744
1712 button &= ~MOD_MASK; 1745 button = STRIP_BUTTON_MODIFIERS(button);
1713 1746
1714 tx = FROMCOORD(x); 1747 tx = FROMCOORD(x);
1715 ty = FROMCOORD(y); 1748 ty = FROMCOORD(y);
@@ -1726,7 +1759,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1726 ui->hpencil = false; 1759 ui->hpencil = false;
1727 } 1760 }
1728 ui->hcursor = false; 1761 ui->hcursor = false;
1729 return UI_UPDATE; 1762 return MOVE_UI_UPDATE;
1730 } 1763 }
1731 if (button == RIGHT_BUTTON) { 1764 if (button == RIGHT_BUTTON) {
1732 /* 1765 /*
@@ -1746,20 +1779,18 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1746 ui->hshow = false; 1779 ui->hshow = false;
1747 } 1780 }
1748 ui->hcursor = false; 1781 ui->hcursor = false;
1749 return UI_UPDATE; 1782 return MOVE_UI_UPDATE;
1750 } 1783 }
1751 } 1784 }
1752 if (IS_CURSOR_MOVE(button)) { 1785 if (IS_CURSOR_MOVE(button)) {
1753 move_cursor(button, &ui->hx, &ui->hy, w, w, false);
1754 ui->hshow = true;
1755 ui->hcursor = true; 1786 ui->hcursor = true;
1756 return UI_UPDATE; 1787 return move_cursor(button, &ui->hx, &ui->hy, w, w, false, &ui->hshow);
1757 } 1788 }
1758 if (ui->hshow && 1789 if (ui->hshow &&
1759 (button == CURSOR_SELECT)) { 1790 (button == CURSOR_SELECT)) {
1760 ui->hpencil ^= 1; 1791 ui->hpencil ^= 1;
1761 ui->hcursor = true; 1792 ui->hcursor = true;
1762 return UI_UPDATE; 1793 return MOVE_UI_UPDATE;
1763 } 1794 }
1764 1795
1765 if (ui->hshow && 1796 if (ui->hshow &&
@@ -1776,10 +1807,31 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1776 if (ui->hpencil && state->grid[ui->hy*w+ui->hx]) 1807 if (ui->hpencil && state->grid[ui->hy*w+ui->hx])
1777 return NULL; 1808 return NULL;
1778 1809
1810 /*
1811 * If you ask to fill a square with what it already contains,
1812 * or blank it when it's already empty, that has no effect...
1813 */
1814 if ((!ui->hpencil || n == 0) && state->grid[ui->hy*w+ui->hx] == n &&
1815 state->pencil[ui->hy*w+ui->hx] == 0) {
1816 /* ... expect to remove the cursor in mouse mode. */
1817 if (!ui->hcursor) {
1818 ui->hshow = false;
1819 return MOVE_UI_UPDATE;
1820 }
1821 return NULL;
1822 }
1823
1779 sprintf(buf, "%c%d,%d,%d", 1824 sprintf(buf, "%c%d,%d,%d",
1780 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n); 1825 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n);
1781 1826
1782 if (!ui->hcursor) ui->hshow = false; 1827 /*
1828 * Hide the highlight after a keypress, if it was mouse-
1829 * generated. Also, don't hide it if this move has changed
1830 * pencil marks and the user preference says not to hide the
1831 * highlight in that situation.
1832 */
1833 if (!ui->hcursor && !(ui->hpencil && ui->pencil_keep_highlight))
1834 ui->hshow = false;
1783 1835
1784 return dupstr(buf); 1836 return dupstr(buf);
1785 } 1837 }
@@ -1854,7 +1906,7 @@ static game_state *execute_move(const game_state *from, const char *move)
1854#define SIZE(w) ((w) * TILESIZE + 2*BORDER) 1906#define SIZE(w) ((w) * TILESIZE + 2*BORDER)
1855 1907
1856static void game_compute_size(const game_params *params, int tilesize, 1908static void game_compute_size(const game_params *params, int tilesize,
1857 int *x, int *y) 1909 const game_ui *ui, int *x, int *y)
1858{ 1910{
1859 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1911 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1860 struct { int tilesize; } ads, *ds = &ads; 1912 struct { int tilesize; } ads, *ds = &ads;
@@ -1939,6 +1991,7 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
1939 int tx, ty, tw, th; 1991 int tx, ty, tw, th;
1940 int cx, cy, cw, ch; 1992 int cx, cy, cw, ch;
1941 char str[64]; 1993 char str[64];
1994 bool draw_clue = dsf_minimal(clues->dsf, y*w+x) == y*w+x;
1942 1995
1943 tx = BORDER + x * TILESIZE + 1 + GRIDEXTRA; 1996 tx = BORDER + x * TILESIZE + 1 + GRIDEXTRA;
1944 ty = BORDER + y * TILESIZE + 1 + GRIDEXTRA; 1997 ty = BORDER + y * TILESIZE + 1 + GRIDEXTRA;
@@ -1948,13 +2001,13 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
1948 cw = tw = TILESIZE-1-2*GRIDEXTRA; 2001 cw = tw = TILESIZE-1-2*GRIDEXTRA;
1949 ch = th = TILESIZE-1-2*GRIDEXTRA; 2002 ch = th = TILESIZE-1-2*GRIDEXTRA;
1950 2003
1951 if (x > 0 && dsf_canonify(clues->dsf, y*w+x) == dsf_canonify(clues->dsf, y*w+x-1)) 2004 if (x > 0 && dsf_equivalent(clues->dsf, y*w+x, y*w+x-1))
1952 cx -= GRIDEXTRA, cw += GRIDEXTRA; 2005 cx -= GRIDEXTRA, cw += GRIDEXTRA;
1953 if (x+1 < w && dsf_canonify(clues->dsf, y*w+x) == dsf_canonify(clues->dsf, y*w+x+1)) 2006 if (x+1 < w && dsf_equivalent(clues->dsf, y*w+x, y*w+x+1))
1954 cw += GRIDEXTRA; 2007 cw += GRIDEXTRA;
1955 if (y > 0 && dsf_canonify(clues->dsf, y*w+x) == dsf_canonify(clues->dsf, (y-1)*w+x)) 2008 if (y > 0 && dsf_equivalent(clues->dsf, y*w+x, (y-1)*w+x))
1956 cy -= GRIDEXTRA, ch += GRIDEXTRA; 2009 cy -= GRIDEXTRA, ch += GRIDEXTRA;
1957 if (y+1 < w && dsf_canonify(clues->dsf, y*w+x) == dsf_canonify(clues->dsf, (y+1)*w+x)) 2010 if (y+1 < w && dsf_equivalent(clues->dsf, y*w+x, (y+1)*w+x))
1958 ch += GRIDEXTRA; 2011 ch += GRIDEXTRA;
1959 2012
1960 clip(dr, cx, cy, cw, ch); 2013 clip(dr, cx, cy, cw, ch);
@@ -1979,17 +2032,21 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
1979 * Draw the corners of thick lines in corner-adjacent squares, 2032 * Draw the corners of thick lines in corner-adjacent squares,
1980 * which jut into this square by one pixel. 2033 * which jut into this square by one pixel.
1981 */ 2034 */
1982 if (x > 0 && y > 0 && dsf_canonify(clues->dsf, y*w+x) != dsf_canonify(clues->dsf, (y-1)*w+x-1)) 2035 if (x > 0 && y > 0 && !dsf_equivalent(clues->dsf, y*w+x, (y-1)*w+x-1))
1983 draw_rect(dr, tx-GRIDEXTRA, ty-GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); 2036 draw_rect(dr, tx-GRIDEXTRA, ty-GRIDEXTRA,
1984 if (x+1 < w && y > 0 && dsf_canonify(clues->dsf, y*w+x) != dsf_canonify(clues->dsf, (y-1)*w+x+1)) 2037 GRIDEXTRA, GRIDEXTRA, COL_GRID);
1985 draw_rect(dr, tx+TILESIZE-1-2*GRIDEXTRA, ty-GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); 2038 if (x+1 < w && y > 0 && !dsf_equivalent(clues->dsf, y*w+x, (y-1)*w+x+1))
1986 if (x > 0 && y+1 < w && dsf_canonify(clues->dsf, y*w+x) != dsf_canonify(clues->dsf, (y+1)*w+x-1)) 2039 draw_rect(dr, tx+TILESIZE-1-2*GRIDEXTRA, ty-GRIDEXTRA,
1987 draw_rect(dr, tx-GRIDEXTRA, ty+TILESIZE-1-2*GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); 2040 GRIDEXTRA, GRIDEXTRA, COL_GRID);
1988 if (x+1 < w && y+1 < w && dsf_canonify(clues->dsf, y*w+x) != dsf_canonify(clues->dsf, (y+1)*w+x+1)) 2041 if (x > 0 && y+1 < w && !dsf_equivalent(clues->dsf, y*w+x, (y+1)*w+x-1))
1989 draw_rect(dr, tx+TILESIZE-1-2*GRIDEXTRA, ty+TILESIZE-1-2*GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); 2042 draw_rect(dr, tx-GRIDEXTRA, ty+TILESIZE-1-2*GRIDEXTRA,
2043 GRIDEXTRA, GRIDEXTRA, COL_GRID);
2044 if (x+1 < w && y+1 < w && !dsf_equivalent(clues->dsf, y*w+x, (y+1)*w+x+1))
2045 draw_rect(dr, tx+TILESIZE-1-2*GRIDEXTRA, ty+TILESIZE-1-2*GRIDEXTRA,
2046 GRIDEXTRA, GRIDEXTRA, COL_GRID);
1990 2047
1991 /* Draw the box clue. */ 2048 /* Draw the box clue. */
1992 if (dsf_canonify(clues->dsf, y*w+x) == y*w+x) { 2049 if (draw_clue) {
1993 long clue = clues->clues[y*w+x]; 2050 long clue = clues->clues[y*w+x];
1994 long cluetype = clue & CMASK, clueval = clue & ~CMASK; 2051 long cluetype = clue & CMASK, clueval = clue & ~CMASK;
1995 int size = dsf_size(clues->dsf, y*w+x); 2052 int size = dsf_size(clues->dsf, y*w+x);
@@ -2043,7 +2100,7 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
2043 pr = pl + TILESIZE - GRIDEXTRA; 2100 pr = pl + TILESIZE - GRIDEXTRA;
2044 pt = ty + GRIDEXTRA; 2101 pt = ty + GRIDEXTRA;
2045 pb = pt + TILESIZE - GRIDEXTRA; 2102 pb = pt + TILESIZE - GRIDEXTRA;
2046 if (dsf_canonify(clues->dsf, y*w+x) == y*w+x) { 2103 if (draw_clue) {
2047 /* 2104 /*
2048 * Make space for the clue text. 2105 * Make space for the clue text.
2049 */ 2106 */
@@ -2097,7 +2154,7 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
2097 * And move it down a bit if it's collided with some 2154 * And move it down a bit if it's collided with some
2098 * clue text. 2155 * clue text.
2099 */ 2156 */
2100 if (dsf_canonify(clues->dsf, y*w+x) == y*w+x) { 2157 if (draw_clue) {
2101 pt = max(pt, ty + GRIDEXTRA * 3 + TILESIZE/4); 2158 pt = max(pt, ty + GRIDEXTRA * 3 + TILESIZE/4);
2102 } 2159 }
2103 2160
@@ -2134,14 +2191,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2134 2191
2135 if (!ds->started) { 2192 if (!ds->started) {
2136 /* 2193 /*
2137 * The initial contents of the window are not guaranteed and
2138 * can vary with front ends. To be on the safe side, all
2139 * games should start by drawing a big background-colour
2140 * rectangle covering the whole window.
2141 */
2142 draw_rect(dr, 0, 0, SIZE(w), SIZE(w), COL_BACKGROUND);
2143
2144 /*
2145 * Big containing rectangle. 2194 * Big containing rectangle.
2146 */ 2195 */
2147 draw_rect(dr, COORD(0) - GRIDEXTRA, COORD(0) - GRIDEXTRA, 2196 draw_rect(dr, COORD(0) - GRIDEXTRA, COORD(0) - GRIDEXTRA,
@@ -2217,21 +2266,15 @@ static int game_status(const game_state *state)
2217 return state->completed ? +1 : 0; 2266 return state->completed ? +1 : 0;
2218} 2267}
2219 2268
2220static bool game_timing_state(const game_state *state, game_ui *ui) 2269static void game_print_size(const game_params *params, const game_ui *ui,
2221{ 2270 float *x, float *y)
2222 if (state->completed)
2223 return false;
2224 return true;
2225}
2226
2227static void game_print_size(const game_params *params, float *x, float *y)
2228{ 2271{
2229 int pw, ph; 2272 int pw, ph;
2230 2273
2231 /* 2274 /*
2232 * We use 9mm squares by default, like Solo. 2275 * We use 9mm squares by default, like Solo.
2233 */ 2276 */
2234 game_compute_size(params, 900, &pw, &ph); 2277 game_compute_size(params, 900, ui, &pw, &ph);
2235 *x = pw / 100.0F; 2278 *x = pw / 100.0F;
2236 *y = ph / 100.0F; 2279 *y = ph / 100.0F;
2237} 2280}
@@ -2245,7 +2288,7 @@ static void game_print_size(const game_params *params, float *x, float *y)
2245 * single polygon. 2288 * single polygon.
2246 */ 2289 */
2247static void outline_block_structure(drawing *dr, game_drawstate *ds, 2290static void outline_block_structure(drawing *dr, game_drawstate *ds,
2248 int w, int *dsf, int ink) 2291 int w, DSF *dsf, int ink)
2249{ 2292{
2250 int a = w*w; 2293 int a = w*w;
2251 int *coords; 2294 int *coords;
@@ -2258,7 +2301,7 @@ static void outline_block_structure(drawing *dr, game_drawstate *ds,
2258 * Iterate over all the blocks. 2301 * Iterate over all the blocks.
2259 */ 2302 */
2260 for (i = 0; i < a; i++) { 2303 for (i = 0; i < a; i++) {
2261 if (dsf_canonify(dsf, i) != i) 2304 if (dsf_minimal(dsf, i) != i)
2262 continue; 2305 continue;
2263 2306
2264 /* 2307 /*
@@ -2297,11 +2340,11 @@ static void outline_block_structure(drawing *dr, game_drawstate *ds,
2297 tx = x - dy + dx; 2340 tx = x - dy + dx;
2298 ty = y + dx + dy; 2341 ty = y + dx + dy;
2299 nin += (tx >= 0 && tx < w && ty >= 0 && ty < w && 2342 nin += (tx >= 0 && tx < w && ty >= 0 && ty < w &&
2300 dsf_canonify(dsf, ty*w+tx) == i); 2343 dsf_minimal(dsf, ty*w+tx) == i);
2301 tx = x - dy; 2344 tx = x - dy;
2302 ty = y + dx; 2345 ty = y + dx;
2303 nin += (tx >= 0 && tx < w && ty >= 0 && ty < w && 2346 nin += (tx >= 0 && tx < w && ty >= 0 && ty < w &&
2304 dsf_canonify(dsf, ty*w+tx) == i); 2347 dsf_minimal(dsf, ty*w+tx) == i);
2305 if (nin == 0) { 2348 if (nin == 0) {
2306 /* 2349 /*
2307 * Turn right. 2350 * Turn right.
@@ -2338,9 +2381,9 @@ static void outline_block_structure(drawing *dr, game_drawstate *ds,
2338 * somewhere sensible. 2381 * somewhere sensible.
2339 */ 2382 */
2340 assert(x >= 0 && x < w && y >= 0 && y < w && 2383 assert(x >= 0 && x < w && y >= 0 && y < w &&
2341 dsf_canonify(dsf, y*w+x) == i); 2384 dsf_minimal(dsf, y*w+x) == i);
2342 assert(x+dx < 0 || x+dx >= w || y+dy < 0 || y+dy >= w || 2385 assert(x+dx < 0 || x+dx >= w || y+dy < 0 || y+dy >= w ||
2343 dsf_canonify(dsf, (y+dy)*w+(x+dx)) != i); 2386 dsf_minimal(dsf, (y+dy)*w+(x+dx)) != i);
2344 2387
2345 /* 2388 /*
2346 * Record the point we just went past at one end of the 2389 * Record the point we just went past at one end of the
@@ -2367,7 +2410,8 @@ static void outline_block_structure(drawing *dr, game_drawstate *ds,
2367 sfree(coords); 2410 sfree(coords);
2368} 2411}
2369 2412
2370static void game_print(drawing *dr, const game_state *state, int tilesize) 2413static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2414 int tilesize)
2371{ 2415{
2372 int w = state->par.w; 2416 int w = state->par.w;
2373 int ink = print_mono_colour(dr, 0); 2417 int ink = print_mono_colour(dr, 0);
@@ -2413,7 +2457,7 @@ static void game_print(drawing *dr, const game_state *state, int tilesize)
2413 */ 2457 */
2414 for (y = 0; y < w; y++) 2458 for (y = 0; y < w; y++)
2415 for (x = 0; x < w; x++) 2459 for (x = 0; x < w; x++)
2416 if (dsf_canonify(state->clues->dsf, y*w+x) == y*w+x) { 2460 if (dsf_minimal(state->clues->dsf, y*w+x) == y*w+x) {
2417 long clue = state->clues->clues[y*w+x]; 2461 long clue = state->clues->clues[y*w+x];
2418 long cluetype = clue & CMASK, clueval = clue & ~CMASK; 2462 long cluetype = clue & CMASK, clueval = clue & ~CMASK;
2419 int size = dsf_size(state->clues->dsf, y*w+x); 2463 int size = dsf_size(state->clues->dsf, y*w+x);
@@ -2478,13 +2522,15 @@ const struct game thegame = {
2478 dup_game, 2522 dup_game,
2479 free_game, 2523 free_game,
2480 true, solve_game, 2524 true, solve_game,
2481 false, game_can_format_as_text_now, game_text_format, 2525 false, NULL, NULL, /* can_format_as_text_now, text_format */
2526 get_prefs, set_prefs,
2482 new_ui, 2527 new_ui,
2483 free_ui, 2528 free_ui,
2484 encode_ui, 2529 NULL, /* encode_ui */
2485 decode_ui, 2530 NULL, /* decode_ui */
2486 game_request_keys, 2531 game_request_keys,
2487 game_changed_state, 2532 game_changed_state,
2533 current_key_label,
2488 interpret_move, 2534 interpret_move,
2489 execute_move, 2535 execute_move,
2490 PREFERRED_TILESIZE, game_compute_size, game_set_size, 2536 PREFERRED_TILESIZE, game_compute_size, game_set_size,
@@ -2498,7 +2544,7 @@ const struct game thegame = {
2498 game_status, 2544 game_status,
2499 true, false, game_print_size, game_print, 2545 true, false, game_print_size, game_print,
2500 false, /* wants_statusbar */ 2546 false, /* wants_statusbar */
2501 false, game_timing_state, 2547 false, NULL, /* timing_state */
2502 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */ 2548 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */
2503}; 2549};
2504 2550