penguins  1.0.0
movement.c
Go to the documentation of this file.
1 #include "movement.h"
2 #include "board.h"
3 #include "game.h"
4 #include "utils.h"
5 #include <assert.h>
6 #include <stddef.h> // IWYU pragma: keep
7 
11 void movement_begin(Game* game) {
12  assert(game->phase == GAME_PHASE_SETUP_DONE);
13  game_set_current_player(game, -1);
15 }
16 
20 void movement_end(Game* game) {
21  assert(game->phase == GAME_PHASE_MOVEMENT);
23 }
24 
32  assert(game->phase == GAME_PHASE_MOVEMENT);
33  int index = game->current_player_index;
34  int checked_players = 0;
35  while (checked_players < game->players_count) {
36  index = (index + 1) % game->players_count;
37  if (any_valid_player_move_exists(game, index)) {
38  game_set_current_player(game, index);
39  return index;
40  }
41  checked_players++;
42  }
43  return -1;
44 }
45 
47 bool any_valid_player_move_exists(const Game* game, int player_idx) {
48  Player* player = game_get_player(game, player_idx);
49  for (int i = 0; i < player->penguins_count; i++) {
50  if (count_obstructed_directions(game, player->penguins[i]) < DIRECTION_MAX) {
51  return true;
52  }
53  }
54  return false;
55 }
56 
59  if (!is_tile_in_bounds(game, start)) {
61  }
62  short start_tile = get_tile(game, start);
63  if (!is_penguin_tile(start_tile)) {
65  } else if (get_tile_player_id(start_tile) != game_get_current_player(game)->id) {
67  }
68  if (count_obstructed_directions(game, start) < DIRECTION_MAX) {
69  return MOVEMENT_VALID;
70  } else {
72  }
73 }
74 
76 MovementError validate_movement(const Game* game, Coords start, Coords target, Coords* fail) {
77  MovementError result = validate_movement_start(game, start);
78  if (result != MOVEMENT_VALID) {
79  return result;
80  }
81 
82  if (!is_tile_in_bounds(game, target)) {
84  }
85  if (target.x == start.x && target.y == start.y) {
87  } else if (target.x != start.x && target.y != start.y) {
88  return MOVEMENT_DIAGONAL;
89  }
90  short target_tile = get_tile(game, target);
91  if (is_water_tile(target_tile)) {
93  } else if (is_penguin_tile(target_tile)) {
94  return MOVEMENT_ONTO_PENGUIN;
95  }
96 
97  Coords coords = start;
98  int dx = target.x > start.x ? 1 : target.x < start.x ? -1 : 0;
99  int dy = target.y > start.y ? 1 : target.y < start.y ? -1 : 0;
100  while (coords.x != target.x || coords.y != target.y) {
101  coords.x += dx, coords.y += dy;
102  if (fail) {
103  *fail = coords;
104  }
105  short tile = get_tile(game, coords);
106  if (is_water_tile(tile)) {
108  } else if (is_penguin_tile(tile)) {
109  return MOVEMENT_OVER_PENGUIN;
110  }
111  }
112 
113  return MOVEMENT_VALID;
114 }
115 
118 void move_penguin(Game* game, Coords start, Coords target) {
119  assert(game->phase == GAME_PHASE_MOVEMENT);
120  assert(validate_movement(game, start, target, NULL) == MOVEMENT_VALID);
121  Player* player = game_get_current_player(game);
122  short target_tile = get_tile(game, target);
123  assert(is_fish_tile(target_tile));
124 
125  GameLogEntry* entry;
126  if ((entry = game_push_log_entry(game, GAME_LOG_ENTRY_MOVEMENT)) != NULL) {
127  GameLogMovement* entry_data = &entry->data.movement;
128  entry_data->penguin = start;
129  entry_data->target = target;
130  entry_data->undo_tile = target_tile;
131  }
132 
133  *game_find_player_penguin(game, game->current_player_index, start) = target;
134  set_tile(game, target, PENGUIN_TILE(player->id));
135  set_tile(game, start, WATER_TILE);
136  player->points += get_tile_fish(target_tile);
137  player->moves_count += 1;
138 }
139 
142 void undo_move_penguin(Game* game) {
143  assert(game->phase == GAME_PHASE_MOVEMENT);
145 
146  Player* player = game_get_current_player(game);
147  *game_find_player_penguin(game, game->current_player_index, entry->target) = entry->penguin;
148  set_tile(game, entry->penguin, PENGUIN_TILE(player->id));
149  set_tile(game, entry->target, entry->undo_tile);
150  player->points -= get_tile_fish(entry->undo_tile);
151  player->moves_count -= 1;
152 }
153 
154 extern int count_obstructed_directions(const Game* game, Coords penguin);
155 extern PossibleSteps calculate_penguin_possible_moves(const Game* game, Coords start);
Functions for working with the game board (and the specifics of its encoding)
#define is_fish_tile(tile)
Definition: board.h:25
#define is_penguin_tile(tile)
Definition: board.h:26
#define PENGUIN_TILE(player_id)
Definition: board.h:23
#define is_water_tile(tile)
Definition: board.h:24
#define WATER_TILE
Definition: board.h:21
#define get_tile_player_id(tile)
Definition: board.h:28
#define get_tile_fish(tile)
Definition: board.h:27
The core of the unified game logic library, contains the Game struct.
@ GAME_PHASE_MOVEMENT
Set by movement_begin.
Definition: game.h:60
@ GAME_PHASE_SETUP_DONE
Set by game_end_setup, placement_end, movement_end.
Definition: game.h:58
@ GAME_LOG_ENTRY_MOVEMENT
See GameLogMovement.
Definition: game.h:91
Movement phase functions.
MovementError
Definition: movement.h:16
@ MOVEMENT_OVER_EMPTY_TILE
Definition: movement.h:25
@ MOVEMENT_PENGUIN_BLOCKED
Definition: movement.h:27
@ MOVEMENT_NOT_YOUR_PENGUIN
Definition: movement.h:22
@ MOVEMENT_NOT_A_PENGUIN
Definition: movement.h:21
@ MOVEMENT_ONTO_PENGUIN
Definition: movement.h:24
@ MOVEMENT_OUT_OF_BOUNDS
Definition: movement.h:18
@ MOVEMENT_CURRENT_LOCATION
Definition: movement.h:19
@ MOVEMENT_DIAGONAL
Definition: movement.h:20
@ MOVEMENT_VALID
Definition: movement.h:17
@ MOVEMENT_OVER_PENGUIN
Definition: movement.h:26
@ MOVEMENT_ONTO_EMPTY_TILE
Definition: movement.h:23
A pair of 2D coordinates, used for addressing the Game::board_grid.
Definition: utils.h:15
int x
Definition: utils.h:16
int y
Definition: utils.h:17
An entry in the Game::log_buffer, implemented as a tagged union.
Definition: game.h:156
union GameLogEntry::GameLogEntryData data
A GameLogEntry created by move_penguin.
Definition: game.h:113
short undo_tile
Definition: game.h:116
Coords penguin
Definition: game.h:114
Coords target
Definition: game.h:115
The central struct of the application, holds the game data and settings.
Definition: game.h:237
ALWAYS_INLINE bool is_tile_in_bounds(const Game *game, Coords coords)
Checks if the given coords are within the bounds of the board.
Definition: board.h:70
int movement_switch_player(Game *game)
Performs the player switching logic for the movement phase.
Definition: movement.c:31
Player * game_get_current_player(const Game *self)
A shorthand for calling game_get_player with Game::current_player_index.
Definition: game.h:401
ALWAYS_INLINE void set_tile(Game *game, Coords coords, short value)
Sets the value of the tile at coords (and also sets the attribute TILE_DIRTY). Fails if coords are ou...
Definition: board.h:117
void move_penguin(Game *game, Coords start, Coords target)
Creates a GameLogMovement entry. The requested move must be valid.
Definition: movement.c:118
ALWAYS_INLINE short get_tile(const Game *game, Coords coords)
Returns the value of the tile at coords. Fails if coords are outside the bounds.
Definition: board.h:108
void game_set_current_player(Game *self, int idx)
Sets Game::current_player_index and creates a GameLogPlayerChange log entry.
Definition: game.c:195
GamePhase phase
The current state of the state machine, initially set to GAME_PHASE_NONE. Use game_set_phase for sett...
Definition: game.h:242
void game_set_phase(Game *self, GamePhase phase)
Sets the current Game::phase and creates a GameLogPhaseChange log entry.
Definition: game.c:181
bool any_valid_player_move_exists(const Game *game, int player_idx)
Definition: movement.c:47
const GameLogEntry * game_pop_log_entry(Game *self, GameLogEntryType expected_type)
Pops the last entry off the top of the stack if its type matches the expected_type (this is used as a...
Definition: game.c:156
int count_obstructed_directions(const Game *game, Coords penguin)
Definition: movement.h:50
Coords * game_find_player_penguin(const Game *self, int idx, Coords coords)
Finds a penguin with the given coordinates in the Player::penguins list of a player at idx and return...
Definition: game.h:420
void movement_begin(Game *game)
Enters the GAME_PHASE_MOVEMENT phase, can only be called in GAME_PHASE_SETUP_DONE.
Definition: movement.c:11
int current_player_index
A negative value means that there is no current player selected. Use game_set_current_player for sett...
Definition: game.h:256
PossibleSteps calculate_penguin_possible_moves(const Game *game, Coords start)
Definition: movement.h:64
int players_count
Use game_set_players_count for setting this.
Definition: game.h:251
MovementError validate_movement(const Game *game, Coords start, Coords target, Coords *fail)
Definition: movement.c:76
MovementError validate_movement_start(const Game *game, Coords start)
Definition: movement.c:58
void movement_end(Game *game)
Exits the GAME_PHASE_MOVEMENT phase and switches to GAME_PHASE_SETUP_DONE.
Definition: movement.c:20
Player * game_get_player(const Game *self, int idx)
Returns a pointer to the player at the given index. Fails if the index isn't within the bounds of the...
Definition: game.h:394
void undo_move_penguin(Game *game)
Removes a GameLogMovement entry from the log and undoes it.
Definition: movement.c:142
GameLogEntry * game_push_log_entry(Game *self, GameLogEntryType type)
Creates a GameLogEntry, sets its GameLogEntry::type, pushes it on top of the stack (reallocating the ...
Definition: game.c:137
Holds the data of the players of the Game.
Definition: game.h:68
short id
The unique ID, usually (but not necessarily) is just index + 1.
Definition: game.h:70
int moves_count
The number of moves (penguin placements and movements) made by the player.
Definition: game.h:81
Coords * penguins
The list of the positions of all of the player's penguins.
Definition: game.h:79
int points
The score of the player, i.e. the number of collected fish.
Definition: game.h:74
int penguins_count
The length of the penguins array.
Definition: game.h:76
Exists purely to wrap an array of the numbers of steps in every possible Direction.
Definition: movement.h:35
GameLogMovement movement
Definition: game.h:162
@ DIRECTION_MAX
Definition: utils.h:25