diff options
Diffstat (limited to 'apps/plugins/puzzles/src/unruly.c')
-rw-r--r-- | apps/plugins/puzzles/src/unruly.c | 189 |
1 files changed, 140 insertions, 49 deletions
diff --git a/apps/plugins/puzzles/src/unruly.c b/apps/plugins/puzzles/src/unruly.c index a1f06332e0..01685b7838 100644 --- a/apps/plugins/puzzles/src/unruly.c +++ b/apps/plugins/puzzles/src/unruly.c | |||
@@ -47,12 +47,16 @@ | |||
47 | #include <string.h> | 47 | #include <string.h> |
48 | #include <assert.h> | 48 | #include <assert.h> |
49 | #include <ctype.h> | 49 | #include <ctype.h> |
50 | #include <math.h> | 50 | #ifdef NO_TGMATH_H |
51 | # include <math.h> | ||
52 | #else | ||
53 | # include <tgmath.h> | ||
54 | #endif | ||
51 | 55 | ||
52 | #include "puzzles.h" | 56 | #include "puzzles.h" |
53 | 57 | ||
54 | #ifdef STANDALONE_SOLVER | 58 | #ifdef STANDALONE_SOLVER |
55 | bool solver_verbose = false; | 59 | static bool solver_verbose = false; |
56 | #endif | 60 | #endif |
57 | 61 | ||
58 | enum { | 62 | enum { |
@@ -81,6 +85,7 @@ struct game_params { | |||
81 | int diff; | 85 | int diff; |
82 | }; | 86 | }; |
83 | #define DIFFLIST(A) \ | 87 | #define DIFFLIST(A) \ |
88 | A(TRIVIAL,Trivial, t) \ | ||
84 | A(EASY,Easy, e) \ | 89 | A(EASY,Easy, e) \ |
85 | A(NORMAL,Normal, n) \ | 90 | A(NORMAL,Normal, n) \ |
86 | 91 | ||
@@ -95,6 +100,7 @@ static char const unruly_diffchars[] = DIFFLIST(ENCODE); | |||
95 | #define DIFFCONFIG DIFFLIST(CONFIG) | 100 | #define DIFFCONFIG DIFFLIST(CONFIG) |
96 | 101 | ||
97 | static const struct game_params unruly_presets[] = { | 102 | static const struct game_params unruly_presets[] = { |
103 | { 8, 8, false, DIFF_TRIVIAL}, | ||
98 | { 8, 8, false, DIFF_EASY}, | 104 | { 8, 8, false, DIFF_EASY}, |
99 | { 8, 8, false, DIFF_NORMAL}, | 105 | { 8, 8, false, DIFF_NORMAL}, |
100 | {10, 10, false, DIFF_EASY}, | 106 | {10, 10, false, DIFF_EASY}, |
@@ -284,6 +290,8 @@ static const char *validate_params(const game_params *params, bool full) | |||
284 | return "Width and height must both be even"; | 290 | return "Width and height must both be even"; |
285 | if (params->w2 < 6 || params->h2 < 6) | 291 | if (params->w2 < 6 || params->h2 < 6) |
286 | return "Width and height must be at least 6"; | 292 | return "Width and height must be at least 6"; |
293 | if (params->w2 > INT_MAX / params->h2) | ||
294 | return "Width times height must not be unreasonably large"; | ||
287 | if (params->unique) { | 295 | if (params->unique) { |
288 | static const long A177790[] = { | 296 | static const long A177790[] = { |
289 | /* | 297 | /* |
@@ -349,7 +357,7 @@ static const char *validate_desc(const game_params *params, const char *desc) | |||
349 | return NULL; | 357 | return NULL; |
350 | } | 358 | } |
351 | 359 | ||
352 | static game_state *blank_state(int w2, int h2, bool unique) | 360 | static game_state *blank_state(int w2, int h2, bool unique, bool new_common) |
353 | { | 361 | { |
354 | game_state *state = snew(game_state); | 362 | game_state *state = snew(game_state); |
355 | int s = w2 * h2; | 363 | int s = w2 * h2; |
@@ -358,12 +366,14 @@ static game_state *blank_state(int w2, int h2, bool unique) | |||
358 | state->h2 = h2; | 366 | state->h2 = h2; |
359 | state->unique = unique; | 367 | state->unique = unique; |
360 | state->grid = snewn(s, char); | 368 | state->grid = snewn(s, char); |
361 | state->common = snew(unruly_common); | ||
362 | state->common->refcount = 1; | ||
363 | state->common->immutable = snewn(s, bool); | ||
364 | |||
365 | memset(state->grid, EMPTY, s); | 369 | memset(state->grid, EMPTY, s); |
366 | memset(state->common->immutable, 0, s*sizeof(bool)); | 370 | |
371 | if (new_common) { | ||
372 | state->common = snew(unruly_common); | ||
373 | state->common->refcount = 1; | ||
374 | state->common->immutable = snewn(s, bool); | ||
375 | memset(state->common->immutable, 0, s*sizeof(bool)); | ||
376 | } | ||
367 | 377 | ||
368 | state->completed = state->cheated = false; | 378 | state->completed = state->cheated = false; |
369 | 379 | ||
@@ -376,7 +386,7 @@ static game_state *new_game(midend *me, const game_params *params, | |||
376 | int w2 = params->w2, h2 = params->h2; | 386 | int w2 = params->w2, h2 = params->h2; |
377 | int s = w2 * h2; | 387 | int s = w2 * h2; |
378 | 388 | ||
379 | game_state *state = blank_state(w2, h2, params->unique); | 389 | game_state *state = blank_state(w2, h2, params->unique, true); |
380 | 390 | ||
381 | const char *p = desc; | 391 | const char *p = desc; |
382 | int pos = 0; | 392 | int pos = 0; |
@@ -413,7 +423,7 @@ static game_state *dup_game(const game_state *state) | |||
413 | int w2 = state->w2, h2 = state->h2; | 423 | int w2 = state->w2, h2 = state->h2; |
414 | int s = w2 * h2; | 424 | int s = w2 * h2; |
415 | 425 | ||
416 | game_state *ret = blank_state(w2, h2, state->unique); | 426 | game_state *ret = blank_state(w2, h2, state->unique, false); |
417 | 427 | ||
418 | memcpy(ret->grid, state->grid, s); | 428 | memcpy(ret->grid, state->grid, s); |
419 | ret->common = state->common; | 429 | ret->common = state->common; |
@@ -743,6 +753,61 @@ static int unruly_solver_fill_row(game_state *state, int i, bool horizontal, | |||
743 | return ret; | 753 | return ret; |
744 | } | 754 | } |
745 | 755 | ||
756 | static int unruly_solver_check_single_gap(game_state *state, | ||
757 | int *complete, bool horizontal, | ||
758 | int *rowcount, int *colcount, | ||
759 | char fill) | ||
760 | { | ||
761 | int w2 = state->w2, h2 = state->h2; | ||
762 | int count = (horizontal ? h2 : w2); /* number of rows to check */ | ||
763 | int target = (horizontal ? w2 : h2) / 2; /* target number of 0s/1s */ | ||
764 | int *other = (horizontal ? rowcount : colcount); | ||
765 | |||
766 | int ret = 0; | ||
767 | |||
768 | int i; | ||
769 | /* Check for completed rows/cols for one number, then fill in the rest */ | ||
770 | for (i = 0; i < count; i++) { | ||
771 | if (complete[i] == target && other[i] == target - 1) { | ||
772 | #ifdef STANDALONE_SOLVER | ||
773 | if (solver_verbose) { | ||
774 | printf("Solver: Row %i has only one square left which must be " | ||
775 | "%c\n", i, (fill == N_ZERO ? '0' : '1')); | ||
776 | } | ||
777 | #endif | ||
778 | ret += unruly_solver_fill_row(state, i, horizontal, rowcount, | ||
779 | colcount, fill); | ||
780 | } | ||
781 | } | ||
782 | |||
783 | return ret; | ||
784 | } | ||
785 | |||
786 | static int unruly_solver_check_all_single_gap(game_state *state, | ||
787 | struct unruly_scratch *scratch) | ||
788 | { | ||
789 | int ret = 0; | ||
790 | |||
791 | ret += | ||
792 | unruly_solver_check_single_gap(state, scratch->ones_rows, true, | ||
793 | scratch->zeros_rows, | ||
794 | scratch->zeros_cols, N_ZERO); | ||
795 | ret += | ||
796 | unruly_solver_check_single_gap(state, scratch->ones_cols, false, | ||
797 | scratch->zeros_rows, | ||
798 | scratch->zeros_cols, N_ZERO); | ||
799 | ret += | ||
800 | unruly_solver_check_single_gap(state, scratch->zeros_rows, true, | ||
801 | scratch->ones_rows, | ||
802 | scratch->ones_cols, N_ONE); | ||
803 | ret += | ||
804 | unruly_solver_check_single_gap(state, scratch->zeros_cols, false, | ||
805 | scratch->ones_rows, | ||
806 | scratch->ones_cols, N_ONE); | ||
807 | |||
808 | return ret; | ||
809 | } | ||
810 | |||
746 | static int unruly_solver_check_complete_nums(game_state *state, | 811 | static int unruly_solver_check_complete_nums(game_state *state, |
747 | int *complete, bool horizontal, | 812 | int *complete, bool horizontal, |
748 | int *rowcount, int *colcount, | 813 | int *rowcount, int *colcount, |
@@ -1140,11 +1205,24 @@ static int unruly_solve_game(game_state *state, | |||
1140 | 1205 | ||
1141 | /* Keep using the simpler techniques while they produce results */ | 1206 | /* Keep using the simpler techniques while they produce results */ |
1142 | if (done) { | 1207 | if (done) { |
1143 | if (maxdiff < DIFF_EASY) | 1208 | if (maxdiff < DIFF_TRIVIAL) |
1144 | maxdiff = DIFF_EASY; | 1209 | maxdiff = DIFF_TRIVIAL; |
1145 | continue; | 1210 | continue; |
1146 | } | 1211 | } |
1147 | 1212 | ||
1213 | /* Check for rows with only one unfilled square */ | ||
1214 | done += unruly_solver_check_all_single_gap(state, scratch); | ||
1215 | |||
1216 | if (done) { | ||
1217 | if (maxdiff < DIFF_TRIVIAL) | ||
1218 | maxdiff = DIFF_TRIVIAL; | ||
1219 | continue; | ||
1220 | } | ||
1221 | |||
1222 | /* Easy techniques */ | ||
1223 | if (diff < DIFF_EASY) | ||
1224 | break; | ||
1225 | |||
1148 | /* Check for completed rows */ | 1226 | /* Check for completed rows */ |
1149 | done += unruly_solver_check_all_complete_nums(state, scratch); | 1227 | done += unruly_solver_check_all_complete_nums(state, scratch); |
1150 | 1228 | ||
@@ -1302,7 +1380,7 @@ static char *new_game_desc(const game_params *params, random_state *rs, | |||
1302 | 1380 | ||
1303 | while (true) { | 1381 | while (true) { |
1304 | attempts++; | 1382 | attempts++; |
1305 | state = blank_state(w2, h2, params->unique); | 1383 | state = blank_state(w2, h2, params->unique, true); |
1306 | scratch = unruly_new_scratch(state); | 1384 | scratch = unruly_new_scratch(state); |
1307 | if (unruly_fill_game(state, scratch, rs)) | 1385 | if (unruly_fill_game(state, scratch, rs)) |
1308 | break; | 1386 | break; |
@@ -1320,6 +1398,8 @@ static char *new_game_desc(const game_params *params, random_state *rs, | |||
1320 | temp_verbose = solver_verbose; | 1398 | temp_verbose = solver_verbose; |
1321 | solver_verbose = false; | 1399 | solver_verbose = false; |
1322 | } | 1400 | } |
1401 | #else | ||
1402 | (void)attempts; | ||
1323 | #endif | 1403 | #endif |
1324 | 1404 | ||
1325 | unruly_free_scratch(scratch); | 1405 | unruly_free_scratch(scratch); |
@@ -1439,7 +1519,7 @@ static game_ui *new_ui(const game_state *state) | |||
1439 | game_ui *ret = snew(game_ui); | 1519 | game_ui *ret = snew(game_ui); |
1440 | 1520 | ||
1441 | ret->cx = ret->cy = 0; | 1521 | ret->cx = ret->cy = 0; |
1442 | ret->cursor = false; | 1522 | ret->cursor = getenv_bool("PUZZLES_SHOW_CURSOR", false); |
1443 | 1523 | ||
1444 | return ret; | 1524 | return ret; |
1445 | } | 1525 | } |
@@ -1449,18 +1529,30 @@ static void free_ui(game_ui *ui) | |||
1449 | sfree(ui); | 1529 | sfree(ui); |
1450 | } | 1530 | } |
1451 | 1531 | ||
1452 | static char *encode_ui(const game_ui *ui) | 1532 | static void game_changed_state(game_ui *ui, const game_state *oldstate, |
1453 | { | 1533 | const game_state *newstate) |
1454 | return NULL; | ||
1455 | } | ||
1456 | |||
1457 | static void decode_ui(game_ui *ui, const char *encoding) | ||
1458 | { | 1534 | { |
1459 | } | 1535 | } |
1460 | 1536 | ||
1461 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | 1537 | static const char *current_key_label(const game_ui *ui, |
1462 | const game_state *newstate) | 1538 | const game_state *state, int button) |
1463 | { | 1539 | { |
1540 | int hx = ui->cx, hy = ui->cy; | ||
1541 | int w2 = state->w2; | ||
1542 | char i = state->grid[hy * w2 + hx]; | ||
1543 | |||
1544 | if (ui->cursor && IS_CURSOR_SELECT(button)) { | ||
1545 | if (state->common->immutable[hy * w2 + hx]) return ""; | ||
1546 | switch (i) { | ||
1547 | case EMPTY: | ||
1548 | return button == CURSOR_SELECT ? "Black" : "White"; | ||
1549 | case N_ONE: | ||
1550 | return button == CURSOR_SELECT ? "White" : "Empty"; | ||
1551 | case N_ZERO: | ||
1552 | return button == CURSOR_SELECT ? "Empty" : "Black"; | ||
1553 | } | ||
1554 | } | ||
1555 | return ""; | ||
1464 | } | 1556 | } |
1465 | 1557 | ||
1466 | struct game_drawstate { | 1558 | struct game_drawstate { |
@@ -1520,7 +1612,9 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1520 | 1612 | ||
1521 | int w2 = state->w2, h2 = state->h2; | 1613 | int w2 = state->w2, h2 = state->h2; |
1522 | 1614 | ||
1523 | button &= ~MOD_MASK; | 1615 | char *nullret = MOVE_NO_EFFECT; |
1616 | |||
1617 | button = STRIP_BUTTON_MODIFIERS(button); | ||
1524 | 1618 | ||
1525 | /* Mouse click */ | 1619 | /* Mouse click */ |
1526 | if (button == LEFT_BUTTON || button == RIGHT_BUTTON || | 1620 | if (button == LEFT_BUTTON || button == RIGHT_BUTTON || |
@@ -1529,17 +1623,18 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1529 | && oy >= (ds->tilesize / 2) && gy < h2) { | 1623 | && oy >= (ds->tilesize / 2) && gy < h2) { |
1530 | hx = gx; | 1624 | hx = gx; |
1531 | hy = gy; | 1625 | hy = gy; |
1532 | ui->cursor = false; | 1626 | if (ui->cursor) { |
1627 | ui->cursor = false; | ||
1628 | nullret = MOVE_UI_UPDATE; | ||
1629 | } | ||
1533 | } else | 1630 | } else |
1534 | return NULL; | 1631 | return MOVE_UNUSED; |
1535 | } | 1632 | } |
1536 | 1633 | ||
1537 | /* Keyboard move */ | 1634 | /* Keyboard move */ |
1538 | if (IS_CURSOR_MOVE(button)) { | 1635 | if (IS_CURSOR_MOVE(button)) |
1539 | move_cursor(button, &ui->cx, &ui->cy, w2, h2, false); | 1636 | return move_cursor(button, &ui->cx, &ui->cy, w2, h2, false, |
1540 | ui->cursor = true; | 1637 | &ui->cursor); |
1541 | return UI_UPDATE; | ||
1542 | } | ||
1543 | 1638 | ||
1544 | /* Place one */ | 1639 | /* Place one */ |
1545 | if ((ui->cursor && (button == CURSOR_SELECT || button == CURSOR_SELECT2 | 1640 | if ((ui->cursor && (button == CURSOR_SELECT || button == CURSOR_SELECT2 |
@@ -1551,7 +1646,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1551 | char c, i; | 1646 | char c, i; |
1552 | 1647 | ||
1553 | if (state->common->immutable[hy * w2 + hx]) | 1648 | if (state->common->immutable[hy * w2 + hx]) |
1554 | return NULL; | 1649 | return nullret; |
1555 | 1650 | ||
1556 | c = '-'; | 1651 | c = '-'; |
1557 | i = state->grid[hy * w2 + hx]; | 1652 | i = state->grid[hy * w2 + hx]; |
@@ -1571,13 +1666,13 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1571 | 1666 | ||
1572 | if (state->grid[hy * w2 + hx] == | 1667 | if (state->grid[hy * w2 + hx] == |
1573 | (c == '0' ? N_ZERO : c == '1' ? N_ONE : EMPTY)) | 1668 | (c == '0' ? N_ZERO : c == '1' ? N_ONE : EMPTY)) |
1574 | return NULL; /* don't put no-ops on the undo chain */ | 1669 | return nullret; /* don't put no-ops on the undo chain */ |
1575 | 1670 | ||
1576 | sprintf(buf, "P%c,%d,%d", c, hx, hy); | 1671 | sprintf(buf, "P%c,%d,%d", c, hx, hy); |
1577 | 1672 | ||
1578 | return dupstr(buf); | 1673 | return dupstr(buf); |
1579 | } | 1674 | } |
1580 | return NULL; | 1675 | return MOVE_UNUSED; |
1581 | } | 1676 | } |
1582 | 1677 | ||
1583 | static game_state *execute_move(const game_state *state, const char *move) | 1678 | static game_state *execute_move(const game_state *state, const char *move) |
@@ -1637,7 +1732,7 @@ static game_state *execute_move(const game_state *state, const char *move) | |||
1637 | */ | 1732 | */ |
1638 | 1733 | ||
1639 | static void game_compute_size(const game_params *params, int tilesize, | 1734 | static void game_compute_size(const game_params *params, int tilesize, |
1640 | int *x, int *y) | 1735 | const game_ui *ui, int *x, int *y) |
1641 | { | 1736 | { |
1642 | *x = tilesize * (params->w2 + 1); | 1737 | *x = tilesize * (params->w2 + 1); |
1643 | *y = tilesize * (params->h2 + 1); | 1738 | *y = tilesize * (params->h2 + 1); |
@@ -1788,9 +1883,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
1788 | int x, y, i; | 1883 | int x, y, i; |
1789 | 1884 | ||
1790 | if (!ds->started) { | 1885 | if (!ds->started) { |
1791 | /* Main window background */ | ||
1792 | draw_rect(dr, 0, 0, TILE_SIZE * (w2+1), TILE_SIZE * (h2+1), | ||
1793 | COL_BACKGROUND); | ||
1794 | /* Outer edge of grid */ | 1886 | /* Outer edge of grid */ |
1795 | draw_rect(dr, COORD(0)-TILE_SIZE/10, COORD(0)-TILE_SIZE/10, | 1887 | draw_rect(dr, COORD(0)-TILE_SIZE/10, COORD(0)-TILE_SIZE/10, |
1796 | TILE_SIZE*w2 + 2*(TILE_SIZE/10) - 1, | 1888 | TILE_SIZE*w2 + 2*(TILE_SIZE/10) - 1, |
@@ -1878,22 +1970,19 @@ static int game_status(const game_state *state) | |||
1878 | return state->completed ? +1 : 0; | 1970 | return state->completed ? +1 : 0; |
1879 | } | 1971 | } |
1880 | 1972 | ||
1881 | static bool game_timing_state(const game_state *state, game_ui *ui) | 1973 | static void game_print_size(const game_params *params, const game_ui *ui, |
1882 | { | 1974 | float *x, float *y) |
1883 | return true; | ||
1884 | } | ||
1885 | |||
1886 | static void game_print_size(const game_params *params, float *x, float *y) | ||
1887 | { | 1975 | { |
1888 | int pw, ph; | 1976 | int pw, ph; |
1889 | 1977 | ||
1890 | /* Using 7mm squares */ | 1978 | /* Using 7mm squares */ |
1891 | game_compute_size(params, 700, &pw, &ph); | 1979 | game_compute_size(params, 700, ui, &pw, &ph); |
1892 | *x = pw / 100.0F; | 1980 | *x = pw / 100.0F; |
1893 | *y = ph / 100.0F; | 1981 | *y = ph / 100.0F; |
1894 | } | 1982 | } |
1895 | 1983 | ||
1896 | static void game_print(drawing *dr, const game_state *state, int tilesize) | 1984 | static void game_print(drawing *dr, const game_state *state, const game_ui *ui, |
1985 | int tilesize) | ||
1897 | { | 1986 | { |
1898 | int w2 = state->w2, h2 = state->h2; | 1987 | int w2 = state->w2, h2 = state->h2; |
1899 | int x, y; | 1988 | int x, y; |
@@ -1946,12 +2035,14 @@ const struct game thegame = { | |||
1946 | free_game, | 2035 | free_game, |
1947 | true, solve_game, | 2036 | true, solve_game, |
1948 | true, game_can_format_as_text_now, game_text_format, | 2037 | true, game_can_format_as_text_now, game_text_format, |
2038 | NULL, NULL, /* get_prefs, set_prefs */ | ||
1949 | new_ui, | 2039 | new_ui, |
1950 | free_ui, | 2040 | free_ui, |
1951 | encode_ui, | 2041 | NULL, /* encode_ui */ |
1952 | decode_ui, | 2042 | NULL, /* decode_ui */ |
1953 | NULL, /* game_request_keys */ | 2043 | NULL, /* game_request_keys */ |
1954 | game_changed_state, | 2044 | game_changed_state, |
2045 | current_key_label, | ||
1955 | interpret_move, | 2046 | interpret_move, |
1956 | execute_move, | 2047 | execute_move, |
1957 | DEFAULT_TILE_SIZE, game_compute_size, game_set_size, | 2048 | DEFAULT_TILE_SIZE, game_compute_size, game_set_size, |
@@ -1965,7 +2056,7 @@ const struct game thegame = { | |||
1965 | game_status, | 2056 | game_status, |
1966 | true, false, game_print_size, game_print, | 2057 | true, false, game_print_size, game_print, |
1967 | false, /* wants_statusbar */ | 2058 | false, /* wants_statusbar */ |
1968 | false, game_timing_state, | 2059 | false, NULL, /* timing_state */ |
1969 | 0, /* flags */ | 2060 | 0, /* flags */ |
1970 | }; | 2061 | }; |
1971 | 2062 | ||
@@ -1979,7 +2070,7 @@ const struct game thegame = { | |||
1979 | 2070 | ||
1980 | /* Most of the standalone solver code was copied from unequal.c and singles.c */ | 2071 | /* Most of the standalone solver code was copied from unequal.c and singles.c */ |
1981 | 2072 | ||
1982 | const char *quis; | 2073 | static const char *quis; |
1983 | 2074 | ||
1984 | static void usage_exit(const char *msg) | 2075 | static void usage_exit(const char *msg) |
1985 | { | 2076 | { |