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/signpost.c | 185 ++++++++++++++++++++++-------------- 1 file changed, 115 insertions(+), 70 deletions(-) (limited to 'apps/plugins/puzzles/src/signpost.c') diff --git a/apps/plugins/puzzles/src/signpost.c b/apps/plugins/puzzles/src/signpost.c index 9aee255ebe..9aed67bb4a 100644 --- a/apps/plugins/puzzles/src/signpost.c +++ b/apps/plugins/puzzles/src/signpost.c @@ -7,7 +7,12 @@ #include #include #include -#include +#include +#ifdef NO_TGMATH_H +# include +#else +# include +#endif #include "puzzles.h" @@ -57,7 +62,7 @@ struct game_state { int *nums; /* numbers, size n */ unsigned int *flags; /* flags, size n */ int *next, *prev; /* links to other cell indexes, size n (-1 absent) */ - int *dsf; /* connects regions with a dsf. */ + DSF *dsf; /* connects regions with a dsf. */ int *numsi; /* for each number, which index is it in? (-1 absent) */ }; @@ -168,7 +173,7 @@ static bool isvalidmove(const game_state *state, bool clever, /* can't create a new connection between cells in the same region * as that would create a loop. */ - if (dsf_canonify(state->dsf, from) == dsf_canonify(state->dsf, to)) + if (dsf_equivalent(state->dsf, from, to)) return false; /* if both cells are actual numbers, can't drag if we're not @@ -277,7 +282,7 @@ static void strip_nums(game_state *state) { memset(state->next, -1, state->n*sizeof(int)); memset(state->prev, -1, state->n*sizeof(int)); memset(state->numsi, -1, (state->n+1)*sizeof(int)); - dsf_init(state->dsf, state->n); + dsf_reinit(state->dsf); } static bool check_nums(game_state *orig, game_state *copy, bool only_immutable) @@ -421,6 +426,8 @@ static const char *validate_params(const game_params *params, bool full) { if (params->w < 1) return "Width must be at least one"; if (params->h < 1) return "Height must be at least one"; + if (params->w > INT_MAX / params->h) + return "Width times height must not be unreasonably large"; if (full && params->w == 1 && params->h == 1) /* The UI doesn't let us move these from unsolved to solved, * so we disallow generating (but not playing) them. */ @@ -454,7 +461,7 @@ static game_state *blank_game(int w, int h) state->flags = snewn(state->n, unsigned int); state->next = snewn(state->n, int); state->prev = snewn(state->n, int); - state->dsf = snew_dsf(state->n); + state->dsf = dsf_new(state->n); state->numsi = snewn(state->n+1, int); blank_game_into(state); @@ -475,7 +482,7 @@ static void dup_game_to(game_state *to, const game_state *from) memcpy(to->next, from->next, to->n*sizeof(int)); memcpy(to->prev, from->prev, to->n*sizeof(int)); - memcpy(to->dsf, from->dsf, to->n*sizeof(int)); + dsf_copy(to->dsf, from->dsf); memcpy(to->numsi, from->numsi, (to->n+1)*sizeof(int)); } @@ -493,7 +500,7 @@ static void free_game(game_state *state) sfree(state->flags); sfree(state->next); sfree(state->prev); - sfree(state->dsf); + dsf_free(state->dsf); sfree(state->numsi); sfree(state); } @@ -1011,7 +1018,7 @@ static void connect_numbers(game_state *state) { int i, di, dni; - dsf_init(state->dsf, state->n); + dsf_reinit(state->dsf); for (i = 0; i < state->n; i++) { if (state->next[i] != -1) { assert(state->prev[state->next[i]] == i); @@ -1028,8 +1035,8 @@ static void connect_numbers(game_state *state) static int compare_heads(const void *a, const void *b) { - struct head_meta *ha = (struct head_meta *)a; - struct head_meta *hb = (struct head_meta *)b; + const struct head_meta *ha = (const struct head_meta *)a; + const struct head_meta *hb = (const struct head_meta *)b; /* Heads with preferred colours first... */ if (ha->preference && !hb->preference) return -1; @@ -1380,8 +1387,32 @@ struct game_ui { bool dragging, drag_is_from; int sx, sy; /* grid coords of start cell */ int dx, dy; /* pixel coords of drag posn */ + + /* + * Trivial and foolish configurable option done on purest whim. + * With this option enabled, the victory flash is done by rotating + * each square in the opposite direction from its immediate + * neighbours, so that they behave like a field of interlocking + * gears. With it disabled, they all rotate in the same direction. + * Choose for yourself which is more brain-twisting :-) + */ + bool gear_mode; }; +static void legacy_prefs_override(struct game_ui *ui_out) +{ + static bool initialised = false; + static int gear_mode = -1; + + if (!initialised) { + initialised = true; + gear_mode = getenv_bool("SIGNPOST_GEARS", -1); + } + + if (gear_mode != -1) + ui_out->gear_mode = gear_mode; +} + static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); @@ -1390,11 +1421,14 @@ static game_ui *new_ui(const game_state *state) * copy to clone, there's code that needs fixing in game_redraw too. */ ui->cx = ui->cy = 0; - ui->cshow = false; + ui->cshow = getenv_bool("PUZZLES_SHOW_CURSOR", false); ui->dragging = false; ui->sx = ui->sy = ui->dx = ui->dy = 0; + ui->gear_mode = false; + legacy_prefs_override(ui); + return ui; } @@ -1403,13 +1437,28 @@ static void free_ui(game_ui *ui) sfree(ui); } -static char *encode_ui(const game_ui *ui) +static config_item *get_prefs(game_ui *ui) { - return NULL; + config_item *ret; + + ret = snewn(2, config_item); + + ret[0].name = "Victory rotation effect"; + ret[0].kw = "flash-type"; + ret[0].type = C_CHOICES; + ret[0].u.choices.choicenames = ":Unidirectional:Meshing gears"; + ret[0].u.choices.choicekws = ":unidirectional:gears"; + ret[0].u.choices.selected = ui->gear_mode; + + ret[1].name = NULL; + ret[1].type = C_END; + + return ret; } -static void decode_ui(game_ui *ui, const char *encoding) +static void set_prefs(game_ui *ui, const config_item *cfg) { + ui->gear_mode = cfg[0].u.choices.selected; } static void game_changed_state(game_ui *ui, const game_state *oldstate, @@ -1421,6 +1470,26 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, } } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cshow) { + if (ui->dragging) { + if (ui->drag_is_from) { + if (isvalidmove(state, false, ui->sx, ui->sy, ui->cx, ui->cy)) + return "To here"; + } else { + if (isvalidmove(state, false, ui->cx, ui->cy, ui->sx, ui->sy)) + return "From here"; + } + return "Cancel"; + } else { + return button == CURSOR_SELECT ? "From here" : "To here"; + } + } + return ""; +} + struct game_drawstate { int tilesize; bool started, solved; @@ -1442,26 +1511,27 @@ static char *interpret_move(const game_state *state, game_ui *ui, char buf[80]; if (IS_CURSOR_MOVE(button)) { - move_cursor(button, &ui->cx, &ui->cy, state->w, state->h, false); - ui->cshow = true; + char *ret; + ret = move_cursor(button, &ui->cx, &ui->cy, state->w, state->h, false, + &ui->cshow); if (ui->dragging) { ui->dx = COORD(ui->cx) + TILE_SIZE/2; ui->dy = COORD(ui->cy) + TILE_SIZE/2; } - return UI_UPDATE; + return ret; } else if (IS_CURSOR_SELECT(button)) { if (!ui->cshow) ui->cshow = true; else if (ui->dragging) { ui->dragging = false; - if (ui->sx == ui->cx && ui->sy == ui->cy) return UI_UPDATE; + if (ui->sx == ui->cx && ui->sy == ui->cy) return MOVE_UI_UPDATE; if (ui->drag_is_from) { if (!isvalidmove(state, false, ui->sx, ui->sy, ui->cx, ui->cy)) - return UI_UPDATE; + return MOVE_UI_UPDATE; sprintf(buf, "L%d,%d-%d,%d", ui->sx, ui->sy, ui->cx, ui->cy); } else { if (!isvalidmove(state, false, ui->cx, ui->cy, ui->sx, ui->sy)) - return UI_UPDATE; + return MOVE_UI_UPDATE; sprintf(buf, "L%d,%d-%d,%d", ui->cx, ui->cy, ui->sx, ui->sy); } return dupstr(buf); @@ -1473,7 +1543,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, ui->dy = COORD(ui->cy) + TILE_SIZE/2; ui->drag_is_from = (button == CURSOR_SELECT); } - return UI_UPDATE; + return MOVE_UI_UPDATE; } if (IS_MOUSE_DOWN(button)) { if (ui->cshow) { @@ -1502,19 +1572,19 @@ static char *interpret_move(const game_state *state, game_ui *ui, ui->dx = mx; ui->dy = my; ui->cshow = false; - return UI_UPDATE; + return MOVE_UI_UPDATE; } else if (IS_MOUSE_DRAG(button) && ui->dragging) { ui->dx = mx; ui->dy = my; - return UI_UPDATE; + return MOVE_UI_UPDATE; } else if (IS_MOUSE_RELEASE(button) && ui->dragging) { ui->dragging = false; - if (ui->sx == x && ui->sy == y) return UI_UPDATE; /* single click */ + if (ui->sx == x && ui->sy == y) return MOVE_UI_UPDATE; /* single click */ if (!INGRID(state, x, y)) { int si = ui->sy*w+ui->sx; if (state->prev[si] == -1 && state->next[si] == -1) - return UI_UPDATE; + return MOVE_UI_UPDATE; sprintf(buf, "%c%d,%d", (int)(ui->drag_is_from ? 'C' : 'X'), ui->sx, ui->sy); return dupstr(buf); @@ -1522,11 +1592,11 @@ static char *interpret_move(const game_state *state, game_ui *ui, if (ui->drag_is_from) { if (!isvalidmove(state, false, ui->sx, ui->sy, x, y)) - return UI_UPDATE; + return MOVE_UI_UPDATE; sprintf(buf, "L%d,%d-%d,%d", ui->sx, ui->sy, x, y); } else { if (!isvalidmove(state, false, x, y, ui->sx, ui->sy)) - return UI_UPDATE; + return MOVE_UI_UPDATE; sprintf(buf, "L%d,%d-%d,%d", x, y, ui->sx, ui->sy); } return dupstr(buf); @@ -1535,7 +1605,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, else if ((button == 'x' || button == 'X') && ui->cshow) { int si = ui->cy*w + ui->cx; if (state->prev[si] == -1 && state->next[si] == -1) - return UI_UPDATE; + return MOVE_UI_UPDATE; sprintf(buf, "%c%d,%d", (int)((button == 'x') ? 'C' : 'X'), ui->cx, ui->cy); return dupstr(buf); @@ -1641,7 +1711,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) { /* Ick: fake up `ds->tilesize' for macro expansion purposes */ struct { int tilesize, order; } ads, *ds = &ads; @@ -1825,9 +1895,8 @@ static void draw_star(drawing *dr, int cx, int cy, int rad, int npoints, coords = snewn(npoints * 2 * 2, int); for (n = 0; n < npoints * 2; n++) { - /* hack to accomodate rockbox's concave polygon drawing */ - a = 2.0 * PI * ((double)n / ((double)npoints * 2.0)) + angle_offset - PI / npoints; - r = (n % 2 == 0) ? (double)rad/2.0 : (double)rad; + a = 2.0 * PI * ((double)n / ((double)npoints * 2.0)) + angle_offset; + r = (n % 2) ? (double)rad/2.0 : (double)rad; /* We're rotating the point at (0, -r) by a degrees */ coords[2*n+0] = cx + (int)( r * sin(a)); @@ -2082,7 +2151,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds, if (!ds->started) { int aw = TILE_SIZE * state->w; int ah = TILE_SIZE * state->h; - draw_rect(dr, 0, 0, aw + 2 * BORDER, ah + 2 * BORDER, COL_BACKGROUND); draw_rect_outline(dr, BORDER - 1, BORDER - 1, aw + 2, ah + 2, COL_GRID); draw_update(dr, 0, 0, aw + 2 * BORDER, ah + 2 * BORDER); } @@ -2127,28 +2195,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, if (state->nums[i] != ds->nums[i] || f != ds->f[i] || dirp != ds->dirp[i] || force || !ds->started) { - int sign; - { - /* - * Trivial and foolish configurable option done on - * purest whim. With this option enabled, the - * victory flash is done by rotating each square - * in the opposite direction from its immediate - * neighbours, so that they behave like a field of - * interlocking gears. With it disabled, they all - * rotate in the same direction. Choose for - * yourself which is more brain-twisting :-) - */ - static int gear_mode = -1; - if (gear_mode < 0) { - char *env = getenv("SIGNPOST_GEARS"); - gear_mode = (env && (env[0] == 'y' || env[0] == 'Y')); - } - if (gear_mode) - sign = 1 - 2 * ((x ^ y) & 1); - else - sign = 1; - } + int sign = (ui->gear_mode ? 1 - 2 * ((x ^ y) & 1) : 1); tile_redraw(dr, ds, BORDER + x * TILE_SIZE, BORDER + y * TILE_SIZE, @@ -2206,21 +2253,18 @@ 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; - game_compute_size(params, 1300, &pw, &ph); + game_compute_size(params, 1300, 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 ink = print_mono_colour(dr, 0); int x, y; @@ -2273,12 +2317,14 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + 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_TILE_SIZE, game_compute_size, game_set_size, @@ -2292,7 +2338,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 */ REQUIRE_RBUTTON, /* flags */ }; @@ -2301,10 +2347,9 @@ const struct game thegame = { #include #include -const char *quis = NULL; -int verbose = 0; +static const char *quis = NULL; -void usage(FILE *out) { +static void usage(FILE *out) { fprintf(out, "usage: %s [--stdin] [--soak] [--seed SEED] |\n", quis); } @@ -2405,7 +2450,7 @@ static void process_desc(char *id) thegame.free_params(p); } -int main(int argc, const char *argv[]) +int main(int argc, char *argv[]) { char *id = NULL, *desc, *aux = NULL; const char *err; -- cgit v1.2.3