From 1549df40a72face9c7fbd4e5d96386ba2a07bb58 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Sat, 11 Jun 2022 22:02:44 -0400 Subject: [PATCH] curses: Make the game winnable. Implement the overlay goal view and make the game stop when the the puzzle is solved. --- src/curses.c | 100 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 20 deletions(-) diff --git a/src/curses.c b/src/curses.c index 6d86101..06ec351 100644 --- a/src/curses.c +++ b/src/curses.c @@ -50,7 +50,18 @@ static struct app_state { WINDOW *gamewin[WINDOW_MAX], *goalwin[WINDOW_MAX]; WINDOW *timer; - int cursor; + + /* Most recently displayed timer value, for screen redraw. */ + uint_least32_t timer_ms; + + /* Location of the keyboard cursor */ + int_least8_t cursor; + + /* If true, the goal will be displayed over the main play area. */ + uint_least8_t view_goal_on_game; + + /* Clicked toolbar item */ + uint_least8_t toolbar_click; } state; static void print_version(void) @@ -171,12 +182,20 @@ static void redraw_area_border(WINDOW **win, unsigned x, unsigned sz) static void curs_redraw_game(struct app_state *state, uint_fast32_t mask) { - uint_least32_t *gp = state->board.game; + uint_least32_t buf[3], *gp = state->board.game; int i; 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; + } + for (i = 0; i < 25; i++) { if (mask & 1) { redraw_tile(state->gamewin, i%5, i/5, @@ -379,11 +398,39 @@ static void app_initialize(int argc, char **argv) refresh(); } +static void update_timer(struct app_state *state, uint_fast32_t ms) +{ + unsigned sec, min; + + state->timer_ms = ms; + sec = ms / 1000; ms %= 1000; + min = sec / 60; sec %= 60; + mvwprintw(state->timer, 0, 0, "Time: %u:%.2u.%.3u", + min, sec, (unsigned)ms); + wclrtoeol(state->timer); + wrefresh(state->timer); +} + static uint_fast32_t do_move(struct app_state *state, int x, int y) { uint_fast32_t mask; if ((mask = game_do_move(&state->board, x, y)) != 0) { + uint_fast32_t goal = game_check_goal(&state->board); + + if (state->view_goal_on_game) { + state->view_goal_on_game = 0; + mask |= goal; + } + + if (goal == 0) { + /* Solved! */ + update_timer(state, game_finish(&state->board)); + mask |= ~GOAL_MASK; + state->cursor = -1; + timeout(-1); + } + curs_redraw_game(state, mask); doupdate(); } @@ -396,18 +443,6 @@ static void do_reset_cursor(struct app_state *state) state->cursor = 5*state->board.y + state->board.x; } -static void update_timer(struct app_state *state, uint_fast32_t ms) -{ - unsigned sec, min; - - sec = ms / 1000; ms %= 1000; - min = sec / 60; sec %= 60; - mvwprintw(state->timer, 0, 0, "Time: %u:%.2u.%.3u", - min, sec, (unsigned)ms); - wclrtoeol(state->timer); - wrefresh(state->timer); -} - static void do_new_game(struct app_state *state) { game_reset(&state->board); @@ -453,7 +488,19 @@ static void do_mouse(struct app_state *state) set_bstate_helper(1); set_bstate_helper(3); #endif - if (bstate == BUTTON1_PRESSED) { + if (bstate & BUTTON3_PRESSED) { + state->view_goal_on_game = 1; + curs_redraw_game(state, game_check_goal(&state->board)); + doupdate(); + } + + if (bstate & BUTTON3_RELEASED) { + state->view_goal_on_game = 0; + curs_redraw_game(state, game_check_goal(&state->board)); + doupdate(); + } + + if (!state->view_goal_on_game && bstate & BUTTON1_PRESSED) { uint_fast32_t cursor_mask, move_mask; int w, h; @@ -479,11 +526,18 @@ static void do_mouse(struct app_state *state) static void do_move_cursor(struct app_state *state, int c) { - uint_fast32_t mask = 1ul << state->cursor; + uint_fast32_t mask = 0; if (state->cursor < 0) { /* Cursor was hidden; reset it */ do_reset_cursor(state); + } else { + mask = 1ul << state->cursor; + } + + if (state->view_goal_on_game) { + state->view_goal_on_game = 0; + mask |= game_check_goal(&state->board); } switch (c) { @@ -506,7 +560,7 @@ static void do_move_cursor(struct app_state *state, int c) } curs_redraw_game(state, mask | 1ul << state->cursor); - refresh(); + doupdate(); } static void do_keystroke(struct app_state *state, int c) @@ -515,8 +569,13 @@ static void do_keystroke(struct app_state *state, int c) case KEY_DOWN: case KEY_UP: case KEY_LEFT: case KEY_RIGHT: do_move_cursor(state, c); break; + case '\t': + state->view_goal_on_game ^= 2u; + curs_redraw_game(state, game_check_goal(&state->board)); + doupdate(); + break; case ' ': - if (state->cursor >= 0) + if (!(state->view_goal_on_game & 1) && state->cursor >= 0) do_move(state, state->cursor%5, state->cursor/5); break; } @@ -540,7 +599,7 @@ int main(int argc, char **argv) refresh(); curs_redraw_game(&state, -1); curs_redraw_goal(&state, -1); - refresh(); + update_timer(&state, state.timer_ms); break; #endif #if HAVE_CURSES_MOUSE_SUPPORT @@ -553,6 +612,7 @@ int main(int argc, char **argv) case ERR:; } - update_timer(&state, game_elapsed(&state.board)); + if (state.board.x <= 4) + update_timer(&state, game_elapsed(&state.board)); } } -- 2.43.2