summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/slant.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/slant.c')
-rw-r--r--apps/plugins/puzzles/src/slant.c180
1 files changed, 108 insertions, 72 deletions
diff --git a/apps/plugins/puzzles/src/slant.c b/apps/plugins/puzzles/src/slant.c
index ed290fe57d..bd04b786e6 100644
--- a/apps/plugins/puzzles/src/slant.c
+++ b/apps/plugins/puzzles/src/slant.c
@@ -28,7 +28,11 @@
28#include <string.h> 28#include <string.h>
29#include <assert.h> 29#include <assert.h>
30#include <ctype.h> 30#include <ctype.h>
31#include <math.h> 31#ifdef NO_TGMATH_H
32# include <math.h>
33#else
34# include <tgmath.h>
35#endif
32 36
33#include "puzzles.h" 37#include "puzzles.h"
34 38
@@ -51,7 +55,7 @@ enum {
51 */ 55 */
52#if defined STANDALONE_SOLVER 56#if defined STANDALONE_SOLVER
53#define SOLVER_DIAGNOSTICS 57#define SOLVER_DIAGNOSTICS
54bool verbose = false; 58static bool verbose = false;
55#elif defined SOLVER_DIAGNOSTICS 59#elif defined SOLVER_DIAGNOSTICS
56#define verbose true 60#define verbose true
57#endif 61#endif
@@ -226,6 +230,8 @@ static const char *validate_params(const game_params *params, bool full)
226 230
227 if (params->w < 2 || params->h < 2) 231 if (params->w < 2 || params->h < 2)
228 return "Width and height must both be at least two"; 232 return "Width and height must both be at least two";
233 if (params->w > INT_MAX / params->h)
234 return "Width times height must not be unreasonably large";
229 235
230 return NULL; 236 return NULL;
231} 237}
@@ -238,7 +244,7 @@ struct solver_scratch {
238 * Disjoint set forest which tracks the connected sets of 244 * Disjoint set forest which tracks the connected sets of
239 * points. 245 * points.
240 */ 246 */
241 int *connected; 247 DSF *connected;
242 248
243 /* 249 /*
244 * Counts the number of possible exits from each connected set 250 * Counts the number of possible exits from each connected set
@@ -259,7 +265,7 @@ struct solver_scratch {
259 * Another disjoint set forest. This one tracks _squares_ which 265 * Another disjoint set forest. This one tracks _squares_ which
260 * are known to slant in the same direction. 266 * are known to slant in the same direction.
261 */ 267 */
262 int *equiv; 268 DSF *equiv;
263 269
264 /* 270 /*
265 * Stores slash values which we know for an equivalence class. 271 * Stores slash values which we know for an equivalence class.
@@ -306,10 +312,10 @@ static struct solver_scratch *new_scratch(int w, int h)
306{ 312{
307 int W = w+1, H = h+1; 313 int W = w+1, H = h+1;
308 struct solver_scratch *ret = snew(struct solver_scratch); 314 struct solver_scratch *ret = snew(struct solver_scratch);
309 ret->connected = snewn(W*H, int); 315 ret->connected = dsf_new(W*H);
310 ret->exits = snewn(W*H, int); 316 ret->exits = snewn(W*H, int);
311 ret->border = snewn(W*H, bool); 317 ret->border = snewn(W*H, bool);
312 ret->equiv = snewn(w*h, int); 318 ret->equiv = dsf_new(w*h);
313 ret->slashval = snewn(w*h, signed char); 319 ret->slashval = snewn(w*h, signed char);
314 ret->vbitmap = snewn(w*h, unsigned char); 320 ret->vbitmap = snewn(w*h, unsigned char);
315 return ret; 321 return ret;
@@ -319,10 +325,10 @@ static void free_scratch(struct solver_scratch *sc)
319{ 325{
320 sfree(sc->vbitmap); 326 sfree(sc->vbitmap);
321 sfree(sc->slashval); 327 sfree(sc->slashval);
322 sfree(sc->equiv); 328 dsf_free(sc->equiv);
323 sfree(sc->border); 329 sfree(sc->border);
324 sfree(sc->exits); 330 sfree(sc->exits);
325 sfree(sc->connected); 331 dsf_free(sc->connected);
326 sfree(sc); 332 sfree(sc);
327} 333}
328 334
@@ -330,7 +336,7 @@ static void free_scratch(struct solver_scratch *sc)
330 * Wrapper on dsf_merge() which updates the `exits' and `border' 336 * Wrapper on dsf_merge() which updates the `exits' and `border'
331 * arrays. 337 * arrays.
332 */ 338 */
333static void merge_vertices(int *connected, 339static void merge_vertices(DSF *connected,
334 struct solver_scratch *sc, int i, int j) 340 struct solver_scratch *sc, int i, int j)
335{ 341{
336 int exits = -1; 342 int exits = -1;
@@ -376,7 +382,7 @@ static void decr_exits(struct solver_scratch *sc, int i)
376 382
377static void fill_square(int w, int h, int x, int y, int v, 383static void fill_square(int w, int h, int x, int y, int v,
378 signed char *soln, 384 signed char *soln,
379 int *connected, struct solver_scratch *sc) 385 DSF *connected, struct solver_scratch *sc)
380{ 386{
381 int W = w+1 /*, H = h+1 */; 387 int W = w+1 /*, H = h+1 */;
382 388
@@ -466,13 +472,13 @@ static int slant_solve(int w, int h, const signed char *clues,
466 * Establish a disjoint set forest for tracking connectedness 472 * Establish a disjoint set forest for tracking connectedness
467 * between grid points. 473 * between grid points.
468 */ 474 */
469 dsf_init(sc->connected, W*H); 475 dsf_reinit(sc->connected);
470 476
471 /* 477 /*
472 * Establish a disjoint set forest for tracking which squares 478 * Establish a disjoint set forest for tracking which squares
473 * are known to slant in the same direction. 479 * are known to slant in the same direction.
474 */ 480 */
475 dsf_init(sc->equiv, w*h); 481 dsf_reinit(sc->equiv);
476 482
477 /* 483 /*
478 * Clear the slashval array. 484 * Clear the slashval array.
@@ -991,7 +997,8 @@ static void slant_generate(int w, int h, signed char *soln, random_state *rs)
991{ 997{
992 int W = w+1, H = h+1; 998 int W = w+1, H = h+1;
993 int x, y, i; 999 int x, y, i;
994 int *connected, *indices; 1000 DSF *connected;
1001 int *indices;
995 1002
996 /* 1003 /*
997 * Clear the output. 1004 * Clear the output.
@@ -1002,7 +1009,7 @@ static void slant_generate(int w, int h, signed char *soln, random_state *rs)
1002 * Establish a disjoint set forest for tracking connectedness 1009 * Establish a disjoint set forest for tracking connectedness
1003 * between grid points. 1010 * between grid points.
1004 */ 1011 */
1005 connected = snew_dsf(W*H); 1012 connected = dsf_new(W*H);
1006 1013
1007 /* 1014 /*
1008 * Prepare a list of the squares in the grid, and fill them in 1015 * Prepare a list of the squares in the grid, and fill them in
@@ -1058,7 +1065,7 @@ static void slant_generate(int w, int h, signed char *soln, random_state *rs)
1058 } 1065 }
1059 1066
1060 sfree(indices); 1067 sfree(indices);
1061 sfree(connected); 1068 dsf_free(connected);
1062} 1069}
1063 1070
1064static char *new_game_desc(const game_params *params, random_state *rs, 1071static char *new_game_desc(const game_params *params, random_state *rs,
@@ -1574,28 +1581,69 @@ static char *game_text_format(const game_state *state)
1574struct game_ui { 1581struct game_ui {
1575 int cur_x, cur_y; 1582 int cur_x, cur_y;
1576 bool cur_visible; 1583 bool cur_visible;
1584
1585 /*
1586 * User preference option to swap the left and right mouse
1587 * buttons. There isn't a completely obvious mapping of left and
1588 * right buttons to the two directions of slash, and at least one
1589 * player turned out not to have the same intuition as me.
1590 */
1591 bool swap_buttons;
1577}; 1592};
1578 1593
1594static void legacy_prefs_override(struct game_ui *ui_out)
1595{
1596 static bool initialised = false;
1597 static int swap_buttons = -1;
1598
1599 if (!initialised) {
1600 initialised = true;
1601 swap_buttons = getenv_bool("SLANT_SWAP_BUTTONS", -1);
1602 }
1603
1604 if (swap_buttons != -1)
1605 ui_out->swap_buttons = swap_buttons;
1606}
1607
1579static game_ui *new_ui(const game_state *state) 1608static game_ui *new_ui(const game_state *state)
1580{ 1609{
1581 game_ui *ui = snew(game_ui); 1610 game_ui *ui = snew(game_ui);
1582 ui->cur_x = ui->cur_y = 0; 1611 ui->cur_x = ui->cur_y = 0;
1583 ui->cur_visible = false; 1612 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1613
1614 ui->swap_buttons = false;
1615 legacy_prefs_override(ui);
1616
1584 return ui; 1617 return ui;
1585} 1618}
1586 1619
1587static void free_ui(game_ui *ui) 1620static config_item *get_prefs(game_ui *ui)
1588{ 1621{
1589 sfree(ui); 1622 config_item *ret;
1623
1624 ret = snewn(2, config_item);
1625
1626 ret[0].name = "Mouse button order";
1627 ret[0].kw = "left-button";
1628 ret[0].type = C_CHOICES;
1629 ret[0].u.choices.choicenames = ":Left \\, right /:Left /, right \\";
1630 ret[0].u.choices.choicekws = ":\\:/";
1631 ret[0].u.choices.selected = ui->swap_buttons;
1632
1633 ret[1].name = NULL;
1634 ret[1].type = C_END;
1635
1636 return ret;
1590} 1637}
1591 1638
1592static char *encode_ui(const game_ui *ui) 1639static void set_prefs(game_ui *ui, const config_item *cfg)
1593{ 1640{
1594 return NULL; 1641 ui->swap_buttons = cfg[0].u.choices.selected;
1595} 1642}
1596 1643
1597static void decode_ui(game_ui *ui, const char *encoding) 1644static void free_ui(game_ui *ui)
1598{ 1645{
1646 sfree(ui);
1599} 1647}
1600 1648
1601static void game_changed_state(game_ui *ui, const game_state *oldstate, 1649static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -1603,6 +1651,22 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1603{ 1651{
1604} 1652}
1605 1653
1654static const char *current_key_label(const game_ui *ui,
1655 const game_state *state, int button)
1656{
1657 if (IS_CURSOR_SELECT(button) && ui->cur_visible) {
1658 switch (state->soln[ui->cur_y*state->p.w+ui->cur_x]) {
1659 case 0:
1660 return button == CURSOR_SELECT ? "\\" : "/";
1661 case -1:
1662 return button == CURSOR_SELECT ? "/" : "Blank";
1663 case +1:
1664 return button == CURSOR_SELECT ? "Blank" : "\\";
1665 }
1666 }
1667 return "";
1668}
1669
1606#define PREFERRED_TILESIZE 32 1670#define PREFERRED_TILESIZE 32
1607#define TILESIZE (ds->tilesize) 1671#define TILESIZE (ds->tilesize)
1608#define BORDER TILESIZE 1672#define BORDER TILESIZE
@@ -1638,7 +1702,6 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1638 1702
1639struct game_drawstate { 1703struct game_drawstate {
1640 int tilesize; 1704 int tilesize;
1641 bool started;
1642 long *grid; 1705 long *grid;
1643 long *todraw; 1706 long *todraw;
1644}; 1707};
@@ -1653,51 +1716,34 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1653 enum { CLOCKWISE, ANTICLOCKWISE, NONE } action = NONE; 1716 enum { CLOCKWISE, ANTICLOCKWISE, NONE } action = NONE;
1654 1717
1655 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { 1718 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
1656 /* 1719 if (ui->swap_buttons) {
1657 * This is an utterly awful hack which I should really sort out 1720 if (button == LEFT_BUTTON)
1658 * by means of a proper configuration mechanism. One Slant 1721 button = RIGHT_BUTTON;
1659 * player has observed that they prefer the mouse buttons to 1722 else
1660 * function exactly the opposite way round, so here's a 1723 button = LEFT_BUTTON;
1661 * mechanism for environment-based configuration. I cache the 1724 }
1662 * result in a global variable - yuck! - to avoid repeated
1663 * lookups.
1664 */
1665 {
1666 static int swap_buttons = -1;
1667 if (swap_buttons < 0) {
1668 char *env = getenv("SLANT_SWAP_BUTTONS");
1669 swap_buttons = (env && (env[0] == 'y' || env[0] == 'Y'));
1670 }
1671 if (swap_buttons) {
1672 if (button == LEFT_BUTTON)
1673 button = RIGHT_BUTTON;
1674 else
1675 button = LEFT_BUTTON;
1676 }
1677 }
1678 action = (button == LEFT_BUTTON) ? CLOCKWISE : ANTICLOCKWISE; 1725 action = (button == LEFT_BUTTON) ? CLOCKWISE : ANTICLOCKWISE;
1679 1726
1680 x = FROMCOORD(x); 1727 x = FROMCOORD(x);
1681 y = FROMCOORD(y); 1728 y = FROMCOORD(y);
1682 if (x < 0 || y < 0 || x >= w || y >= h) 1729 if (x < 0 || y < 0 || x >= w || y >= h)
1683 return NULL; 1730 return MOVE_UNUSED;
1684 ui->cur_visible = false; 1731 ui->cur_visible = false;
1685 } else if (IS_CURSOR_SELECT(button)) { 1732 } else if (IS_CURSOR_SELECT(button)) {
1686 if (!ui->cur_visible) { 1733 if (!ui->cur_visible) {
1687 ui->cur_visible = true; 1734 ui->cur_visible = true;
1688 return UI_UPDATE; 1735 return MOVE_UI_UPDATE;
1689 } 1736 }
1690 x = ui->cur_x; 1737 x = ui->cur_x;
1691 y = ui->cur_y; 1738 y = ui->cur_y;
1692 1739
1693 action = (button == CURSOR_SELECT2) ? ANTICLOCKWISE : CLOCKWISE; 1740 action = (button == CURSOR_SELECT2) ? ANTICLOCKWISE : CLOCKWISE;
1694 } else if (IS_CURSOR_MOVE(button)) { 1741 } else if (IS_CURSOR_MOVE(button)) {
1695 move_cursor(button, &ui->cur_x, &ui->cur_y, w, h, false); 1742 return move_cursor(button, &ui->cur_x, &ui->cur_y, w, h, false, &ui->cur_visible);
1696 ui->cur_visible = true;
1697 return UI_UPDATE;
1698 } else if (button == '\\' || button == '\b' || button == '/') { 1743 } else if (button == '\\' || button == '\b' || button == '/') {
1699 int x = ui->cur_x, y = ui->cur_y; 1744 int x = ui->cur_x, y = ui->cur_y;
1700 if (button == ("\\" "\b" "/")[state->soln[y*w + x] + 1]) return NULL; 1745 if (button == ("\\" "\b" "/")[state->soln[y*w + x] + 1])
1746 return MOVE_NO_EFFECT;
1701 sprintf(buf, "%c%d,%d", button == '\b' ? 'C' : button, x, y); 1747 sprintf(buf, "%c%d,%d", button == '\b' ? 'C' : button, x, y);
1702 return dupstr(buf); 1748 return dupstr(buf);
1703 } 1749 }
@@ -1723,7 +1769,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1723 return dupstr(buf); 1769 return dupstr(buf);
1724 } 1770 }
1725 1771
1726 return NULL; 1772 return MOVE_UNUSED;
1727} 1773}
1728 1774
1729static game_state *execute_move(const game_state *state, const char *move) 1775static game_state *execute_move(const game_state *state, const char *move)
@@ -1774,7 +1820,7 @@ static game_state *execute_move(const game_state *state, const char *move)
1774 */ 1820 */
1775 1821
1776static void game_compute_size(const game_params *params, int tilesize, 1822static void game_compute_size(const game_params *params, int tilesize,
1777 int *x, int *y) 1823 const game_ui *ui, int *x, int *y)
1778{ 1824{
1779 /* fool the macros */ 1825 /* fool the macros */
1780 struct dummy { int tilesize; } dummy, *ds = &dummy; 1826 struct dummy { int tilesize; } dummy, *ds = &dummy;
@@ -1832,7 +1878,6 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1832 struct game_drawstate *ds = snew(struct game_drawstate); 1878 struct game_drawstate *ds = snew(struct game_drawstate);
1833 1879
1834 ds->tilesize = 0; 1880 ds->tilesize = 0;
1835 ds->started = false;
1836 ds->grid = snewn((w+2)*(h+2), long); 1881 ds->grid = snewn((w+2)*(h+2), long);
1837 ds->todraw = snewn((w+2)*(h+2), long); 1882 ds->todraw = snewn((w+2)*(h+2), long);
1838 for (i = 0; i < (w+2)*(h+2); i++) 1883 for (i = 0; i < (w+2)*(h+2); i++)
@@ -1972,14 +2017,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1972 else 2017 else
1973 flashing = false; 2018 flashing = false;
1974 2019
1975 if (!ds->started) {
1976 int ww, wh;
1977 game_compute_size(&state->p, TILESIZE, &ww, &wh);
1978 draw_rect(dr, 0, 0, ww, wh, COL_BACKGROUND);
1979 draw_update(dr, 0, 0, ww, wh);
1980 ds->started = true;
1981 }
1982
1983 /* 2020 /*
1984 * Loop over the grid and work out where all the slashes are. 2021 * Loop over the grid and work out where all the slashes are.
1985 * We need to do this because a slash in one square affects the 2022 * We need to do this because a slash in one square affects the
@@ -2082,24 +2119,21 @@ static int game_status(const game_state *state)
2082 return state->completed ? +1 : 0; 2119 return state->completed ? +1 : 0;
2083} 2120}
2084 2121
2085static bool game_timing_state(const game_state *state, game_ui *ui) 2122static void game_print_size(const game_params *params, const game_ui *ui,
2086{ 2123 float *x, float *y)
2087 return true;
2088}
2089
2090static void game_print_size(const game_params *params, float *x, float *y)
2091{ 2124{
2092 int pw, ph; 2125 int pw, ph;
2093 2126
2094 /* 2127 /*
2095 * I'll use 6mm squares by default. 2128 * I'll use 6mm squares by default.
2096 */ 2129 */
2097 game_compute_size(params, 600, &pw, &ph); 2130 game_compute_size(params, 600, ui, &pw, &ph);
2098 *x = pw / 100.0F; 2131 *x = pw / 100.0F;
2099 *y = ph / 100.0F; 2132 *y = ph / 100.0F;
2100} 2133}
2101 2134
2102static void game_print(drawing *dr, const game_state *state, int tilesize) 2135static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2136 int tilesize)
2103{ 2137{
2104 int w = state->p.w, h = state->p.h, W = w+1; 2138 int w = state->p.w, h = state->p.h, W = w+1;
2105 int ink = print_mono_colour(dr, 0); 2139 int ink = print_mono_colour(dr, 0);
@@ -2179,12 +2213,14 @@ const struct game thegame = {
2179 free_game, 2213 free_game,
2180 true, solve_game, 2214 true, solve_game,
2181 true, game_can_format_as_text_now, game_text_format, 2215 true, game_can_format_as_text_now, game_text_format,
2216 get_prefs, set_prefs,
2182 new_ui, 2217 new_ui,
2183 free_ui, 2218 free_ui,
2184 encode_ui, 2219 NULL, /* encode_ui */
2185 decode_ui, 2220 NULL, /* decode_ui */
2186 NULL, /* game_request_keys */ 2221 NULL, /* game_request_keys */
2187 game_changed_state, 2222 game_changed_state,
2223 current_key_label,
2188 interpret_move, 2224 interpret_move,
2189 execute_move, 2225 execute_move,
2190 PREFERRED_TILESIZE, game_compute_size, game_set_size, 2226 PREFERRED_TILESIZE, game_compute_size, game_set_size,
@@ -2198,7 +2234,7 @@ const struct game thegame = {
2198 game_status, 2234 game_status,
2199 true, false, game_print_size, game_print, 2235 true, false, game_print_size, game_print,
2200 false, /* wants_statusbar */ 2236 false, /* wants_statusbar */
2201 false, game_timing_state, 2237 false, NULL, /* timing_state */
2202 0, /* flags */ 2238 0, /* flags */
2203}; 2239};
2204 2240