summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/pattern.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/pattern.c')
-rw-r--r--apps/plugins/puzzles/src/pattern.c224
1 files changed, 145 insertions, 79 deletions
diff --git a/apps/plugins/puzzles/src/pattern.c b/apps/plugins/puzzles/src/pattern.c
index df720b7d82..b370a3dc2f 100644
--- a/apps/plugins/puzzles/src/pattern.c
+++ b/apps/plugins/puzzles/src/pattern.c
@@ -7,7 +7,12 @@
7#include <string.h> 7#include <string.h>
8#include <assert.h> 8#include <assert.h>
9#include <ctype.h> 9#include <ctype.h>
10#include <math.h> 10#include <limits.h>
11#ifdef NO_TGMATH_H
12# include <math.h>
13#else
14# include <tgmath.h>
15#endif
11 16
12#include "puzzles.h" 17#include "puzzles.h"
13 18
@@ -53,6 +58,7 @@ typedef struct game_state_common {
53 int *rowdata, *rowlen; 58 int *rowdata, *rowlen;
54 bool *immutable; 59 bool *immutable;
55 int refcount; 60 int refcount;
61 enum { FS_SMALL, FS_LARGE } fontsize;
56} game_state_common; 62} game_state_common;
57 63
58struct game_state { 64struct game_state {
@@ -176,7 +182,10 @@ static const char *validate_params(const game_params *params, bool full)
176{ 182{
177 if (params->w <= 0 || params->h <= 0) 183 if (params->w <= 0 || params->h <= 0)
178 return "Width and height must both be greater than zero"; 184 return "Width and height must both be greater than zero";
179 if (params->w * params->w < 2) 185 if (params->w > INT_MAX - 1 || params->h > INT_MAX - 1 ||
186 params->w > INT_MAX / params->h)
187 return "Puzzle must not be unreasonably large";
188 if (params->w * params->h < 2)
180 return "Grid must contain at least two squares"; 189 return "Grid must contain at least two squares";
181 return NULL; 190 return NULL;
182} 191}
@@ -360,7 +369,7 @@ static int compute_rowdata(int *ret, unsigned char *start, int len, int step)
360#define STILL_UNKNOWN 3 369#define STILL_UNKNOWN 3
361 370
362#ifdef STANDALONE_SOLVER 371#ifdef STANDALONE_SOLVER
363bool verbose = false; 372static bool verbose = false;
364#endif 373#endif
365 374
366static bool do_recurse(unsigned char *known, unsigned char *deduced, 375static bool do_recurse(unsigned char *known, unsigned char *deduced,
@@ -441,6 +450,8 @@ static bool do_row(unsigned char *known, unsigned char *deduced,
441 int rowlen, i, freespace; 450 int rowlen, i, freespace;
442 bool done_any; 451 bool done_any;
443 452
453 assert(len >= 0); /* avoid compile warnings about the memsets below */
454
444 freespace = len+1; 455 freespace = len+1;
445 for (rowlen = 0; data[rowlen]; rowlen++) { 456 for (rowlen = 0; data[rowlen]; rowlen++) {
446 minpos_done[rowlen] = minpos_ok[rowlen] = len - 1; 457 minpos_done[rowlen] = minpos_ok[rowlen] = len - 1;
@@ -654,7 +665,7 @@ static bool solve_puzzle(const game_state *state, unsigned char *grid,
654#ifndef STANDALONE_PICTURE_GENERATOR 665#ifndef STANDALONE_PICTURE_GENERATOR
655static unsigned char *generate_soluble(random_state *rs, int w, int h) 666static unsigned char *generate_soluble(random_state *rs, int w, int h)
656{ 667{
657 int i, j, ntries, max; 668 int i, j, max;
658 bool ok; 669 bool ok;
659 unsigned char *grid, *matrix, *workspace; 670 unsigned char *grid, *matrix, *workspace;
660 unsigned int *changed_h, *changed_w; 671 unsigned int *changed_h, *changed_w;
@@ -670,11 +681,7 @@ static unsigned char *generate_soluble(random_state *rs, int w, int h)
670 changed_w = snewn(max+1, unsigned int); 681 changed_w = snewn(max+1, unsigned int);
671 rowdata = snewn(max+1, int); 682 rowdata = snewn(max+1, int);
672 683
673 ntries = 0;
674
675 do { 684 do {
676 ntries++;
677
678 generate(rs, w, h, grid); 685 generate(rs, w, h, grid);
679 686
680 /* 687 /*
@@ -719,7 +726,7 @@ static unsigned char *generate_soluble(random_state *rs, int w, int h)
719#endif 726#endif
720 727
721#ifdef STANDALONE_PICTURE_GENERATOR 728#ifdef STANDALONE_PICTURE_GENERATOR
722unsigned char *picture; 729static unsigned char *picture;
723#endif 730#endif
724 731
725static char *new_game_desc(const game_params *params, random_state *rs, 732static char *new_game_desc(const game_params *params, random_state *rs,
@@ -908,6 +915,10 @@ static const char *validate_desc(const game_params *params, const char *desc)
908 p = desc; 915 p = desc;
909 while (*desc && isdigit((unsigned char)*desc)) desc++; 916 while (*desc && isdigit((unsigned char)*desc)) desc++;
910 n = atoi(p); 917 n = atoi(p);
918 if (n <= 0)
919 return "all clues must be positive";
920 if (n > INT_MAX - 1)
921 return "at least one clue is grossly excessive";
911 rowspace -= n+1; 922 rowspace -= n+1;
912 923
913 if (rowspace < 0) { 924 if (rowspace < 0) {
@@ -965,7 +976,7 @@ static const char *validate_desc(const game_params *params, const char *desc)
965static game_state *new_game(midend *me, const game_params *params, 976static game_state *new_game(midend *me, const game_params *params,
966 const char *desc) 977 const char *desc)
967{ 978{
968 int i; 979 int i, j;
969 const char *p; 980 const char *p;
970 game_state *state = snew(game_state); 981 game_state *state = snew(game_state);
971 982
@@ -1003,6 +1014,26 @@ static game_state *new_game(midend *me, const game_params *params,
1003 } 1014 }
1004 } 1015 }
1005 1016
1017 /*
1018 * Choose a font size based on the clues. If any column clue is
1019 * more than one digit, switch to the smaller size.
1020 */
1021 state->common->fontsize = FS_LARGE;
1022 for (i = 0; i < params->w; i++)
1023 for (j = 0; j < state->common->rowlen[i]; j++)
1024 if (state->common->rowdata[state->common->rowsize * i + j] >= 10)
1025 state->common->fontsize = FS_SMALL;
1026 /*
1027 * We might also need to use the small font if there are lots of
1028 * row clues. We assume that all clues are one digit and that a
1029 * single-digit clue takes up 1.5 tiles, of which the clue is 0.5
1030 * tiles and the space is 1.0 tiles.
1031 */
1032 for (i = params->w; i < params->w + params->h; i++)
1033 if ((state->common->rowlen[i] * 3 - 2) >
1034 TLBORDER(state->common->w) * 2)
1035 state->common->fontsize = FS_SMALL;
1036
1006 if (desc[-1] == ',') { 1037 if (desc[-1] == ',') {
1007 /* 1038 /*
1008 * Optional extra piece of game description which fills in 1039 * Optional extra piece of game description which fills in
@@ -1217,7 +1248,7 @@ static game_ui *new_ui(const game_state *state)
1217 ret = snew(game_ui); 1248 ret = snew(game_ui);
1218 ret->dragging = false; 1249 ret->dragging = false;
1219 ret->cur_x = ret->cur_y = 0; 1250 ret->cur_x = ret->cur_y = 0;
1220 ret->cur_visible = false; 1251 ret->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1221 1252
1222 return ret; 1253 return ret;
1223} 1254}
@@ -1227,18 +1258,26 @@ static void free_ui(game_ui *ui)
1227 sfree(ui); 1258 sfree(ui);
1228} 1259}
1229 1260
1230static char *encode_ui(const game_ui *ui) 1261static void game_changed_state(game_ui *ui, const game_state *oldstate,
1231{ 1262 const game_state *newstate)
1232 return NULL;
1233}
1234
1235static void decode_ui(game_ui *ui, const char *encoding)
1236{ 1263{
1237} 1264}
1238 1265
1239static void game_changed_state(game_ui *ui, const game_state *oldstate, 1266static const char *current_key_label(const game_ui *ui,
1240 const game_state *newstate) 1267 const game_state *state, int button)
1241{ 1268{
1269 if (IS_CURSOR_SELECT(button)) {
1270 if (!ui->cur_visible) return "";
1271 switch (state->grid[ui->cur_y * state->common->w + ui->cur_x]) {
1272 case GRID_UNKNOWN:
1273 return button == CURSOR_SELECT ? "Black" : "White";
1274 case GRID_FULL:
1275 return button == CURSOR_SELECT ? "White" : "Grey";
1276 case GRID_EMPTY:
1277 return button == CURSOR_SELECT ? "Grey" : "Black";
1278 }
1279 }
1280 return "";
1242} 1281}
1243 1282
1244struct game_drawstate { 1283struct game_drawstate {
@@ -1247,6 +1286,7 @@ struct game_drawstate {
1247 int tilesize; 1286 int tilesize;
1248 unsigned char *visible, *numcolours; 1287 unsigned char *visible, *numcolours;
1249 int cur_x, cur_y; 1288 int cur_x, cur_y;
1289 char *strbuf; /* Used for formatting clues. */
1250}; 1290};
1251 1291
1252static char *interpret_move(const game_state *state, game_ui *ui, 1292static char *interpret_move(const game_state *state, game_ui *ui,
@@ -1254,7 +1294,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1254 int x, int y, int button) 1294 int x, int y, int button)
1255{ 1295{
1256 bool control = button & MOD_CTRL, shift = button & MOD_SHFT; 1296 bool control = button & MOD_CTRL, shift = button & MOD_SHFT;
1257 button &= ~MOD_MASK; 1297 button = STRIP_BUTTON_MODIFIERS(button);
1258 1298
1259 x = FROMCOORD(state->common->w, x); 1299 x = FROMCOORD(state->common->w, x);
1260 y = FROMCOORD(state->common->h, y); 1300 y = FROMCOORD(state->common->h, y);
@@ -1294,7 +1334,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1294 ui->drag_start_y = ui->drag_end_y = y; 1334 ui->drag_start_y = ui->drag_end_y = y;
1295 ui->cur_visible = false; 1335 ui->cur_visible = false;
1296 1336
1297 return UI_UPDATE; 1337 return MOVE_UI_UPDATE;
1298 } 1338 }
1299 1339
1300 if (ui->dragging && button == ui->drag) { 1340 if (ui->dragging && button == ui->drag) {
@@ -1323,7 +1363,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1323 ui->drag_end_x = x; 1363 ui->drag_end_x = x;
1324 ui->drag_end_y = y; 1364 ui->drag_end_y = y;
1325 1365
1326 return UI_UPDATE; 1366 return MOVE_UI_UPDATE;
1327 } 1367 }
1328 1368
1329 if (ui->dragging && button == ui->release) { 1369 if (ui->dragging && button == ui->release) {
@@ -1351,20 +1391,21 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1351 x1, y1, x2-x1+1, y2-y1+1); 1391 x1, y1, x2-x1+1, y2-y1+1);
1352 return dupstr(buf); 1392 return dupstr(buf);
1353 } else 1393 } else
1354 return UI_UPDATE; 1394 return MOVE_UI_UPDATE;
1355 } 1395 }
1356 1396
1357 if (IS_CURSOR_MOVE(button)) { 1397 if (IS_CURSOR_MOVE(button)) {
1358 int x = ui->cur_x, y = ui->cur_y, newstate; 1398 int x = ui->cur_x, y = ui->cur_y, newstate;
1359 char buf[80]; 1399 char buf[80], *ret;
1360 move_cursor(button, &ui->cur_x, &ui->cur_y, state->common->w, state->common->h, false); 1400 ret = move_cursor(button, &ui->cur_x, &ui->cur_y,
1361 ui->cur_visible = true; 1401 state->common->w, state->common->h, false,
1362 if (!control && !shift) return UI_UPDATE; 1402 &ui->cur_visible);
1403 if (!control && !shift) return ret;
1363 1404
1364 newstate = control ? shift ? GRID_UNKNOWN : GRID_FULL : GRID_EMPTY; 1405 newstate = control ? shift ? GRID_UNKNOWN : GRID_FULL : GRID_EMPTY;
1365 if (state->grid[y * state->common->w + x] == newstate && 1406 if (state->grid[y * state->common->w + x] == newstate &&
1366 state->grid[ui->cur_y * state->common->w + ui->cur_x] == newstate) 1407 state->grid[ui->cur_y * state->common->w + ui->cur_x] == newstate)
1367 return UI_UPDATE; 1408 return ret;
1368 1409
1369 sprintf(buf, "%c%d,%d,%d,%d", control ? shift ? 'U' : 'F' : 'E', 1410 sprintf(buf, "%c%d,%d,%d,%d", control ? shift ? 'U' : 'F' : 'E',
1370 min(x, ui->cur_x), min(y, ui->cur_y), 1411 min(x, ui->cur_x), min(y, ui->cur_y),
@@ -1379,7 +1420,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1379 1420
1380 if (!ui->cur_visible) { 1421 if (!ui->cur_visible) {
1381 ui->cur_visible = true; 1422 ui->cur_visible = true;
1382 return UI_UPDATE; 1423 return MOVE_UI_UPDATE;
1383 } 1424 }
1384 1425
1385 if (button == CURSOR_SELECT2) 1426 if (button == CURSOR_SELECT2)
@@ -1637,7 +1678,7 @@ static bool check_errors(const game_state *state, int i)
1637 */ 1678 */
1638 1679
1639static void game_compute_size(const game_params *params, int tilesize, 1680static void game_compute_size(const game_params *params, int tilesize,
1640 int *x, int *y) 1681 const game_ui *ui, int *x, int *y)
1641{ 1682{
1642 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1683 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1643 struct { int tilesize; } ads, *ds = &ads; 1684 struct { int tilesize; } ads, *ds = &ads;
@@ -1692,6 +1733,8 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1692 ds->numcolours = snewn(ds->w + ds->h, unsigned char); 1733 ds->numcolours = snewn(ds->w + ds->h, unsigned char);
1693 memset(ds->numcolours, 255, ds->w + ds->h); 1734 memset(ds->numcolours, 255, ds->w + ds->h);
1694 ds->cur_x = ds->cur_y = 0; 1735 ds->cur_x = ds->cur_y = 0;
1736 ds->strbuf = snewn(state->common->rowsize *
1737 MAX_DIGITS(*state->common->rowdata) + 1, char);
1695 1738
1696 return ds; 1739 return ds;
1697} 1740}
@@ -1699,6 +1742,8 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1699static void game_free_drawstate(drawing *dr, game_drawstate *ds) 1742static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1700{ 1743{
1701 sfree(ds->visible); 1744 sfree(ds->visible);
1745 sfree(ds->numcolours);
1746 sfree(ds->strbuf);
1702 sfree(ds); 1747 sfree(ds);
1703} 1748}
1704 1749
@@ -1743,19 +1788,43 @@ static void draw_numbers(
1743 int *rowdata = state->common->rowdata + state->common->rowsize * i; 1788 int *rowdata = state->common->rowdata + state->common->rowsize * i;
1744 int nfit; 1789 int nfit;
1745 int j; 1790 int j;
1791 int rx, ry, rw, rh;
1792 int fontsize;
1746 1793
1747 if (erase) { 1794 if (i < state->common->w) {
1748 if (i < state->common->w) { 1795 rx = TOCOORD(state->common->w, i);
1749 draw_rect(dr, TOCOORD(state->common->w, i), 0, 1796 ry = 0;
1750 TILE_SIZE, BORDER + TLBORDER(state->common->h) * TILE_SIZE, 1797 rw = TILE_SIZE;
1751 COL_BACKGROUND); 1798 rh = BORDER + TLBORDER(state->common->h) * TILE_SIZE;
1752 } else { 1799 } else {
1753 draw_rect(dr, 0, TOCOORD(state->common->h, i - state->common->w), 1800 rx = 0;
1754 BORDER + TLBORDER(state->common->w) * TILE_SIZE, TILE_SIZE, 1801 ry = TOCOORD(state->common->h, i - state->common->w);
1755 COL_BACKGROUND); 1802 rw = BORDER + TLBORDER(state->common->w) * TILE_SIZE;
1756 } 1803 rh = TILE_SIZE;
1757 } 1804 }
1758 1805
1806 clip(dr, rx, ry, rw, rh);
1807 if (erase)
1808 draw_rect(dr, rx, ry, rw, rh, COL_BACKGROUND);
1809
1810 /*
1811 * Choose a font size that's suitable for the lengths of clue.
1812 * Only column clues are interesting because row clues can be
1813 * spaced out independent of the tile size. For column clues, we
1814 * want to go as large as practical while leaving decent space
1815 * between horizintally adjacent clues. We currently distinguish
1816 * two cases: FS_LARGE is when all column clues are single digits,
1817 * and FS_SMALL in all other cases.
1818 *
1819 * If we assume that a digit is about 0.6em wide, and we want
1820 * about that space between clues, then FS_LARGE should be
1821 * TILESIZE/1.2. If we also assume that clues are at most two
1822 * digits long then the case where adjacent clues are two digits
1823 * long requries FS_SMALL to be TILESIZE/1.8.
1824 */
1825 fontsize = (TILE_SIZE + 0.5F) /
1826 (state->common->fontsize == FS_LARGE ? 1.2F : 1.8F);
1827
1759 /* 1828 /*
1760 * Normally I space the numbers out by the same distance as the 1829 * Normally I space the numbers out by the same distance as the
1761 * tile size. However, if there are more numbers than available 1830 * tile size. However, if there are more numbers than available
@@ -1768,32 +1837,38 @@ static void draw_numbers(
1768 nfit = max(rowlen, nfit) - 1; 1837 nfit = max(rowlen, nfit) - 1;
1769 assert(nfit > 0); 1838 assert(nfit > 0);
1770 1839
1771 for (j = 0; j < rowlen; j++) { 1840 if (i < state->common->w) {
1772 int x, y; 1841 for (j = 0; j < rowlen; j++) {
1773 char str[80]; 1842 int x, y;
1843 char str[MAX_DIGITS(*rowdata) + 1];
1774 1844
1775 if (i < state->common->w) { 1845 x = rx;
1776 x = TOCOORD(state->common->w, i);
1777 y = BORDER + TILE_SIZE * (TLBORDER(state->common->h)-1); 1846 y = BORDER + TILE_SIZE * (TLBORDER(state->common->h)-1);
1778 y -= ((rowlen-j-1)*TILE_SIZE) * (TLBORDER(state->common->h)-1) / nfit; 1847 y -= ((rowlen-j-1)*TILE_SIZE) * (TLBORDER(state->common->h)-1) / nfit;
1779 } else { 1848 sprintf(str, "%d", rowdata[j]);
1780 y = TOCOORD(state->common->h, i - state->common->w); 1849 draw_text(dr, x+TILE_SIZE/2, y+TILE_SIZE/2, FONT_VARIABLE,
1781 x = BORDER + TILE_SIZE * (TLBORDER(state->common->w)-1); 1850 fontsize, ALIGN_HCENTRE | ALIGN_VCENTRE, colour, str);
1782 x -= ((rowlen-j-1)*TILE_SIZE) * (TLBORDER(state->common->w)-1) / nfit;
1783 } 1851 }
1784
1785 sprintf(str, "%d", rowdata[j]);
1786 draw_text(dr, x+TILE_SIZE/2, y+TILE_SIZE/2, FONT_VARIABLE,
1787 TILE_SIZE/2, ALIGN_HCENTRE | ALIGN_VCENTRE, colour, str);
1788 }
1789
1790 if (i < state->common->w) {
1791 draw_update(dr, TOCOORD(state->common->w, i), 0,
1792 TILE_SIZE, BORDER + TLBORDER(state->common->h) * TILE_SIZE);
1793 } else { 1852 } else {
1794 draw_update(dr, 0, TOCOORD(state->common->h, i - state->common->w), 1853 int x, y;
1795 BORDER + TLBORDER(state->common->w) * TILE_SIZE, TILE_SIZE); 1854 size_t off = 0;
1855 const char *spaces = " ";
1856
1857 assert(rowlen <= state->common->rowsize);
1858 *ds->strbuf = '\0';
1859 /* Squish up a bit if there are lots of clues. */
1860 if (rowlen > TLBORDER(state->common->w)) spaces++;
1861 for (j = 0; j < rowlen; j++)
1862 off += sprintf(ds->strbuf + off, "%s%d",
1863 j ? spaces : "", rowdata[j]);
1864 y = ry;
1865 x = BORDER + TILE_SIZE * (TLBORDER(state->common->w)-1);
1866 draw_text(dr, x+TILE_SIZE, y+TILE_SIZE/2, FONT_VARIABLE,
1867 fontsize, ALIGN_HRIGHT | ALIGN_VCENTRE, colour, ds->strbuf);
1796 } 1868 }
1869
1870 unclip(dr);
1871 draw_update(dr, rx, ry, rw, rh);
1797} 1872}
1798 1873
1799static void game_redraw(drawing *dr, game_drawstate *ds, 1874static void game_redraw(drawing *dr, game_drawstate *ds,
@@ -1808,14 +1883,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1808 1883
1809 if (!ds->started) { 1884 if (!ds->started) {
1810 /* 1885 /*
1811 * The initial contents of the window are not guaranteed
1812 * and can vary with front ends. To be on the safe side,
1813 * all games should start by drawing a big background-
1814 * colour rectangle covering the whole window.
1815 */
1816 draw_rect(dr, 0, 0, SIZE(ds->w), SIZE(ds->h), COL_BACKGROUND);
1817
1818 /*
1819 * Draw the grid outline. 1886 * Draw the grid outline.
1820 */ 1887 */
1821 draw_rect(dr, TOCOORD(ds->w, 0) - 1, TOCOORD(ds->h, 0) - 1, 1888 draw_rect(dr, TOCOORD(ds->w, 0) - 1, TOCOORD(ds->h, 0) - 1,
@@ -1936,24 +2003,21 @@ static int game_status(const game_state *state)
1936 return state->completed ? +1 : 0; 2003 return state->completed ? +1 : 0;
1937} 2004}
1938 2005
1939static bool game_timing_state(const game_state *state, game_ui *ui) 2006static void game_print_size(const game_params *params, const game_ui *ui,
1940{ 2007 float *x, float *y)
1941 return true;
1942}
1943
1944static void game_print_size(const game_params *params, float *x, float *y)
1945{ 2008{
1946 int pw, ph; 2009 int pw, ph;
1947 2010
1948 /* 2011 /*
1949 * I'll use 5mm squares by default. 2012 * I'll use 5mm squares by default.
1950 */ 2013 */
1951 game_compute_size(params, 500, &pw, &ph); 2014 game_compute_size(params, 500, ui, &pw, &ph);
1952 *x = pw / 100.0F; 2015 *x = pw / 100.0F;
1953 *y = ph / 100.0F; 2016 *y = ph / 100.0F;
1954} 2017}
1955 2018
1956static void game_print(drawing *dr, const game_state *state, int tilesize) 2019static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
2020 int tilesize)
1957{ 2021{
1958 int w = state->common->w, h = state->common->h; 2022 int w = state->common->w, h = state->common->h;
1959 int ink = print_mono_colour(dr, 0); 2023 int ink = print_mono_colour(dr, 0);
@@ -2027,12 +2091,14 @@ const struct game thegame = {
2027 free_game, 2091 free_game,
2028 true, solve_game, 2092 true, solve_game,
2029 true, game_can_format_as_text_now, game_text_format, 2093 true, game_can_format_as_text_now, game_text_format,
2094 NULL, NULL, /* get_prefs, set_prefs */
2030 new_ui, 2095 new_ui,
2031 free_ui, 2096 free_ui,
2032 encode_ui, 2097 NULL, /* encode_ui */
2033 decode_ui, 2098 NULL, /* decode_ui */
2034 NULL, /* game_request_keys */ 2099 NULL, /* game_request_keys */
2035 game_changed_state, 2100 game_changed_state,
2101 current_key_label,
2036 interpret_move, 2102 interpret_move,
2037 execute_move, 2103 execute_move,
2038 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 2104 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -2046,7 +2112,7 @@ const struct game thegame = {
2046 game_status, 2112 game_status,
2047 true, false, game_print_size, game_print, 2113 true, false, game_print_size, game_print,
2048 false, /* wants_statusbar */ 2114 false, /* wants_statusbar */
2049 false, game_timing_state, 2115 false, NULL, /* timing_state */
2050 REQUIRE_RBUTTON, /* flags */ 2116 REQUIRE_RBUTTON, /* flags */
2051}; 2117};
2052 2118