diff options
Diffstat (limited to 'apps/plugins/puzzles/src/dominosa.c')
-rw-r--r-- | apps/plugins/puzzles/src/dominosa.c | 136 |
1 files changed, 78 insertions, 58 deletions
diff --git a/apps/plugins/puzzles/src/dominosa.c b/apps/plugins/puzzles/src/dominosa.c index 758db4f506..3ac723e6e1 100644 --- a/apps/plugins/puzzles/src/dominosa.c +++ b/apps/plugins/puzzles/src/dominosa.c | |||
@@ -47,7 +47,12 @@ | |||
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 | #include <limits.h> |
51 | #ifdef NO_TGMATH_H | ||
52 | # include <math.h> | ||
53 | #else | ||
54 | # include <tgmath.h> | ||
55 | #endif | ||
51 | 56 | ||
52 | #include "puzzles.h" | 57 | #include "puzzles.h" |
53 | 58 | ||
@@ -243,6 +248,10 @@ static const char *validate_params(const game_params *params, bool full) | |||
243 | { | 248 | { |
244 | if (params->n < 1) | 249 | if (params->n < 1) |
245 | return "Maximum face number must be at least one"; | 250 | return "Maximum face number must be at least one"; |
251 | if (params->n > INT_MAX - 2 || | ||
252 | params->n + 2 > INT_MAX / (params->n + 1)) | ||
253 | return "Maximum face number must not be unreasonably large"; | ||
254 | |||
246 | if (params->diff >= DIFFCOUNT) | 255 | if (params->diff >= DIFFCOUNT) |
247 | return "Unknown difficulty rating"; | 256 | return "Unknown difficulty rating"; |
248 | return NULL; | 257 | return NULL; |
@@ -254,9 +263,9 @@ static const char *validate_params(const game_params *params, bool full) | |||
254 | 263 | ||
255 | #ifdef STANDALONE_SOLVER | 264 | #ifdef STANDALONE_SOLVER |
256 | #define SOLVER_DIAGNOSTICS | 265 | #define SOLVER_DIAGNOSTICS |
257 | bool solver_diagnostics = false; | 266 | static bool solver_diagnostics = false; |
258 | #elif defined SOLVER_DIAGNOSTICS | 267 | #elif defined SOLVER_DIAGNOSTICS |
259 | const bool solver_diagnostics = true; | 268 | static const bool solver_diagnostics = true; |
260 | #endif | 269 | #endif |
261 | 270 | ||
262 | struct solver_domino; | 271 | struct solver_domino; |
@@ -342,6 +351,7 @@ struct solver_scratch { | |||
342 | struct findloopstate *fls; | 351 | struct findloopstate *fls; |
343 | bool squares_by_number_initialised; | 352 | bool squares_by_number_initialised; |
344 | int *wh_scratch, *pc_scratch, *pc_scratch2, *dc_scratch; | 353 | int *wh_scratch, *pc_scratch, *pc_scratch2, *dc_scratch; |
354 | DSF *dsf_scratch; | ||
345 | }; | 355 | }; |
346 | 356 | ||
347 | static struct solver_scratch *solver_make_scratch(int n) | 357 | static struct solver_scratch *solver_make_scratch(int n) |
@@ -473,6 +483,7 @@ static struct solver_scratch *solver_make_scratch(int n) | |||
473 | sc->wh_scratch = NULL; | 483 | sc->wh_scratch = NULL; |
474 | sc->pc_scratch = sc->pc_scratch2 = NULL; | 484 | sc->pc_scratch = sc->pc_scratch2 = NULL; |
475 | sc->dc_scratch = NULL; | 485 | sc->dc_scratch = NULL; |
486 | sc->dsf_scratch = NULL; | ||
476 | 487 | ||
477 | return sc; | 488 | return sc; |
478 | } | 489 | } |
@@ -500,6 +511,7 @@ static void solver_free_scratch(struct solver_scratch *sc) | |||
500 | sfree(sc->pc_scratch); | 511 | sfree(sc->pc_scratch); |
501 | sfree(sc->pc_scratch2); | 512 | sfree(sc->pc_scratch2); |
502 | sfree(sc->dc_scratch); | 513 | sfree(sc->dc_scratch); |
514 | dsf_free(sc->dsf_scratch); | ||
503 | sfree(sc); | 515 | sfree(sc); |
504 | } | 516 | } |
505 | 517 | ||
@@ -925,7 +937,7 @@ struct parity_findloop_ctx { | |||
925 | int i; | 937 | int i; |
926 | }; | 938 | }; |
927 | 939 | ||
928 | int parity_neighbour(int vertex, void *vctx) | 940 | static int parity_neighbour(int vertex, void *vctx) |
929 | { | 941 | { |
930 | struct parity_findloop_ctx *ctx = (struct parity_findloop_ctx *)vctx; | 942 | struct parity_findloop_ctx *ctx = (struct parity_findloop_ctx *)vctx; |
931 | struct solver_placement *p; | 943 | struct solver_placement *p; |
@@ -1416,21 +1428,23 @@ static bool deduce_forcing_chain(struct solver_scratch *sc) | |||
1416 | sc->pc_scratch2 = snewn(sc->pc, int); | 1428 | sc->pc_scratch2 = snewn(sc->pc, int); |
1417 | if (!sc->dc_scratch) | 1429 | if (!sc->dc_scratch) |
1418 | sc->dc_scratch = snewn(sc->dc, int); | 1430 | sc->dc_scratch = snewn(sc->dc, int); |
1431 | if (!sc->dsf_scratch) | ||
1432 | sc->dsf_scratch = dsf_new_flip(sc->pc); | ||
1419 | 1433 | ||
1420 | /* | 1434 | /* |
1421 | * Start by identifying chains of placements which must all occur | 1435 | * Start by identifying chains of placements which must all occur |
1422 | * together if any of them occurs. We do this by making | 1436 | * together if any of them occurs. We do this by making |
1423 | * pc_scratch2 an edsf binding the placements into an equivalence | 1437 | * dsf_scratch a flip dsf binding the placements into an equivalence |
1424 | * class for each entire forcing chain, with the two possible sets | 1438 | * class for each entire forcing chain, with the two possible sets |
1425 | * of dominoes for the chain listed as inverses. | 1439 | * of dominoes for the chain listed as inverses. |
1426 | */ | 1440 | */ |
1427 | dsf_init(sc->pc_scratch2, sc->pc); | 1441 | dsf_reinit(sc->dsf_scratch); |
1428 | for (si = 0; si < sc->wh; si++) { | 1442 | for (si = 0; si < sc->wh; si++) { |
1429 | struct solver_square *sq = &sc->squares[si]; | 1443 | struct solver_square *sq = &sc->squares[si]; |
1430 | if (sq->nplacements == 2) | 1444 | if (sq->nplacements == 2) |
1431 | edsf_merge(sc->pc_scratch2, | 1445 | dsf_merge_flip(sc->dsf_scratch, |
1432 | sq->placements[0]->index, | 1446 | sq->placements[0]->index, |
1433 | sq->placements[1]->index, true); | 1447 | sq->placements[1]->index, true); |
1434 | } | 1448 | } |
1435 | /* | 1449 | /* |
1436 | * Now read out the whole dsf into pc_scratch, flattening its | 1450 | * Now read out the whole dsf into pc_scratch, flattening its |
@@ -1443,7 +1457,7 @@ static bool deduce_forcing_chain(struct solver_scratch *sc) | |||
1443 | */ | 1457 | */ |
1444 | for (pi = 0; pi < sc->pc; pi++) { | 1458 | for (pi = 0; pi < sc->pc; pi++) { |
1445 | bool inv; | 1459 | bool inv; |
1446 | int c = edsf_canonify(sc->pc_scratch2, pi, &inv); | 1460 | int c = dsf_canonify_flip(sc->dsf_scratch, pi, &inv); |
1447 | sc->pc_scratch[pi] = c * 2 + (inv ? 1 : 0); | 1461 | sc->pc_scratch[pi] = c * 2 + (inv ? 1 : 0); |
1448 | } | 1462 | } |
1449 | 1463 | ||
@@ -2708,7 +2722,7 @@ static game_ui *new_ui(const game_state *state) | |||
2708 | { | 2722 | { |
2709 | game_ui *ui = snew(game_ui); | 2723 | game_ui *ui = snew(game_ui); |
2710 | ui->cur_x = ui->cur_y = 0; | 2724 | ui->cur_x = ui->cur_y = 0; |
2711 | ui->cur_visible = false; | 2725 | ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false); |
2712 | ui->highlight_1 = ui->highlight_2 = -1; | 2726 | ui->highlight_1 = ui->highlight_2 = -1; |
2713 | return ui; | 2727 | return ui; |
2714 | } | 2728 | } |
@@ -2718,15 +2732,6 @@ static void free_ui(game_ui *ui) | |||
2718 | sfree(ui); | 2732 | sfree(ui); |
2719 | } | 2733 | } |
2720 | 2734 | ||
2721 | static char *encode_ui(const game_ui *ui) | ||
2722 | { | ||
2723 | return NULL; | ||
2724 | } | ||
2725 | |||
2726 | static void decode_ui(game_ui *ui, const char *encoding) | ||
2727 | { | ||
2728 | } | ||
2729 | |||
2730 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | 2735 | static void game_changed_state(game_ui *ui, const game_state *oldstate, |
2731 | const game_state *newstate) | 2736 | const game_state *newstate) |
2732 | { | 2737 | { |
@@ -2734,6 +2739,33 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, | |||
2734 | ui->cur_visible = false; | 2739 | ui->cur_visible = false; |
2735 | } | 2740 | } |
2736 | 2741 | ||
2742 | static const char *current_key_label(const game_ui *ui, | ||
2743 | const game_state *state, int button) | ||
2744 | { | ||
2745 | if (IS_CURSOR_SELECT(button)) { | ||
2746 | int d1, d2, w = state->w; | ||
2747 | |||
2748 | if (!((ui->cur_x ^ ui->cur_y) & 1)) | ||
2749 | return ""; /* must have exactly one dimension odd */ | ||
2750 | d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2); | ||
2751 | d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2); | ||
2752 | |||
2753 | /* We can't mark an edge next to any domino. */ | ||
2754 | if (button == CURSOR_SELECT2 && | ||
2755 | (state->grid[d1] != d1 || state->grid[d2] != d2)) | ||
2756 | return ""; | ||
2757 | if (button == CURSOR_SELECT) { | ||
2758 | if (state->grid[d1] == d2) return "Remove"; | ||
2759 | return "Place"; | ||
2760 | } else { | ||
2761 | int edge = d2 == d1 + 1 ? EDGE_R : EDGE_B; | ||
2762 | if (state->edges[d1] & edge) return "Remove"; | ||
2763 | return "Line"; | ||
2764 | } | ||
2765 | } | ||
2766 | return ""; | ||
2767 | } | ||
2768 | |||
2737 | #define PREFERRED_TILESIZE 32 | 2769 | #define PREFERRED_TILESIZE 32 |
2738 | #define TILESIZE (ds->tilesize) | 2770 | #define TILESIZE (ds->tilesize) |
2739 | #define BORDER (TILESIZE * 3 / 4) | 2771 | #define BORDER (TILESIZE * 3 / 4) |
@@ -2746,7 +2778,6 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, | |||
2746 | #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 ) | 2778 | #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 ) |
2747 | 2779 | ||
2748 | struct game_drawstate { | 2780 | struct game_drawstate { |
2749 | bool started; | ||
2750 | int w, h, tilesize; | 2781 | int w, h, tilesize; |
2751 | unsigned long *visible; | 2782 | unsigned long *visible; |
2752 | }; | 2783 | }; |
@@ -2768,7 +2799,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
2768 | int d1, d2; | 2799 | int d1, d2; |
2769 | 2800 | ||
2770 | if (tx < 0 || tx >= w || ty < 0 || ty >= h) | 2801 | if (tx < 0 || tx >= w || ty < 0 || ty >= h) |
2771 | return NULL; | 2802 | return MOVE_UNUSED; |
2772 | 2803 | ||
2773 | /* | 2804 | /* |
2774 | * Now we know which square the click was in, decide which | 2805 | * Now we know which square the click was in, decide which |
@@ -2786,29 +2817,26 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
2786 | else if (abs(dy) > abs(dx) && dy > 0 && ty+1 < h) | 2817 | else if (abs(dy) > abs(dx) && dy > 0 && ty+1 < h) |
2787 | d1 = t, d2 = t + w; /* clicked in top half of domino */ | 2818 | d1 = t, d2 = t + w; /* clicked in top half of domino */ |
2788 | else | 2819 | else |
2789 | return NULL; | 2820 | return MOVE_NO_EFFECT; /* clicked precisely on a diagonal */ |
2790 | 2821 | ||
2791 | /* | 2822 | /* |
2792 | * We can't mark an edge next to any domino. | 2823 | * We can't mark an edge next to any domino. |
2793 | */ | 2824 | */ |
2794 | if (button == RIGHT_BUTTON && | 2825 | if (button == RIGHT_BUTTON && |
2795 | (state->grid[d1] != d1 || state->grid[d2] != d2)) | 2826 | (state->grid[d1] != d1 || state->grid[d2] != d2)) |
2796 | return NULL; | 2827 | return MOVE_NO_EFFECT; |
2797 | 2828 | ||
2798 | ui->cur_visible = false; | 2829 | ui->cur_visible = false; |
2799 | sprintf(buf, "%c%d,%d", (int)(button == RIGHT_BUTTON ? 'E' : 'D'), d1, d2); | 2830 | sprintf(buf, "%c%d,%d", (int)(button == RIGHT_BUTTON ? 'E' : 'D'), d1, d2); |
2800 | return dupstr(buf); | 2831 | return dupstr(buf); |
2801 | } else if (IS_CURSOR_MOVE(button)) { | 2832 | } else if (IS_CURSOR_MOVE(button)) { |
2802 | ui->cur_visible = true; | 2833 | return move_cursor(button, &ui->cur_x, &ui->cur_y, 2*w-1, 2*h-1, false, |
2803 | 2834 | &ui->cur_visible); | |
2804 | move_cursor(button, &ui->cur_x, &ui->cur_y, 2*w-1, 2*h-1, false); | ||
2805 | |||
2806 | return UI_UPDATE; | ||
2807 | } else if (IS_CURSOR_SELECT(button)) { | 2835 | } else if (IS_CURSOR_SELECT(button)) { |
2808 | int d1, d2; | 2836 | int d1, d2; |
2809 | 2837 | ||
2810 | if (!((ui->cur_x ^ ui->cur_y) & 1)) | 2838 | if (!((ui->cur_x ^ ui->cur_y) & 1)) |
2811 | return NULL; /* must have exactly one dimension odd */ | 2839 | return MOVE_NO_EFFECT; /* must have exactly one dimension odd */ |
2812 | d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2); | 2840 | d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2); |
2813 | d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2); | 2841 | d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2); |
2814 | 2842 | ||
@@ -2817,14 +2845,14 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
2817 | */ | 2845 | */ |
2818 | if (button == CURSOR_SELECT2 && | 2846 | if (button == CURSOR_SELECT2 && |
2819 | (state->grid[d1] != d1 || state->grid[d2] != d2)) | 2847 | (state->grid[d1] != d1 || state->grid[d2] != d2)) |
2820 | return NULL; | 2848 | return MOVE_NO_EFFECT; |
2821 | 2849 | ||
2822 | sprintf(buf, "%c%d,%d", (int)(button == CURSOR_SELECT2 ? 'E' : 'D'), d1, d2); | 2850 | sprintf(buf, "%c%d,%d", (int)(button == CURSOR_SELECT2 ? 'E' : 'D'), d1, d2); |
2823 | return dupstr(buf); | 2851 | return dupstr(buf); |
2824 | } else if (isdigit(button)) { | 2852 | } else if (isdigit(button)) { |
2825 | int n = state->params.n, num = button - '0'; | 2853 | int n = state->params.n, num = button - '0'; |
2826 | if (num > n) { | 2854 | if (num > n) { |
2827 | return NULL; | 2855 | return MOVE_UNUSED; |
2828 | } else if (ui->highlight_1 == num) { | 2856 | } else if (ui->highlight_1 == num) { |
2829 | ui->highlight_1 = -1; | 2857 | ui->highlight_1 = -1; |
2830 | } else if (ui->highlight_2 == num) { | 2858 | } else if (ui->highlight_2 == num) { |
@@ -2834,12 +2862,12 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
2834 | } else if (ui->highlight_2 == -1) { | 2862 | } else if (ui->highlight_2 == -1) { |
2835 | ui->highlight_2 = num; | 2863 | ui->highlight_2 = num; |
2836 | } else { | 2864 | } else { |
2837 | return NULL; | 2865 | return MOVE_NO_EFFECT; |
2838 | } | 2866 | } |
2839 | return UI_UPDATE; | 2867 | return MOVE_UI_UPDATE; |
2840 | } | 2868 | } |
2841 | 2869 | ||
2842 | return NULL; | 2870 | return MOVE_UNUSED; |
2843 | } | 2871 | } |
2844 | 2872 | ||
2845 | static game_state *execute_move(const game_state *state, const char *move) | 2873 | static game_state *execute_move(const game_state *state, const char *move) |
@@ -2865,7 +2893,8 @@ static game_state *execute_move(const game_state *state, const char *move) | |||
2865 | move++; | 2893 | move++; |
2866 | } else if (move[0] == 'D' && | 2894 | } else if (move[0] == 'D' && |
2867 | sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 && | 2895 | sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 && |
2868 | d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2) { | 2896 | d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 && |
2897 | (d2 - d1 == 1 || d2 - d1 == w)) { | ||
2869 | 2898 | ||
2870 | /* | 2899 | /* |
2871 | * Toggle domino presence between d1 and d2. | 2900 | * Toggle domino presence between d1 and d2. |
@@ -2933,7 +2962,8 @@ static game_state *execute_move(const game_state *state, const char *move) | |||
2933 | } else if (move[0] == 'E' && | 2962 | } else if (move[0] == 'E' && |
2934 | sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 && | 2963 | sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 && |
2935 | d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 && | 2964 | d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 && |
2936 | ret->grid[d1] == d1 && ret->grid[d2] == d2) { | 2965 | ret->grid[d1] == d1 && ret->grid[d2] == d2 && |
2966 | (d2 - d1 == 1 || d2 - d1 == w)) { | ||
2937 | 2967 | ||
2938 | /* | 2968 | /* |
2939 | * Toggle edge presence between d1 and d2. | 2969 | * Toggle edge presence between d1 and d2. |
@@ -2998,7 +3028,7 @@ static game_state *execute_move(const game_state *state, const char *move) | |||
2998 | */ | 3028 | */ |
2999 | 3029 | ||
3000 | static void game_compute_size(const game_params *params, int tilesize, | 3030 | static void game_compute_size(const game_params *params, int tilesize, |
3001 | int *x, int *y) | 3031 | const game_ui *ui, int *x, int *y) |
3002 | { | 3032 | { |
3003 | int n = params->n, w = n+2, h = n+1; | 3033 | int n = params->n, w = n+2, h = n+1; |
3004 | 3034 | ||
@@ -3059,7 +3089,6 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) | |||
3059 | struct game_drawstate *ds = snew(struct game_drawstate); | 3089 | struct game_drawstate *ds = snew(struct game_drawstate); |
3060 | int i; | 3090 | int i; |
3061 | 3091 | ||
3062 | ds->started = false; | ||
3063 | ds->w = state->w; | 3092 | ds->w = state->w; |
3064 | ds->h = state->h; | 3093 | ds->h = state->h; |
3065 | ds->visible = snewn(ds->w * ds->h, unsigned long); | 3094 | ds->visible = snewn(ds->w * ds->h, unsigned long); |
@@ -3225,14 +3254,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
3225 | int x, y, i; | 3254 | int x, y, i; |
3226 | unsigned char *used; | 3255 | unsigned char *used; |
3227 | 3256 | ||
3228 | if (!ds->started) { | ||
3229 | int pw, ph; | ||
3230 | game_compute_size(&state->params, TILESIZE, &pw, &ph); | ||
3231 | draw_rect(dr, 0, 0, pw, ph, COL_BACKGROUND); | ||
3232 | draw_update(dr, 0, 0, pw, ph); | ||
3233 | ds->started = true; | ||
3234 | } | ||
3235 | |||
3236 | /* | 3257 | /* |
3237 | * See how many dominoes of each type there are, so we can | 3258 | * See how many dominoes of each type there are, so we can |
3238 | * highlight clashes in red. | 3259 | * highlight clashes in red. |
@@ -3347,24 +3368,21 @@ static int game_status(const game_state *state) | |||
3347 | return state->completed ? +1 : 0; | 3368 | return state->completed ? +1 : 0; |
3348 | } | 3369 | } |
3349 | 3370 | ||
3350 | static bool game_timing_state(const game_state *state, game_ui *ui) | 3371 | static void game_print_size(const game_params *params, const game_ui *ui, |
3351 | { | 3372 | float *x, float *y) |
3352 | return true; | ||
3353 | } | ||
3354 | |||
3355 | static void game_print_size(const game_params *params, float *x, float *y) | ||
3356 | { | 3373 | { |
3357 | int pw, ph; | 3374 | int pw, ph; |
3358 | 3375 | ||
3359 | /* | 3376 | /* |
3360 | * I'll use 6mm squares by default. | 3377 | * I'll use 6mm squares by default. |
3361 | */ | 3378 | */ |
3362 | game_compute_size(params, 600, &pw, &ph); | 3379 | game_compute_size(params, 600, ui, &pw, &ph); |
3363 | *x = pw / 100.0F; | 3380 | *x = pw / 100.0F; |
3364 | *y = ph / 100.0F; | 3381 | *y = ph / 100.0F; |
3365 | } | 3382 | } |
3366 | 3383 | ||
3367 | static void game_print(drawing *dr, const game_state *state, int tilesize) | 3384 | static void game_print(drawing *dr, const game_state *state, const game_ui *ui, |
3385 | int tilesize) | ||
3368 | { | 3386 | { |
3369 | int w = state->w, h = state->h; | 3387 | int w = state->w, h = state->h; |
3370 | int c, x, y; | 3388 | int c, x, y; |
@@ -3421,12 +3439,14 @@ const struct game thegame = { | |||
3421 | free_game, | 3439 | free_game, |
3422 | true, solve_game, | 3440 | true, solve_game, |
3423 | true, game_can_format_as_text_now, game_text_format, | 3441 | true, game_can_format_as_text_now, game_text_format, |
3442 | NULL, NULL, /* get_prefs, set_prefs */ | ||
3424 | new_ui, | 3443 | new_ui, |
3425 | free_ui, | 3444 | free_ui, |
3426 | encode_ui, | 3445 | NULL, /* encode_ui */ |
3427 | decode_ui, | 3446 | NULL, /* decode_ui */ |
3428 | NULL, /* game_request_keys */ | 3447 | NULL, /* game_request_keys */ |
3429 | game_changed_state, | 3448 | game_changed_state, |
3449 | current_key_label, | ||
3430 | interpret_move, | 3450 | interpret_move, |
3431 | execute_move, | 3451 | execute_move, |
3432 | PREFERRED_TILESIZE, game_compute_size, game_set_size, | 3452 | PREFERRED_TILESIZE, game_compute_size, game_set_size, |
@@ -3440,7 +3460,7 @@ const struct game thegame = { | |||
3440 | game_status, | 3460 | game_status, |
3441 | true, false, game_print_size, game_print, | 3461 | true, false, game_print_size, game_print, |
3442 | false, /* wants_statusbar */ | 3462 | false, /* wants_statusbar */ |
3443 | false, game_timing_state, | 3463 | false, NULL, /* timing_state */ |
3444 | 0, /* flags */ | 3464 | 0, /* flags */ |
3445 | }; | 3465 | }; |
3446 | 3466 | ||