summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/palisade.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/palisade.c')
-rw-r--r--apps/plugins/puzzles/src/palisade.c172
1 files changed, 75 insertions, 97 deletions
diff --git a/apps/plugins/puzzles/src/palisade.c b/apps/plugins/puzzles/src/palisade.c
index e941661a0e..811204b1fe 100644
--- a/apps/plugins/puzzles/src/palisade.c
+++ b/apps/plugins/puzzles/src/palisade.c
@@ -17,6 +17,7 @@
17 17
18#include <assert.h> 18#include <assert.h>
19#include <ctype.h> 19#include <ctype.h>
20#include <limits.h>
20#include <stdarg.h> 21#include <stdarg.h>
21#include <stdio.h> 22#include <stdio.h>
22#include <stdlib.h> 23#include <stdlib.h>
@@ -46,7 +47,7 @@ struct game_params {
46 int w, h, k; 47 int w, h, k;
47}; 48};
48 49
49typedef char clue; 50typedef signed char clue;
50typedef unsigned char borderflag; 51typedef unsigned char borderflag;
51 52
52typedef struct shared_state { 53typedef struct shared_state {
@@ -156,13 +157,15 @@ static game_params *custom_params(const config_item *cfg)
156 157
157static const char *validate_params(const game_params *params, bool full) 158static const char *validate_params(const game_params *params, bool full)
158{ 159{
159 int w = params->w, h = params->h, k = params->k, wh = w * h; 160 int w = params->w, h = params->h, k = params->k, wh;
160 161
161 if (k < 1) return "Region size must be at least one"; 162 if (k < 1) return "Region size must be at least one";
162 if (w < 1) return "Width must be at least one"; 163 if (w < 1) return "Width must be at least one";
163 if (h < 1) return "Height must be at least one"; 164 if (h < 1) return "Height must be at least one";
165 if (w > INT_MAX / h)
166 return "Width times height must not be unreasonably large";
167 wh = w * h;
164 if (wh % k) return "Region size must divide grid area"; 168 if (wh % k) return "Region size must divide grid area";
165
166 if (!full) return NULL; /* succeed partial validation */ 169 if (!full) return NULL; /* succeed partial validation */
167 170
168 /* MAYBE FIXME: we (just?) don't have the UI for winning these. */ 171 /* MAYBE FIXME: we (just?) don't have the UI for winning these. */
@@ -183,7 +186,7 @@ typedef struct solver_ctx {
183 const game_params *params; /* also in shared_state */ 186 const game_params *params; /* also in shared_state */
184 clue *clues; /* also in shared_state */ 187 clue *clues; /* also in shared_state */
185 borderflag *borders; /* also in game_state */ 188 borderflag *borders; /* also in game_state */
186 int *dsf; /* particular to the solver */ 189 DSF *dsf; /* particular to the solver */
187} solver_ctx; 190} solver_ctx;
188 191
189/* Deductions: 192/* Deductions:
@@ -270,7 +273,7 @@ static void connect(solver_ctx *ctx, int i, int j)
270static bool connected(solver_ctx *ctx, int i, int j, int dir) 273static bool connected(solver_ctx *ctx, int i, int j, int dir)
271{ 274{
272 if (j == COMPUTE_J) j = i + dx[dir] + ctx->params->w*dy[dir]; 275 if (j == COMPUTE_J) j = i + dx[dir] + ctx->params->w*dy[dir];
273 return dsf_canonify(ctx->dsf, i) == dsf_canonify(ctx->dsf, j); 276 return dsf_equivalent(ctx->dsf, i, j);
274} 277}
275 278
276static void disconnect(solver_ctx *ctx, int i, int j, int dir) 279static void disconnect(solver_ctx *ctx, int i, int j, int dir)
@@ -502,19 +505,20 @@ static bool solver_equivalent_edges(solver_ctx *ctx)
502 return changed; 505 return changed;
503} 506}
504 507
505#define UNVISITED 6
506
507/* build connected components in `dsf', along the lines of `borders'. */ 508/* build connected components in `dsf', along the lines of `borders'. */
508static void dfs_dsf(int i, int w, borderflag *border, int *dsf, bool black) 509static void build_dsf(int w, int h, borderflag *border, DSF *dsf, bool black)
509{ 510{
510 int dir; 511 int x, y;
511 for (dir = 0; dir < 4; ++dir) { 512
512 int ii = i + dx[dir] + w*dy[dir], bdir = BORDER(dir); 513 for (y = 0; y < h; y++) {
513 if (black ? (border[i] & bdir) : !(border[i] & DISABLED(bdir))) 514 for (x = 0; x < w; x++) {
514 continue; 515 if (x+1 < w && (black ? !(border[y*w+x] & BORDER_R) :
515 if (dsf[ii] != UNVISITED) continue; 516 (border[y*w+x] & DISABLED(BORDER_R))))
516 dsf_merge(dsf, i, ii); 517 dsf_merge(dsf, y*w+x, y*w+(x+1));
517 dfs_dsf(ii, w, border, dsf, black); 518 if (y+1 < h && (black ? !(border[y*w+x] & BORDER_D) :
519 (border[y*w+x] & DISABLED(BORDER_D))))
520 dsf_merge(dsf, y*w+x, (y+1)*w+x);
521 }
518 } 522 }
519} 523}
520 524
@@ -523,9 +527,9 @@ static bool is_solved(const game_params *params, clue *clues,
523{ 527{
524 int w = params->w, h = params->h, wh = w*h, k = params->k; 528 int w = params->w, h = params->h, wh = w*h, k = params->k;
525 int i, x, y; 529 int i, x, y;
526 int *dsf = snew_dsf(wh); 530 DSF *dsf = dsf_new(wh);
527 531
528 assert (dsf[0] == UNVISITED); /* check: UNVISITED and dsf.c match up */ 532 build_dsf(w, h, border, dsf, true);
529 533
530 /* 534 /*
531 * A game is solved if: 535 * A game is solved if:
@@ -536,7 +540,6 @@ static bool is_solved(const game_params *params, clue *clues,
536 * - the borders also satisfy the clue set 540 * - the borders also satisfy the clue set
537 */ 541 */
538 for (i = 0; i < wh; ++i) { 542 for (i = 0; i < wh; ++i) {
539 if (dsf[i] == UNVISITED) dfs_dsf(i, params->w, border, dsf, true);
540 if (dsf_size(dsf, i) != k) goto error; 543 if (dsf_size(dsf, i) != k) goto error;
541 if (clues[i] == EMPTY) continue; 544 if (clues[i] == EMPTY) continue;
542 if (clues[i] != bitcount[border[i] & BORDER_MASK]) goto error; 545 if (clues[i] != bitcount[border[i] & BORDER_MASK]) goto error;
@@ -554,19 +557,19 @@ static bool is_solved(const game_params *params, clue *clues,
554 for (y = 0; y < h; y++) { 557 for (y = 0; y < h; y++) {
555 for (x = 0; x < w; x++) { 558 for (x = 0; x < w; x++) {
556 if (x+1 < w && (border[y*w+x] & BORDER_R) && 559 if (x+1 < w && (border[y*w+x] & BORDER_R) &&
557 dsf_canonify(dsf, y*w+x) == dsf_canonify(dsf, y*w+(x+1))) 560 dsf_equivalent(dsf, y*w+x, y*w+(x+1)))
558 goto error; 561 goto error;
559 if (y+1 < h && (border[y*w+x] & BORDER_D) && 562 if (y+1 < h && (border[y*w+x] & BORDER_D) &&
560 dsf_canonify(dsf, y*w+x) == dsf_canonify(dsf, (y+1)*w+x)) 563 dsf_equivalent(dsf, y*w+x, (y+1)*w+x))
561 goto error; 564 goto error;
562 } 565 }
563 } 566 }
564 567
565 sfree(dsf); 568 dsf_free(dsf);
566 return true; 569 return true;
567 570
568error: 571error:
569 sfree(dsf); 572 dsf_free(dsf);
570 return false; 573 return false;
571} 574}
572 575
@@ -579,7 +582,7 @@ static bool solver(const game_params *params, clue *clues, borderflag *borders)
579 ctx.params = params; 582 ctx.params = params;
580 ctx.clues = clues; 583 ctx.clues = clues;
581 ctx.borders = borders; 584 ctx.borders = borders;
582 ctx.dsf = snew_dsf(wh); 585 ctx.dsf = dsf_new(wh);
583 586
584 solver_connected_clues_versus_region_size(&ctx); /* idempotent */ 587 solver_connected_clues_versus_region_size(&ctx); /* idempotent */
585 do { 588 do {
@@ -591,7 +594,7 @@ static bool solver(const game_params *params, clue *clues, borderflag *borders)
591 changed |= solver_equivalent_edges(&ctx); 594 changed |= solver_equivalent_edges(&ctx);
592 } while (changed); 595 } while (changed);
593 596
594 sfree(ctx.dsf); 597 dsf_free(ctx.dsf);
595 598
596 return is_solved(params, clues, borders); 599 return is_solved(params, clues, borders);
597} 600}
@@ -622,15 +625,14 @@ static char *new_game_desc(const game_params *params, random_state *rs,
622{ 625{
623 int w = params->w, h = params->h, wh = w*h, k = params->k; 626 int w = params->w, h = params->h, wh = w*h, k = params->k;
624 627
625 clue *numbers = snewn(wh + 1, clue), *p; 628 clue *numbers = snewn(wh + 1, clue);
626 borderflag *rim = snewn(wh, borderflag); 629 borderflag *rim = snewn(wh, borderflag);
627 borderflag *scratch_borders = snewn(wh, borderflag); 630 borderflag *scratch_borders = snewn(wh, borderflag);
628 631
629 char *soln = snewa(*aux, wh + 2); 632 char *soln = snewa(*aux, wh + 2);
630 int *shuf = snewn(wh, int); 633 int *shuf = snewn(wh, int);
631 int *dsf = NULL, i, r, c; 634 DSF *dsf = NULL;
632 635 int i, r, c;
633 int attempts = 0;
634 636
635 for (i = 0; i < wh; ++i) shuf[i] = i; 637 for (i = 0; i < wh; ++i) shuf[i] = i;
636 xshuffle(shuf, wh, rs); 638 xshuffle(shuf, wh, rs);
@@ -642,10 +644,9 @@ static char *new_game_desc(const game_params *params, random_state *rs,
642 soln[wh] = '\0'; 644 soln[wh] = '\0';
643 645
644 do { 646 do {
645 ++attempts;
646 setmem(soln, '@', wh); 647 setmem(soln, '@', wh);
647 648
648 sfree(dsf); 649 dsf_free(dsf);
649 dsf = divvy_rectangle(w, h, k, rs); 650 dsf = divvy_rectangle(w, h, k, rs);
650 651
651 for (r = 0; r < h; ++r) 652 for (r = 0; r < h; ++r)
@@ -655,7 +656,7 @@ static char *new_game_desc(const game_params *params, random_state *rs,
655 for (dir = 0; dir < 4; ++dir) { 656 for (dir = 0; dir < 4; ++dir) {
656 int rr = r + dy[dir], cc = c + dx[dir], ii = rr * w + cc; 657 int rr = r + dy[dir], cc = c + dx[dir], ii = rr * w + cc;
657 if (OUT_OF_BOUNDS(cc, rr, w, h) || 658 if (OUT_OF_BOUNDS(cc, rr, w, h) ||
658 dsf_canonify(dsf, i) != dsf_canonify(dsf, ii)) { 659 !dsf_equivalent(dsf, i, ii)) {
659 ++numbers[i]; 660 ++numbers[i];
660 soln[i] |= BORDER(dir); 661 soln[i] |= BORDER(dir);
661 } 662 }
@@ -680,9 +681,10 @@ static char *new_game_desc(const game_params *params, random_state *rs,
680 sfree(scratch_borders); 681 sfree(scratch_borders);
681 sfree(rim); 682 sfree(rim);
682 sfree(shuf); 683 sfree(shuf);
683 sfree(dsf); 684 dsf_free(dsf);
685
686 char *output = snewn(wh + 1, char), *p = output;
684 687
685 p = numbers;
686 r = 0; 688 r = 0;
687 for (i = 0; i < wh; ++i) { 689 for (i = 0; i < wh; ++i) {
688 if (numbers[i] != EMPTY) { 690 if (numbers[i] != EMPTY) {
@@ -699,7 +701,8 @@ static char *new_game_desc(const game_params *params, random_state *rs,
699 } 701 }
700 *p++ = '\0'; 702 *p++ = '\0';
701 703
702 return sresize(numbers, p - numbers, clue); 704 sfree(numbers);
705 return sresize(output, p - output, char);
703} 706}
704 707
705static const char *validate_desc(const game_params *params, const char *desc) 708static const char *validate_desc(const game_params *params, const char *desc)
@@ -867,14 +870,13 @@ static char *game_text_format(const game_state *state)
867struct game_ui { 870struct game_ui {
868 int x, y; 871 int x, y;
869 bool show; 872 bool show;
870 bool fake_ctrl, fake_shift;
871}; 873};
872 874
873static game_ui *new_ui(const game_state *state) 875static game_ui *new_ui(const game_state *state)
874{ 876{
875 game_ui *ui = snew(game_ui); 877 game_ui *ui = snew(game_ui);
876 ui->x = ui->y = 0; 878 ui->x = ui->y = 0;
877 ui->show = ui->fake_ctrl = ui->fake_shift = false; 879 ui->show = getenv_bool("PUZZLES_SHOW_CURSOR", false);
878 return ui; 880 return ui;
879} 881}
880 882
@@ -883,16 +885,6 @@ static void free_ui(game_ui *ui)
883 sfree(ui); 885 sfree(ui);
884} 886}
885 887
886static char *encode_ui(const game_ui *ui)
887{
888 return NULL;
889}
890
891static void decode_ui(game_ui *ui, const char *encoding)
892{
893 assert (encoding == NULL);
894}
895
896static void game_changed_state(game_ui *ui, const game_state *oldstate, 888static void game_changed_state(game_ui *ui, const game_state *oldstate,
897 const game_state *newstate) 889 const game_state *newstate)
898{ 890{
@@ -907,7 +899,7 @@ struct game_drawstate {
907 899
908#define TILESIZE (ds->tilesize) 900#define TILESIZE (ds->tilesize)
909#define MARGIN (ds->tilesize / 2) 901#define MARGIN (ds->tilesize / 2)
910#define WIDTH (1 + (TILESIZE >= 16) + (TILESIZE >= 32) + (TILESIZE >= 64)) 902#define WIDTH (3*TILESIZE/32 > 1 ? 3*TILESIZE/32 : 1)
911#define CENTER ((ds->tilesize / 2) + WIDTH/2) 903#define CENTER ((ds->tilesize / 2) + WIDTH/2)
912 904
913#define FROMCOORD(x) (((x) - MARGIN) / TILESIZE) 905#define FROMCOORD(x) (((x) - MARGIN) / TILESIZE)
@@ -918,12 +910,9 @@ static char *interpret_move(const game_state *state, game_ui *ui,
918 const game_drawstate *ds, int x, int y, int button) 910 const game_drawstate *ds, int x, int y, int button)
919{ 911{
920 int w = state->shared->params.w, h = state->shared->params.h; 912 int w = state->shared->params.w, h = state->shared->params.h;
921 bool control = (button & MOD_CTRL) | ui->fake_ctrl, shift = (button & MOD_SHFT) | ui->fake_shift; 913 bool control = button & MOD_CTRL, shift = button & MOD_SHFT;
922
923 /* reset */
924 ui->fake_ctrl = ui->fake_shift = false;
925 914
926 button &= ~MOD_MASK; 915 button = STRIP_BUTTON_MODIFIERS(button);
927 916
928 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { 917 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
929 int gx = FROMCOORD(x), gy = FROMCOORD(y), possible = BORDER_MASK; 918 int gx = FROMCOORD(x), gy = FROMCOORD(y), possible = BORDER_MASK;
@@ -974,13 +963,13 @@ static char *interpret_move(const game_state *state, game_ui *ui,
974 } 963 }
975 964
976 if (IS_CURSOR_MOVE(button)) { 965 if (IS_CURSOR_MOVE(button)) {
977 ui->show = true;
978 if (control || shift) { 966 if (control || shift) {
979 borderflag flag = 0, newflag; 967 borderflag flag = 0, newflag;
980 int dir, i = ui->y * w + ui->x; 968 int dir, i = ui->y * w + ui->x;
969 ui->show = true;
981 x = ui->x; 970 x = ui->x;
982 y = ui->y; 971 y = ui->y;
983 move_cursor(button, &x, &y, w, h, false); 972 move_cursor(button, &x, &y, w, h, false, NULL);
984 if (OUT_OF_BOUNDS(x, y, w, h)) return NULL; 973 if (OUT_OF_BOUNDS(x, y, w, h)) return NULL;
985 974
986 for (dir = 0; dir < 4; ++dir) 975 for (dir = 0; dir < 4; ++dir)
@@ -999,20 +988,8 @@ static char *interpret_move(const game_state *state, game_ui *ui,
999 if (shift) newflag |= DISABLED(BORDER(FLIP(dir))); 988 if (shift) newflag |= DISABLED(BORDER(FLIP(dir)));
1000 return string(80, "F%d,%d,%dF%d,%d,%d", 989 return string(80, "F%d,%d,%dF%d,%d,%d",
1001 ui->x, ui->y, flag, x, y, newflag); 990 ui->x, ui->y, flag, x, y, newflag);
1002 } else { 991 } else
1003 move_cursor(button, &ui->x, &ui->y, w, h, false); 992 return move_cursor(button, &ui->x, &ui->y, w, h, false, &ui->show);
1004 return UI_UPDATE;
1005 }
1006 }
1007 else if(IS_CURSOR_SELECT(button)) {
1008 /* CURSOR_SELECT or CURSOR_SELECT2 tells us to toggle whether
1009 * the button press should be interpreted as having CTRL or
1010 * shift pressed along with it, respectively. */
1011 ui->show = true;
1012 if(button == CURSOR_SELECT2)
1013 ui->fake_shift = !ui->fake_shift;
1014 else
1015 ui->fake_ctrl = !ui->fake_ctrl;
1016 } 993 }
1017 994
1018 return NULL; 995 return NULL;
@@ -1022,15 +999,14 @@ static game_state *execute_move(const game_state *state, const char *move)
1022{ 999{
1023 int w = state->shared->params.w, h = state->shared->params.h, wh = w * h; 1000 int w = state->shared->params.w, h = state->shared->params.h, wh = w * h;
1024 game_state *ret = dup_game(state); 1001 game_state *ret = dup_game(state);
1025 int nchars, x, y, flag; 1002 int nchars, x, y, flag, i;
1026 1003
1027 if (*move == 'S') { 1004 if (*move == 'S') {
1028 int i;
1029 ++move; 1005 ++move;
1030 for (i = 0; i < wh && move[i]; ++i) 1006 for (i = 0; i < wh && move[i]; ++i)
1031 ret->borders[i] = 1007 ret->borders[i] =
1032 (move[i] & BORDER_MASK) | DISABLED(~move[i] & BORDER_MASK); 1008 (move[i] & BORDER_MASK) | DISABLED(~move[i] & BORDER_MASK);
1033 if (i < wh || move[i]) return NULL; /* leaks `ret', then we die */ 1009 if (i < wh || move[i]) goto badmove;
1034 ret->cheated = ret->completed = true; 1010 ret->cheated = ret->completed = true;
1035 return ret; 1011 return ret;
1036 } 1012 }
@@ -1038,22 +1014,31 @@ static game_state *execute_move(const game_state *state, const char *move)
1038 while (sscanf(move, "F%d,%d,%d%n", &x, &y, &flag, &nchars) == 3 && 1014 while (sscanf(move, "F%d,%d,%d%n", &x, &y, &flag, &nchars) == 3 &&
1039 !OUT_OF_BOUNDS(x, y, w, h)) { 1015 !OUT_OF_BOUNDS(x, y, w, h)) {
1040 move += nchars; 1016 move += nchars;
1017 for (i = 0; i < 4; i++)
1018 if ((flag & BORDER(i)) &&
1019 OUT_OF_BOUNDS(x+dx[i], y+dy[i], w, h))
1020 /* No toggling the borders of the grid! */
1021 goto badmove;
1041 ret->borders[y*w + x] ^= flag; 1022 ret->borders[y*w + x] ^= flag;
1042 } 1023 }
1043 1024
1044 if (*move) return NULL; /* leaks `ret', then we die */ 1025 if (*move) goto badmove;
1045 1026
1046 if (!ret->completed) 1027 if (!ret->completed)
1047 ret->completed = is_solved(&ret->shared->params, ret->shared->clues, 1028 ret->completed = is_solved(&ret->shared->params, ret->shared->clues,
1048 ret->borders); 1029 ret->borders);
1049 1030
1050 return ret; 1031 return ret;
1032
1033 badmove:
1034 free_game(ret);
1035 return NULL;
1051} 1036}
1052 1037
1053/* --- Drawing routines --------------------------------------------- */ 1038/* --- Drawing routines --------------------------------------------- */
1054 1039
1055static void game_compute_size(const game_params *params, int tilesize, 1040static void game_compute_size(const game_params *params, int tilesize,
1056 int *x, int *y) 1041 const game_ui *ui, int *x, int *y)
1057{ 1042{
1058 *x = (params->w + 1) * tilesize; 1043 *x = (params->w + 1) * tilesize;
1059 *y = (params->h + 1) * tilesize; 1044 *y = (params->h + 1) * tilesize;
@@ -1181,14 +1166,13 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1181 float animtime, float flashtime) 1166 float animtime, float flashtime)
1182{ 1167{
1183 int w = state->shared->params.w, h = state->shared->params.h, wh = w*h; 1168 int w = state->shared->params.w, h = state->shared->params.h, wh = w*h;
1184 int r, c, i, flash = ((int) (flashtime * 5 / FLASH_TIME)) % 2; 1169 int r, c, flash = ((int) (flashtime * 5 / FLASH_TIME)) % 2;
1185 int *black_border_dsf = snew_dsf(wh), *yellow_border_dsf = snew_dsf(wh); 1170 DSF *black_border_dsf = dsf_new(wh), *yellow_border_dsf = dsf_new(wh);
1186 int k = state->shared->params.k; 1171 int k = state->shared->params.k;
1187 1172
1188 if (!ds->grid) { 1173 if (!ds->grid) {
1189 char buf[40]; 1174 char buf[40];
1190 int bgw = (w+1) * ds->tilesize, bgh = (h+1) * ds->tilesize; 1175 int bgw = (w+1) * ds->tilesize, bgh = (h+1) * ds->tilesize;
1191 draw_rect(dr, 0, 0, bgw, bgh, COL_BACKGROUND);
1192 1176
1193 for (r = 0; r <= h; ++r) 1177 for (r = 0; r <= h; ++r)
1194 for (c = 0; c <= w; ++c) 1178 for (c = 0; c <= w; ++c)
@@ -1203,12 +1187,8 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1203 status_bar(dr, buf); 1187 status_bar(dr, buf);
1204 } 1188 }
1205 1189
1206 for (i = 0; i < wh; ++i) { 1190 build_dsf(w, h, state->borders, black_border_dsf, true);
1207 if (black_border_dsf[i] == UNVISITED) 1191 build_dsf(w, h, state->borders, yellow_border_dsf, false);
1208 dfs_dsf(i, w, state->borders, black_border_dsf, true);
1209 if (yellow_border_dsf[i] == UNVISITED)
1210 dfs_dsf(i, w, state->borders, yellow_border_dsf, false);
1211 }
1212 1192
1213 for (r = 0; r < h; ++r) 1193 for (r = 0; r < h; ++r)
1214 for (c = 0; c < w; ++c) { 1194 for (c = 0; c < w; ++c) {
@@ -1268,8 +1248,8 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1268 draw_tile(dr, ds, r, c, ds->grid[i], clue); 1248 draw_tile(dr, ds, r, c, ds->grid[i], clue);
1269 } 1249 }
1270 1250
1271 sfree(black_border_dsf); 1251 dsf_free(black_border_dsf);
1272 sfree(yellow_border_dsf); 1252 dsf_free(yellow_border_dsf);
1273} 1253}
1274 1254
1275static float game_anim_length(const game_state *oldstate, 1255static float game_anim_length(const game_state *oldstate,
@@ -1306,17 +1286,12 @@ static int game_status(const game_state *state)
1306 return state->completed ? +1 : 0; 1286 return state->completed ? +1 : 0;
1307} 1287}
1308 1288
1309static bool game_timing_state(const game_state *state, game_ui *ui) 1289static void game_print_size(const game_params *params, const game_ui *ui,
1310{ 1290 float *x, float *y)
1311 assert (!"this shouldn't get called");
1312 return false; /* placate optimiser */
1313}
1314
1315static void game_print_size(const game_params *params, float *x, float *y)
1316{ 1291{
1317 int pw, ph; 1292 int pw, ph;
1318 1293
1319 game_compute_size(params, 700, &pw, &ph); /* 7mm, like loopy */ 1294 game_compute_size(params, 700, ui, &pw, &ph); /* 7mm, like loopy */
1320 1295
1321 *x = pw / 100.0F; 1296 *x = pw / 100.0F;
1322 *y = ph / 100.0F; 1297 *y = ph / 100.0F;
@@ -1335,7 +1310,8 @@ static void print_line(drawing *dr, int x1, int y1, int x2, int y2,
1335 } else draw_line(dr, x1, y1, x2, y2, colour); 1310 } else draw_line(dr, x1, y1, x2, y2, colour);
1336} 1311}
1337 1312
1338static void game_print(drawing *dr, const game_state *state, int tilesize) 1313static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
1314 int tilesize)
1339{ 1315{
1340 int w = state->shared->params.w, h = state->shared->params.h; 1316 int w = state->shared->params.w, h = state->shared->params.h;
1341 int ink = print_mono_colour(dr, 0); 1317 int ink = print_mono_colour(dr, 0);
@@ -1399,12 +1375,14 @@ const struct game thegame = {
1399 free_game, 1375 free_game,
1400 true, solve_game, 1376 true, solve_game,
1401 true, game_can_format_as_text_now, game_text_format, 1377 true, game_can_format_as_text_now, game_text_format,
1378 NULL, NULL, /* get_prefs, set_prefs */
1402 new_ui, 1379 new_ui,
1403 free_ui, 1380 free_ui,
1404 encode_ui, 1381 NULL, /* encode_ui */
1405 decode_ui, 1382 NULL, /* decode_ui */
1406 NULL, /* game_request_keys */ 1383 NULL, /* game_request_keys */
1407 game_changed_state, 1384 game_changed_state,
1385 NULL, /* current_key_label */
1408 interpret_move, 1386 interpret_move,
1409 execute_move, 1387 execute_move,
1410 48, game_compute_size, game_set_size, 1388 48, game_compute_size, game_set_size,
@@ -1418,6 +1396,6 @@ const struct game thegame = {
1418 game_status, 1396 game_status,
1419 true, false, game_print_size, game_print, 1397 true, false, game_print_size, game_print,
1420 true, /* wants_statusbar */ 1398 true, /* wants_statusbar */
1421 false, game_timing_state, 1399 false, NULL, /* timing_state */
1422 0, /* flags */ 1400 0, /* flags */
1423}; 1401};