/*
* 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