summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/dominosa.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/dominosa.c')
-rw-r--r--apps/plugins/puzzles/src/dominosa.c136
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
257bool solver_diagnostics = false; 266static bool solver_diagnostics = false;
258#elif defined SOLVER_DIAGNOSTICS 267#elif defined SOLVER_DIAGNOSTICS
259const bool solver_diagnostics = true; 268static const bool solver_diagnostics = true;
260#endif 269#endif
261 270
262struct solver_domino; 271struct 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
347static struct solver_scratch *solver_make_scratch(int n) 357static 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
928int parity_neighbour(int vertex, void *vctx) 940static 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
2721static char *encode_ui(const game_ui *ui)
2722{
2723 return NULL;
2724}
2725
2726static void decode_ui(game_ui *ui, const char *encoding)
2727{
2728}
2729
2730static void game_changed_state(game_ui *ui, const game_state *oldstate, 2735static 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
2742static 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
2748struct game_drawstate { 2780struct 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
2845static game_state *execute_move(const game_state *state, const char *move) 2873static 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
3000static void game_compute_size(const game_params *params, int tilesize, 3030static 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
3350static bool game_timing_state(const game_state *state, game_ui *ui) 3371static void game_print_size(const game_params *params, const game_ui *ui,
3351{ 3372 float *x, float *y)
3352 return true;
3353}
3354
3355static 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
3367static void game_print(drawing *dr, const game_state *state, int tilesize) 3384static 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