From a70b0ef8bf7cb26cc779e9b2da480295f5ebf2bd Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Wed, 4 Jan 2023 02:20:16 -0500 Subject: [PATCH] Fix bitmap generation in goal overlay feature. The board initialization procedure no longer ensures that unused bits of the goal bitmap are zeroed. When these are ORed with the game bitmaps, it can result in bogus values for the outer tiles, which will either render incorrectly or trip the assertion if the tile value is 7. Normally only the objective area tiles get redrawn when the feature is toggled on or off, which hides the problem most of the time, as something else needs to trigger redraw of the outer tiles. One way to make this happen is to use the keyboard to start a new game while holding down the right mouse button. The best solution seems to be to just mask the goal bitmaps where they are used. As the same fix is needed in both the curses and motif UIs, implement this new logic into a common function and add a new test case to check for this bug. --- Makefile.am | 6 ++--- src/curses.c | 11 +++------ src/game.h | 19 ++++++++++++++- src/x11.c | 9 ++------ t/.gitignore | 1 + t/overlaygoal.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/game.at | 25 +++++++++++++++++++- testsuite.at | 2 +- 8 files changed, 113 insertions(+), 21 deletions(-) create mode 100644 t/overlaygoal.c diff --git a/Makefile.am b/Makefile.am index c0d0c27..88fde3c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -94,11 +94,13 @@ check_PROGRAMS = t/boardbit \ t/boardrect \ t/checkgoal \ t/ewmhicon \ + t/overlaygoal \ t/rng-test EXTRA_DIST += t/xos256ss.c -t_boardbit_SOURCES = t/boardbit.c $(t_boardbit_OBJECTS): $(gnulib_headers) +$(t_overlaygoal_OBJECTS): $(gnulib_headers) +$(t_rng_test_OBJECTS): $(gnulib_headers) t_boardmove_SOURCES = t/boardmove.c src/game.c $(t_boardmove_OBJECTS): $(gnulib_headers) @@ -113,8 +115,6 @@ t_ewmhicon_SOURCES = t/ewmhicon.c src/ewmhicon.c src/icon.c common/src/help.c t_ewmhicon_LDADD = $(MOTIF_LIBS) $(GNULIB) $(t_ewmhicon_OBJECTS): $(gnulib_headers) -$(t_rng_test_OBJECTS): $(gnulib_headers) - XPMICONS_LOCOLOR = data/lo16x16.xpm data/lo32x32.xpm data/lo48x48.xpm XPMICONS_HICOLOR = data/hi16x16.xpm data/hi24x24.xpm \ data/hi32x32.xpm data/hi48x48.xpm diff --git a/src/curses.c b/src/curses.c index f66589e..c0f4265 100644 --- a/src/curses.c +++ b/src/curses.c @@ -1,6 +1,6 @@ /* * Curses UI for slide puzzle game - * Copyright © 2022 Nick Bowler + * Copyright © 2022-2023 Nick Bowler * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -156,13 +156,8 @@ static void curs_redraw_game(struct app_state *state, uint_fast32_t mask) if (mask == -1) redraw_area_border(state->gamewin, 2, 5); - if (state->view_goal_on_game) { - for (i = 0; i < 3; i++) { - buf[i] = state->board.goal[i]; - buf[i] = (gp[i] & ~GOAL_MASK) | (buf[i] << GOAL_SHIFT); - } - gp = buf; - } + if (state->view_goal_on_game) + gp = game_overlay_goal(&state->board, buf); for (i = 0; i < 25; i++) { if (mask & 1) { diff --git a/src/game.h b/src/game.h index efa7ef0..56fb442 100644 --- a/src/game.h +++ b/src/game.h @@ -1,6 +1,6 @@ /* * Slide puzzle core game logic - * Copyright © 2022 Nick Bowler + * Copyright © 2022-2023 Nick Bowler * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -221,4 +221,21 @@ int_fast32_t game_elapsed(struct board *board); */ int_fast32_t game_finish(struct board *board); +/* + * Constructs a new set of game bitmaps into buf, with tiles in the + * objective area replaced by the goal tile colours, and returns buf. + */ +static inline uint_least32_t * +game_overlay_goal(struct board *board, uint_least32_t *buf) +{ + int i; + + for (i = 0; i < 3; i++) { + buf[i] = (unsigned long)board->goal[i] << GOAL_SHIFT; + buf[i] = (buf[i] & GOAL_MASK) | (board->game[i] & ~GOAL_MASK); + } + + return buf; +} + #endif diff --git a/src/x11.c b/src/x11.c index c8ef335..b4b0a2c 100644 --- a/src/x11.c +++ b/src/x11.c @@ -281,13 +281,8 @@ void x11_redraw_game(struct app_state *state, uint_fast32_t mask) unsigned sz = state->game_tile_sz; int i; - if (state->flags & FLAG_VIEW_GOAL_ON_GAME) { - for (i = 0; i < 3; i++) { - buf[i] = state->board.goal[i]; - buf[i] = (gp[i] & ~GOAL_MASK) | (buf[i] << GOAL_SHIFT); - } - gp = buf; - } + if (state->flags & FLAG_VIEW_GOAL_ON_GAME) + gp = game_overlay_goal(&state->board, buf); /* Optimize the game end case where the outer frame is cleared */ if (mask & GAME_MASK & ~GOAL_MASK) { diff --git a/t/.gitignore b/t/.gitignore index b8c0b58..54f6466 100644 --- a/t/.gitignore +++ b/t/.gitignore @@ -3,4 +3,5 @@ /boardrect /checkgoal /ewmhicon +/overlaygoal /rng-test diff --git a/t/overlaygoal.c b/t/overlaygoal.c new file mode 100644 index 0000000..059e835 --- /dev/null +++ b/t/overlaygoal.c @@ -0,0 +1,61 @@ +/* + * Helper to test game_overlay_goal function. + * Copyright © 2023 Nick Bowler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include "game.h" + +#define X3(x) x x x + +static const char *progname = "overlaygoal"; + +int main(int argc, char **argv) +{ + struct board board; + int rc; + + if (argc > 0) + progname = argv[0]; + + while ((rc = scanf(X3("%" SCNxLEAST32) X3("%" SCNxLEAST16), + &board.game[0], &board.game[1], + &board.game[2], &board.goal[0], + &board.goal[1], &board.goal[2])) == 6) + { + uint_least32_t buf[3]; + + game_overlay_goal(&board, buf); + printf(X3(" %.7" PRIxLEAST32)"\n"+1, buf[0], buf[1], buf[2]); + } + + if (rc == EOF) { + if (ferror(stdin)) { + fprintf(stderr, "%s: read error: %s\n", progname, + strerror(errno)); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; + } + + fprintf(stderr, "%s: invalid input sequence\n", progname); + return EXIT_FAILURE; +} diff --git a/tests/game.at b/tests/game.at index 4d367af..610b066 100644 --- a/tests/game.at +++ b/tests/game.at @@ -1,4 +1,4 @@ -# Copyright © 2022 Nick Bowler +# Copyright © 2022-2023 Nick Bowler # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -629,3 +629,26 @@ AT_DATA([expout], AT_CHECK([checkgoal result.dat && cat result.dat], [0], [expout]) AT_CLEANUP + +AT_SETUP([game_overlay_goal]) + +AT_DATA([boards.dat], [dnl +0 0 0 0 0 0 +0 0 0 -1 0 0 +0 0 0 0 -1 0 +0 0 0 0 0 -1 +1ffffff 1ffffff 1ffffff 0 0 0 +]) + +AT_DATA([expout], [dnl +0000000 0000000 0000000 +00739c0 0000000 0000000 +0000000 00739c0 0000000 +0000000 0000000 00739c0 +1f8c63f 1f8c63f 1f8c63f +]) + +AT_CHECK([overlaygoal result.dat && cat result.dat], + [0], [expout]) + +AT_CLEANUP diff --git a/testsuite.at b/testsuite.at index f76b15a..09efc3a 100644 --- a/testsuite.at +++ b/testsuite.at @@ -1,4 +1,4 @@ -AT_COPYRIGHT([Copyright © 2022 Nick Bowler]) +AT_COPYRIGHT([Copyright © 2022-2023 Nick Bowler]) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by -- 2.43.2