summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/unruly.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/unruly.c')
-rw-r--r--apps/plugins/puzzles/src/unruly.c189
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
55bool solver_verbose = false; 59static bool solver_verbose = false;
56#endif 60#endif
57 61
58enum { 62enum {
@@ -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
97static const struct game_params unruly_presets[] = { 102static 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
352static game_state *blank_state(int w2, int h2, bool unique) 360static 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
756static 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
786static 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
746static int unruly_solver_check_complete_nums(game_state *state, 811static 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
1452static char *encode_ui(const game_ui *ui) 1532static void game_changed_state(game_ui *ui, const game_state *oldstate,
1453{ 1533 const game_state *newstate)
1454 return NULL;
1455}
1456
1457static void decode_ui(game_ui *ui, const char *encoding)
1458{ 1534{
1459} 1535}
1460 1536
1461static void game_changed_state(game_ui *ui, const game_state *oldstate, 1537static 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
1466struct game_drawstate { 1558struct 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
1583static game_state *execute_move(const game_state *state, const char *move) 1678static 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
1639static void game_compute_size(const game_params *params, int tilesize, 1734static 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
1881static bool game_timing_state(const game_state *state, game_ui *ui) 1973static void game_print_size(const game_params *params, const game_ui *ui,
1882{ 1974 float *x, float *y)
1883 return true;
1884}
1885
1886static 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
1896static void game_print(drawing *dr, const game_state *state, int tilesize) 1984static 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
1982const char *quis; 2073static const char *quis;
1983 2074
1984static void usage_exit(const char *msg) 2075static void usage_exit(const char *msg)
1985{ 2076{