/* * Slide puzzle core game logic * 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 * 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 . */ #ifndef RRACE_GAME_H_ #define RRACE_GAME_H_ #include #include enum { TILE_EMPTY, TILE_RED, TILE_ORANGE, TILE_YELLOW, TILE_GREEN, TILE_BLUE, TILE_WHITE, TILE_MAX }; #define GOAL_SHIFT 6 #define GOAL_MASK 0x739c0ul #define GAME_MASK 0x1fffffful struct board { /* * Bit planes representing the current game area. * * The 5x5 game board is represented by these four 25-bit values. * The bits are arranged in row-major order so, for example, bit 0 * corresponds to position (0,0), bit 4 is position (4,0) and bit 25 * is position (4, 4). * * game[0] - least significant bit of the tile's colour. * game[1] - tile colour. * game[2] - most significant bit of the tile's colour. * game[3] - No internal meaning. The game_reset function will clear * the bit at the empty position and set all others, and * game_do_move will adjust it. */ uint_least32_t game[4]; /* * Bit planes representing the goal area. * * These are encoded identically to the game area, except the values * are shifted right by 6 as only bits 6 through 18 are relevant. * * goal[0] - least significant bit of the tile's colour. * goal[2] - tile colour. * goal[3] - most significant bit of the tile's colour. */ uint_least16_t goal[3]; /* (x, y) position of the current empty space. */ uint_least8_t x, y; xtime_t time_start; }; /* Return the board bitmap with all bits in column x set */ static inline uint_fast32_t board_column(int x) { return 0x108421ul << x; } /* Return the board bitmap with all bits in row y set */ static inline uint_fast32_t board_row(int y) { return 0x1ful << 5*y; } /* Return the board bitmap with the bit at position (x, y) set. */ static inline uint_fast32_t board_position(int x, int y) { return 1ul << x << 5*y; } /* * Return the board bitmap with set bits indicating tile locations * that change with the hole at (x0, y) and the play at (x1, y). */ static inline uint_fast32_t board_mask_h(int y, int x0, int x1) { uint_fast32_t row = board_row(y); if (x0 < x1) return (row << x0) & (row >> (4-x1)); return (row << x1) & (row >> (4-x0)); } /* * Return the board bitmap with set bits indicating tile locations * that change with the hole at (x, y0) and the play at (x, y1). */ static inline uint_fast32_t board_mask_v(int x, int y0, int y1) { uint_fast32_t col = board_column(x); if (y0 < y1) return (col << 5*y0) & (col >> 5*(4-y1)); return (col << 5*y1) & (col >> 5*(4-y0)); } /* * Return the board bitmap setting locations on or above row y. */ static inline uint_fast32_t board_above(int y) { return ( 0x20ul << 5*y ) - 1; } /* * Return the board bitmap setting locations on or below row y. */ static inline uint_fast32_t board_below(int y) { return ~( 1ul << 5*y ) + 1; } /* * Return the board bitmap setting locations on or left of column x. */ static inline uint_fast32_t board_left(int x) { uint_fast32_t val = board_column(x); return val | (val - 0x108421); } /* * Return the board bitmap setting locations on or right of column x. */ static inline uint_fast32_t board_right(int x) { uint_fast32_t val = board_column(x); return ~val + 0x108421; } /* * Return the board bitmap setting the rectangle of locations that are: * * - on or right of column x1, and * - on or left of column x2, and * - on or below row y1, and * - on or above row y2. * * It must be the case that x2 >= x1 and y2 >= y1. */ static inline uint_fast32_t board_rect(int x1, int y1, int x2, int y2) { return (board_left(x2-x1) << x1) & (board_above(y2-y1) << 5*y1); } /* * Extract the tile colour from a specific position of one of the * arrays of tile bitmaps. The position is a bit index. So for * example, game the tile at a given (x, y) position can be extracted * by board_tile(board.game, 5*y+x). */ #define board_tile(planes, bit) (((0[planes]<<0) >> (bit)) & 1) \ | (((1[planes]<<1) >> (bit)) & 2) \ | (((2[planes]<<2) >> (bit)) & 4) /* * Move the bits in the game bitmaps according to a move at position (x, y), * and update the location of the empty position which, if the move was valid * is now (x, y). * * Returns the board bitmap indicating which positions changed. A return * value of 0 therefore indicates an invalid move. */ uint_fast32_t game_do_move(struct board *board, int x, int y); /* * Returns the board bitmap setting game locations that differ from the goal. * A return value of 0 therefore indicates a winning position. */ uint_fast32_t game_check_goal(struct board *board); /* * Initialize the game RNG such that the next call to game_reset will produce a * new board that is entirely dependent on the given seed value. */ void game_reseed(unsigned long long seed); /* * Shuffle the game and goal tiles to produce a new game state. */ void game_reset(struct board *board); /* * Reset the game start time. */ void game_begin(struct board *board); /* * Return the total elapsed time (in ms) since the last call to game_begin. */ int_fast32_t game_elapsed(struct board *board); /* * Disable new moves and clear all tile bits other than the 9 goal tiles. * Returns the total elapsed time (in ms). */ 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