diff options
Diffstat (limited to 'apps/plugins/puzzles/src/keen.c')
-rw-r--r-- | apps/plugins/puzzles/src/keen.c | 250 |
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 { | |||
69 | struct clues { | 73 | struct 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 | ||
680 | static int solver(int w, int *dsf, long *clues, digit *soln, int maxdiff) | 684 | static 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 | ||
743 | static char *encode_block_structure(char *p, int w, int *dsf) | 747 | static 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 | ||
818 | static const char *parse_block_structure(const char **p, int w, int *dsf) | 822 | static 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 | |||
1295 | static const char *validate_desc(const game_params *params, const char *desc) | 1298 | static 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 | ||
1493 | static bool game_can_format_as_text_now(const game_params *params) | ||
1494 | { | ||
1495 | return true; | ||
1496 | } | ||
1497 | |||
1498 | static char *game_text_format(const game_state *state) | ||
1499 | { | ||
1500 | return NULL; | ||
1501 | } | ||
1502 | |||
1503 | struct game_ui { | 1503 | struct 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 | ||
1530 | static game_ui *new_ui(const game_state *state) | 1541 | static 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 | ||
1547 | static char *encode_ui(const game_ui *ui) | 1559 | static 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 | ||
1552 | static void decode_ui(game_ui *ui, const char *encoding) | 1576 | static 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 | ||
1556 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | 1581 | static 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 | ||
1597 | static 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 | ||
1856 | static void game_compute_size(const game_params *params, int tilesize, | 1908 | static 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 | ||
2220 | static bool game_timing_state(const game_state *state, game_ui *ui) | 2269 | static 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 | |||
2227 | static 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 | */ |
2247 | static void outline_block_structure(drawing *dr, game_drawstate *ds, | 2290 | static 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 | ||
2370 | static void game_print(drawing *dr, const game_state *state, int tilesize) | 2413 | static 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 | ||