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/guess.c | 221 +++++++++++++++++++++++---------------- 1 file changed, 129 insertions(+), 92 deletions(-) (limited to 'apps/plugins/puzzles/src/guess.c') diff --git a/apps/plugins/puzzles/src/guess.c b/apps/plugins/puzzles/src/guess.c index a501579442..5c5f941230 100644 --- a/apps/plugins/puzzles/src/guess.c +++ b/apps/plugins/puzzles/src/guess.c @@ -7,7 +7,11 @@ #include #include #include -#include +#ifdef NO_TGMATH_H +# include +#else +# include +#endif #include "puzzles.h" @@ -35,6 +39,11 @@ typedef struct pegrow { int *pegs; /* 0 is 'empty' */ int *feedback; /* may well be unused */ } *pegrow; +/* Pegs can have these flags OR'ed into them. */ +#define PEG_CURSOR 0x1000 +#define PEG_HOLD 0x2000 +#define PEG_LABELLED 0x4000 +#define PEG_FLAGS (PEG_CURSOR | PEG_HOLD | PEG_LABELLED) struct game_state { game_params params; @@ -88,13 +97,7 @@ static bool game_fetch_preset(int i, char **name, game_params **params) return false; *name = dupstr(guess_presets[i].name); - /* - * get round annoying const issues - */ - { - game_params tmp = guess_presets[i].params; - *params = dup_params(&tmp); - } + *params = dup_params(&guess_presets[i].params); return true; } @@ -365,16 +368,6 @@ static char *solve_game(const game_state *state, const game_state *currstate, return dupstr("S"); } -static bool game_can_format_as_text_now(const game_params *params) -{ - return true; -} - -static char *game_text_format(const game_state *state) -{ - return NULL; -} - static bool is_markable(const game_params *params, pegrow pegs) { int i, nset = 0, nrequired; @@ -386,6 +379,7 @@ static bool is_markable(const game_params *params, pegrow pegs) for (i = 0; i < params->npegs; i++) { int c = pegs->pegs[i]; if (c > 0) { + assert(c <= params->ncolours); colcount->pegs[c-1]++; nset++; } @@ -414,7 +408,7 @@ struct game_ui { int drag_col, drag_x, drag_y; /* x and y are *center* of peg! */ int drag_opeg; /* peg index, if dragged from a peg (from current guess), otherwise -1 */ - bool show_labels; /* label the colours with letters */ + bool show_labels; /* label the colours with numbers */ pegrow hint; }; @@ -422,19 +416,45 @@ static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); memset(ui, 0, sizeof(game_ui)); - ui->params = state->params; /* structure copy */ - ui->curr_pegs = new_pegrow(state->params.npegs); - ui->holds = snewn(state->params.npegs, bool); - memset(ui->holds, 0, sizeof(bool)*state->params.npegs); + if (state != NULL) { + ui->params = state->params; /* structure copy */ + ui->curr_pegs = new_pegrow(state->params.npegs); + ui->holds = snewn(state->params.npegs, bool); + memset(ui->holds, 0, sizeof(bool)*state->params.npegs); + } + ui->display_cur = getenv_bool("PUZZLES_SHOW_CURSOR", false); ui->drag_opeg = -1; return ui; } +static config_item *get_prefs(game_ui *ui) +{ + config_item *ret; + + ret = snewn(2, config_item); + + ret[0].name = "Label colours with numbers"; + ret[0].kw = "show-labels"; + ret[0].type = C_BOOLEAN; + ret[0].u.boolean.bval = ui->show_labels; + + ret[1].name = NULL; + ret[1].type = C_END; + + return ret; +} + +static void set_prefs(game_ui *ui, const config_item *cfg) +{ + ui->show_labels = cfg[0].u.boolean.bval; +} + static void free_ui(game_ui *ui) { if (ui->hint) free_pegrow(ui->hint); - free_pegrow(ui->curr_pegs); + if (ui->curr_pegs) + free_pegrow(ui->curr_pegs); sfree(ui->holds); sfree(ui); } @@ -462,12 +482,16 @@ static char *encode_ui(const game_ui *ui) return sresize(ret, p - ret, char); } -static void decode_ui(game_ui *ui, const char *encoding) +static void decode_ui(game_ui *ui, const char *encoding, + const game_state *state) { int i; const char *p = encoding; for (i = 0; i < ui->curr_pegs->npegs; i++) { ui->curr_pegs->pegs[i] = atoi(p); + if (ui->curr_pegs->pegs[i] < 0 || + ui->curr_pegs->pegs[i] > ui->params.ncolours) + ui->curr_pegs->pegs[i] = 0; /* Remove invalid pegs. */ while (*p && isdigit((unsigned char)*p)) p++; if (*p == '_') { /* NB: old versions didn't store holds */ @@ -507,7 +531,20 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->markable = is_markable(&newstate->params, ui->curr_pegs); /* Clean up cursor position */ if (!ui->markable && ui->peg_cur == newstate->solution->npegs) - ui->peg_cur--; + ui->peg_cur = 0; +} + +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (state->solved) return ""; + if (button == CURSOR_SELECT) { + if (ui->peg_cur == state->params.npegs) return "Submit"; + return "Place"; + } + if (button == CURSOR_SELECT2 && ui->peg_cur != state->params.npegs) + return "Hold"; + return ""; } #define PEGSZ (ds->pegsz) @@ -700,7 +737,11 @@ static void compute_hint(const game_state *state, game_ui *ui) for (j = 0; j < state->params.npegs; ++j) if (state->guesses[i]->pegs[j] > maxcolour) maxcolour = state->guesses[i]->pegs[j]; - maxcolour = min(maxcolour + 1, state->params.ncolours); + if (state->params.allow_multiple) + maxcolour = min(maxcolour + 1, state->params.ncolours); + else + maxcolour = min(maxcolour + state->params.npegs, + state->params.ncolours); increase_mincolour: for (i = 0; i < state->next_go; ++i) { @@ -722,6 +763,7 @@ increase_mincolour: } while (ui->hint->pegs[0] <= state->params.ncolours) { + if (!is_markable(&state->params, ui->hint)) goto increment_pegrow; for (i = 0; i < state->next_go; ++i) { mark_pegs(ui->hint, state->guesses[i], maxcolour); for (j = 0; j < state->params.npegs; ++j) @@ -777,7 +819,7 @@ static char *interpret_move(const game_state *from, game_ui *ui, */ if (button == 'l' || button == 'L') { ui->show_labels = !ui->show_labels; - return UI_UPDATE; + return MOVE_UI_UPDATE; } if (from->solved) return NULL; @@ -834,13 +876,13 @@ static char *interpret_move(const game_state *from, game_ui *ui, ui->drag_y = y; debug(("Start dragging, col = %d, (%d,%d)", ui->drag_col, ui->drag_x, ui->drag_y)); - ret = UI_UPDATE; + ret = MOVE_UI_UPDATE; } } else if (button == LEFT_DRAG && ui->drag_col) { ui->drag_x = x; ui->drag_y = y; debug(("Keep dragging, (%d,%d)", ui->drag_x, ui->drag_y)); - ret = UI_UPDATE; + ret = MOVE_UI_UPDATE; } else if (button == LEFT_RELEASE && ui->drag_col) { if (over_guess > -1) { debug(("Dropping colour %d onto guess peg %d", @@ -857,13 +899,13 @@ static char *interpret_move(const game_state *from, game_ui *ui, ui->drag_opeg = -1; ui->display_cur = false; debug(("Stop dragging.")); - ret = UI_UPDATE; + ret = MOVE_UI_UPDATE; } else if (button == RIGHT_BUTTON) { if (over_guess > -1) { /* we use ths feedback in the game_ui to signify * 'carry this peg to the next guess as well'. */ ui->holds[over_guess] ^= 1; - ret = UI_UPDATE; + ret = MOVE_UI_UPDATE; } } else if (button == LEFT_RELEASE && over_hint && ui->markable) { /* NB this won't trigger if on the end of a drag; that's on @@ -872,44 +914,47 @@ static char *interpret_move(const game_state *from, game_ui *ui, } /* keyboard input */ - if (button == CURSOR_UP || button == CURSOR_DOWN) { - ui->display_cur = true; - if (button == CURSOR_DOWN && (ui->colour_cur+1) < from->params.ncolours) - ui->colour_cur++; - if (button == CURSOR_UP && ui->colour_cur > 0) - ui->colour_cur--; - ret = UI_UPDATE; - } else if (button == 'h' || button == 'H' || button == '?') { - compute_hint(from, ui); - ret = UI_UPDATE; - } else if (button == CURSOR_LEFT || button == CURSOR_RIGHT) { + if (IS_CURSOR_MOVE(button)) { int maxcur = from->params.npegs; if (ui->markable) maxcur++; - ui->display_cur = true; - if (button == CURSOR_RIGHT && (ui->peg_cur+1) < maxcur) - ui->peg_cur++; - if (button == CURSOR_LEFT && ui->peg_cur > 0) - ui->peg_cur--; - ret = UI_UPDATE; - } else if (IS_CURSOR_SELECT(button)) { + ret = move_cursor(button, &ui->peg_cur, &ui->colour_cur, + maxcur, from->params.ncolours, + false, &ui->display_cur); + } else if (button == 'h' || button == 'H' || button == '?') { + compute_hint(from, ui); + ret = MOVE_UI_UPDATE; + } else if (button == CURSOR_SELECT) { ui->display_cur = true; if (ui->peg_cur == from->params.npegs) { ret = encode_move(from, ui); } else { set_peg(&from->params, ui, ui->peg_cur, ui->colour_cur+1); - ret = UI_UPDATE; + ret = MOVE_UI_UPDATE; } - } else if (button == 'D' || button == 'd' || button == '\b') { + } else if (((button >= '1' && button <= '0' + from->params.ncolours) || + (button == '0' && from->params.ncolours == 10)) && + ui->peg_cur < from->params.npegs) { ui->display_cur = true; - set_peg(&from->params, ui, ui->peg_cur, 0); - ret = UI_UPDATE; + /* Number keys insert a peg and advance the cursor. */ + set_peg(&from->params, ui, ui->peg_cur, + button == '0' ? 10 : button - '0'); + if (ui->peg_cur + 1 < from->params.npegs + ui->markable) + ui->peg_cur++; + ret = MOVE_UI_UPDATE; + } else if (button == 'D' || button == 'd' || button == '\b') { + if (!ui->display_cur || ui->curr_pegs->pegs[ui->peg_cur] != 0) { + ui->display_cur = true; + set_peg(&from->params, ui, ui->peg_cur, 0); + ret = MOVE_UI_UPDATE; + } else + ret = MOVE_NO_EFFECT; } else if (button == CURSOR_SELECT2) { if (ui->peg_cur == from->params.npegs) return NULL; ui->display_cur = true; ui->holds[ui->peg_cur] ^= 1; - ret = UI_UPDATE; + ret = MOVE_UI_UPDATE; } return ret; } @@ -925,6 +970,8 @@ static game_state *execute_move(const game_state *from, const char *move) ret->solved = -1; return ret; } else if (move[0] == 'G') { + /* No guesses are allowed once the game is solved. */ + if (from->solved) return NULL; p = move+1; ret = dup_game(from); @@ -975,7 +1022,7 @@ static game_state *execute_move(const game_state *from, const char *move) #define BORDER 0.5 static void game_compute_size(const game_params *params, int tilesize, - int *x, int *y) + const game_ui *ui, int *x, int *y) { double hmul, vmul_c, vmul_g, vmul; int hintw = (params->npegs+1)/2; @@ -1019,7 +1066,8 @@ static void game_set_size(drawing *dr, game_drawstate *ds, guessh = ((ds->pegsz + ds->gapsz) * params->nguesses); /* guesses */ guessh += ds->gapsz + ds->pegsz; /* solution */ - game_compute_size(params, tilesize, &ds->w, &ds->h); + /* We know we don't need anything from the game_ui we haven't got */ + game_compute_size(params, tilesize, NULL, &ds->w, &ds->h); ds->colx = ds->border; ds->coly = (ds->h - colh) / 2; @@ -1196,7 +1244,7 @@ static void draw_peg(drawing *dr, game_drawstate *ds, int cx, int cy, if (labelled && col) { char buf[2]; - buf[0] = 'a'-1 + col; + buf[0] = '0' + (col % 10); buf[1] = '\0'; draw_text(dr, cx+PEGRAD, cy+PEGRAD, FONT_VARIABLE, PEGRAD, ALIGN_HCENTRE|ALIGN_VCENTRE, COL_FRAME, buf); @@ -1233,23 +1281,25 @@ static void guess_redraw(drawing *dr, game_drawstate *ds, int guess, for (i = 0; i < dest->npegs; i++) { scol = src ? src->pegs[i] : 0; if (i == cur_col) - scol |= 0x1000; + scol |= PEG_CURSOR; if (holds && holds[i]) - scol |= 0x2000; + scol |= PEG_HOLD; if (labelled) - scol |= 0x4000; + scol |= PEG_LABELLED; if ((dest->pegs[i] != scol) || force) { draw_peg(dr, ds, rowx + PEGOFF * i, rowy, false, labelled, - scol &~ 0x7000); + scol &~ PEG_FLAGS); + if (scol & PEG_CURSOR) + draw_cursor(dr, ds, rowx + PEGOFF * i, rowy); /* * Hold marker. */ - draw_rect(dr, rowx + PEGOFF * i, rowy + PEGSZ + ds->gapsz/2, - PEGSZ, 2, (scol & 0x2000 ? COL_HOLD : COL_BACKGROUND)); - draw_update(dr, rowx + PEGOFF * i, rowy + PEGSZ + ds->gapsz/2, - PEGSZ, 2); - if (scol & 0x1000) - draw_cursor(dr, ds, rowx + PEGOFF * i, rowy); + if (scol & PEG_HOLD) { + draw_rect(dr, rowx + PEGOFF * i, + rowy + PEGSZ + ds->gapsz/2 - 2, PEGSZ, 2, COL_HOLD); + } + draw_update(dr, rowx + PEGOFF * i, + rowy + PEGSZ + ds->gapsz/2 - 2, PEGSZ, 2); } dest->pegs[i] = scol; } @@ -1276,9 +1326,9 @@ static void hint_redraw(drawing *dr, game_drawstate *ds, int guess, for (i = 0; i < dest->npegs; i++) { scol = src ? src->feedback[i] : 0; if (i == 0 && cursor) - scol |= 0x1000; + scol |= PEG_CURSOR; if (i == 0 && markable) - scol |= 0x2000; + scol |= PEG_HOLD; if ((scol != dest->feedback[i]) || force) { need_redraw = true; } @@ -1349,7 +1399,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds, new_move = (state->next_go != ds->next_go) || !ds->started; if (!ds->started) { - draw_rect(dr, 0, 0, ds->w, ds->h, COL_BACKGROUND); draw_rect(dr, SOLN_OX, SOLN_OY - ds->gapsz - 1, SOLN_W, 2, COL_FRAME); draw_update(dr, 0, 0, ds->w, ds->h); } @@ -1364,19 +1413,18 @@ static void game_redraw(drawing *dr, game_drawstate *ds, for (i = 0; i < state->params.ncolours; i++) { int val = i+1; if (ui->display_cur && ui->colour_cur == i) - val |= 0x1000; + val |= PEG_CURSOR; if (ui->show_labels) - val |= 0x2000; + val |= PEG_HOLD; if (ds->colours->pegs[i] != val) { draw_peg(dr, ds, COL_X(i), COL_Y(i), false, ui->show_labels, i+1); - if (val & 0x1000) + if (val & PEG_CURSOR) draw_cursor(dr, ds, COL_X(i), COL_Y(i)); ds->colours->pegs[i] = val; } } - /* draw the guesses (so far) and the hints - * (in reverse order to avoid trampling holds, and postponing the + /* draw the guesses (so far) and the hints (postponing the * next_go'th to not overrender the top of the circular cursor) */ for (i = state->params.nguesses - 1; i >= 0; i--) { if (i < state->next_go || state->solved) { @@ -1473,19 +1521,6 @@ static int game_status(const game_state *state) return state->solved; } -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(drawing *dr, const game_state *state, int tilesize) -{ -} - #ifdef COMBINED #define thegame guess #endif @@ -1506,13 +1541,15 @@ const struct game thegame = { dup_game, free_game, true, solve_game, - false, game_can_format_as_text_now, game_text_format, + false, NULL, NULL, /* can_format_as_text_now, text_format */ + get_prefs, set_prefs, new_ui, free_ui, encode_ui, decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PEG_PREFER_SZ, game_compute_size, game_set_size, @@ -1524,9 +1561,9 @@ const struct game thegame = { game_flash_length, game_get_cursor_location, game_status, - false, false, game_print_size, game_print, + false, false, NULL, NULL, /* print_size, print */ false, /* wants_statusbar */ - false, game_timing_state, + false, NULL, /* timing_state */ 0, /* flags */ }; -- cgit v1.2.3