From 09aa8de52cb962f1ceebfb1fd44f2c54a924fc5c Mon Sep 17 00:00:00 2001 From: Franklin Wei Date: Mon, 22 Jul 2024 21:43:25 -0400 Subject: puzzles: resync with upstream This brings the puzzles source in sync with Simon's branch, commit fd304c5 (from March 2024), with some added Rockbox-specific compatibility changes: https://www.franklinwei.com/git/puzzles/commit/?h=rockbox-devel&id=516830d9d76bdfe64fe5ccf2a9b59c33f5c7c078 There are quite a lot of backend changes, including a new "Mosaic" puzzle. In addition, some new frontend changes were necessary: - New "Preferences" menu to access the user preferences system. - Enabled spacebar input for several games. Change-Id: I94c7df674089c92f32d5f07025f6a1059068af1e --- apps/plugins/puzzles/src/dominosa.c | 136 +++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 58 deletions(-) (limited to 'apps/plugins/puzzles/src/dominosa.c') 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 @@ #include #include #include -#include +#include +#ifdef NO_TGMATH_H +# include +#else +# include +#endif #include "puzzles.h" @@ -243,6 +248,10 @@ static const char *validate_params(const game_params *params, bool full) { if (params->n < 1) return "Maximum face number must be at least one"; + if (params->n > INT_MAX - 2 || + params->n + 2 > INT_MAX / (params->n + 1)) + return "Maximum face number must not be unreasonably large"; + if (params->diff >= DIFFCOUNT) return "Unknown difficulty rating"; return NULL; @@ -254,9 +263,9 @@ static const char *validate_params(const game_params *params, bool full) #ifdef STANDALONE_SOLVER #define SOLVER_DIAGNOSTICS -bool solver_diagnostics = false; +static bool solver_diagnostics = false; #elif defined SOLVER_DIAGNOSTICS -const bool solver_diagnostics = true; +static const bool solver_diagnostics = true; #endif struct solver_domino; @@ -342,6 +351,7 @@ struct solver_scratch { struct findloopstate *fls; bool squares_by_number_initialised; int *wh_scratch, *pc_scratch, *pc_scratch2, *dc_scratch; + DSF *dsf_scratch; }; static struct solver_scratch *solver_make_scratch(int n) @@ -473,6 +483,7 @@ static struct solver_scratch *solver_make_scratch(int n) sc->wh_scratch = NULL; sc->pc_scratch = sc->pc_scratch2 = NULL; sc->dc_scratch = NULL; + sc->dsf_scratch = NULL; return sc; } @@ -500,6 +511,7 @@ static void solver_free_scratch(struct solver_scratch *sc) sfree(sc->pc_scratch); sfree(sc->pc_scratch2); sfree(sc->dc_scratch); + dsf_free(sc->dsf_scratch); sfree(sc); } @@ -925,7 +937,7 @@ struct parity_findloop_ctx { int i; }; -int parity_neighbour(int vertex, void *vctx) +static int parity_neighbour(int vertex, void *vctx) { struct parity_findloop_ctx *ctx = (struct parity_findloop_ctx *)vctx; struct solver_placement *p; @@ -1416,21 +1428,23 @@ static bool deduce_forcing_chain(struct solver_scratch *sc) sc->pc_scratch2 = snewn(sc->pc, int); if (!sc->dc_scratch) sc->dc_scratch = snewn(sc->dc, int); + if (!sc->dsf_scratch) + sc->dsf_scratch = dsf_new_flip(sc->pc); /* * Start by identifying chains of placements which must all occur * together if any of them occurs. We do this by making - * pc_scratch2 an edsf binding the placements into an equivalence + * dsf_scratch a flip dsf binding the placements into an equivalence * class for each entire forcing chain, with the two possible sets * of dominoes for the chain listed as inverses. */ - dsf_init(sc->pc_scratch2, sc->pc); + dsf_reinit(sc->dsf_scratch); for (si = 0; si < sc->wh; si++) { struct solver_square *sq = &sc->squares[si]; if (sq->nplacements == 2) - edsf_merge(sc->pc_scratch2, - sq->placements[0]->index, - sq->placements[1]->index, true); + dsf_merge_flip(sc->dsf_scratch, + sq->placements[0]->index, + sq->placements[1]->index, true); } /* * Now read out the whole dsf into pc_scratch, flattening its @@ -1443,7 +1457,7 @@ static bool deduce_forcing_chain(struct solver_scratch *sc) */ for (pi = 0; pi < sc->pc; pi++) { bool inv; - int c = edsf_canonify(sc->pc_scratch2, pi, &inv); + int c = dsf_canonify_flip(sc->dsf_scratch, pi, &inv); sc->pc_scratch[pi] = c * 2 + (inv ? 1 : 0); } @@ -2708,7 +2722,7 @@ static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->cur_x = ui->cur_y = 0; - ui->cur_visible = false; + ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false); ui->highlight_1 = ui->highlight_2 = -1; return ui; } @@ -2718,15 +2732,6 @@ static void free_ui(game_ui *ui) sfree(ui); } -static char *encode_ui(const game_ui *ui) -{ - return NULL; -} - -static void decode_ui(game_ui *ui, const char *encoding) -{ -} - static void game_changed_state(game_ui *ui, const game_state *oldstate, const game_state *newstate) { @@ -2734,6 +2739,33 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->cur_visible = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button)) { + int d1, d2, w = state->w; + + if (!((ui->cur_x ^ ui->cur_y) & 1)) + return ""; /* must have exactly one dimension odd */ + d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2); + d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2); + + /* We can't mark an edge next to any domino. */ + if (button == CURSOR_SELECT2 && + (state->grid[d1] != d1 || state->grid[d2] != d2)) + return ""; + if (button == CURSOR_SELECT) { + if (state->grid[d1] == d2) return "Remove"; + return "Place"; + } else { + int edge = d2 == d1 + 1 ? EDGE_R : EDGE_B; + if (state->edges[d1] & edge) return "Remove"; + return "Line"; + } + } + return ""; +} + #define PREFERRED_TILESIZE 32 #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE * 3 / 4) @@ -2746,7 +2778,6 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 ) struct game_drawstate { - bool started; int w, h, tilesize; unsigned long *visible; }; @@ -2768,7 +2799,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, int d1, d2; if (tx < 0 || tx >= w || ty < 0 || ty >= h) - return NULL; + return MOVE_UNUSED; /* * 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, else if (abs(dy) > abs(dx) && dy > 0 && ty+1 < h) d1 = t, d2 = t + w; /* clicked in top half of domino */ else - return NULL; + return MOVE_NO_EFFECT; /* clicked precisely on a diagonal */ /* * We can't mark an edge next to any domino. */ if (button == RIGHT_BUTTON && (state->grid[d1] != d1 || state->grid[d2] != d2)) - return NULL; + return MOVE_NO_EFFECT; ui->cur_visible = false; sprintf(buf, "%c%d,%d", (int)(button == RIGHT_BUTTON ? 'E' : 'D'), d1, d2); return dupstr(buf); } else if (IS_CURSOR_MOVE(button)) { - ui->cur_visible = true; - - move_cursor(button, &ui->cur_x, &ui->cur_y, 2*w-1, 2*h-1, false); - - return UI_UPDATE; + return move_cursor(button, &ui->cur_x, &ui->cur_y, 2*w-1, 2*h-1, false, + &ui->cur_visible); } else if (IS_CURSOR_SELECT(button)) { int d1, d2; if (!((ui->cur_x ^ ui->cur_y) & 1)) - return NULL; /* must have exactly one dimension odd */ + return MOVE_NO_EFFECT; /* must have exactly one dimension odd */ d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2); d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2); @@ -2817,14 +2845,14 @@ static char *interpret_move(const game_state *state, game_ui *ui, */ if (button == CURSOR_SELECT2 && (state->grid[d1] != d1 || state->grid[d2] != d2)) - return NULL; + return MOVE_NO_EFFECT; sprintf(buf, "%c%d,%d", (int)(button == CURSOR_SELECT2 ? 'E' : 'D'), d1, d2); return dupstr(buf); } else if (isdigit(button)) { int n = state->params.n, num = button - '0'; if (num > n) { - return NULL; + return MOVE_UNUSED; } else if (ui->highlight_1 == num) { ui->highlight_1 = -1; } else if (ui->highlight_2 == num) { @@ -2834,12 +2862,12 @@ static char *interpret_move(const game_state *state, game_ui *ui, } else if (ui->highlight_2 == -1) { ui->highlight_2 = num; } else { - return NULL; + return MOVE_NO_EFFECT; } - return UI_UPDATE; + return MOVE_UI_UPDATE; } - return NULL; + return MOVE_UNUSED; } 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) move++; } else if (move[0] == 'D' && sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 && - d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2) { + d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 && + (d2 - d1 == 1 || d2 - d1 == w)) { /* * Toggle domino presence between d1 and d2. @@ -2933,7 +2962,8 @@ static game_state *execute_move(const game_state *state, const char *move) } else if (move[0] == 'E' && sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 && d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 && - ret->grid[d1] == d1 && ret->grid[d2] == d2) { + ret->grid[d1] == d1 && ret->grid[d2] == d2 && + (d2 - d1 == 1 || d2 - d1 == w)) { /* * Toggle edge presence between d1 and d2. @@ -2998,7 +3028,7 @@ static game_state *execute_move(const game_state *state, const char *move) */ static void game_compute_size(const game_params *params, int tilesize, - int *x, int *y) + const game_ui *ui, int *x, int *y) { int n = params->n, w = n+2, h = n+1; @@ -3059,7 +3089,6 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) struct game_drawstate *ds = snew(struct game_drawstate); int i; - ds->started = false; ds->w = state->w; ds->h = state->h; ds->visible = snewn(ds->w * ds->h, unsigned long); @@ -3225,14 +3254,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds, int x, y, i; unsigned char *used; - if (!ds->started) { - int pw, ph; - game_compute_size(&state->params, TILESIZE, &pw, &ph); - draw_rect(dr, 0, 0, pw, ph, COL_BACKGROUND); - draw_update(dr, 0, 0, pw, ph); - ds->started = true; - } - /* * See how many dominoes of each type there are, so we can * highlight clashes in red. @@ -3347,24 +3368,21 @@ static int game_status(const game_state *state) return state->completed ? +1 : 0; } -static bool game_timing_state(const game_state *state, game_ui *ui) -{ - return true; -} - -static void game_print_size(const game_params *params, float *x, float *y) +static void game_print_size(const game_params *params, const game_ui *ui, + float *x, float *y) { int pw, ph; /* * I'll use 6mm squares by default. */ - game_compute_size(params, 600, &pw, &ph); + game_compute_size(params, 600, ui, &pw, &ph); *x = pw / 100.0F; *y = ph / 100.0F; } -static void game_print(drawing *dr, const game_state *state, int tilesize) +static void game_print(drawing *dr, const game_state *state, const game_ui *ui, + int tilesize) { int w = state->w, h = state->h; int c, x, y; @@ -3421,12 +3439,14 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, - encode_ui, - decode_ui, + NULL, /* encode_ui */ + NULL, /* decode_ui */ NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, @@ -3440,7 +3460,7 @@ const struct game thegame = { game_status, true, false, game_print_size, game_print, false, /* wants_statusbar */ - false, game_timing_state, + false, NULL, /* timing_state */ 0, /* flags */ }; -- cgit v1.2.3