penguins  1.0.0
bot.h File Reference

Go to the source code of this file.

Detailed Description

The bot algorithm.

See also
autonomous.h

Definition in file bot.h.

Data Structures

struct  BotParameters
 Various parameters for the bot algorithm. More...
 
struct  BotMove
 A penguin -> target pair. More...
 
struct  FillSpan
 Used internally by flood_fill. More...
 
struct  BotState
 Contains temporary data created during the evaluation of bot's moves. More...
 

Enumerations

enum  BotPlacementStrategy { BOT_PLACEMENT_SMART , BOT_PLACEMENT_RANDOM , BOT_PLACEMENT_FIRST_POSSIBLE , BOT_PLACEMENT_MOST_FISH }
 Values of BotParameters::placement_strategy. More...
 
enum  BotMovementStrategy { BOT_MOVEMENT_SMART , BOT_MOVEMENT_RANDOM , BOT_MOVEMENT_FIRST_POSSIBLE }
 Values of BotParameters::movement_strategy. More...
 

Functions

void init_bot_parameters (BotParameters *self)
 Initializes all fields of the given BotParameters to default values. More...
 
BotStatebot_state_new (const BotParameters *params, Game *game, Rng *rng)
 Constructs a BotState (similarly game_new). More...
 
void bot_state_free (BotState *self)
 Recursively destroys a BotState and its substates (similarly to game_free). More...
 
BotStatebot_enter_substate (BotState *self)
 Allocates BotState::substate if necessary and returns it. More...
 
bool bot_compute_placement (BotState *self, Coords *out_target)
 Computes the best placement for the current player given the current game state. More...
 
int bot_rate_placement (BotState *self, Coords penguin)
 Assigns a score to the placement at the given coordinates. More...
 
bool bot_compute_move (BotState *self, Coords *out_penguin, Coords *out_target)
 Computes the best move for the current player given the current game state. More...
 
BotMovebot_generate_all_moves_list (BotState *self, int penguins_count, Coords *penguins, int *moves_count)
 Creates a list with all the possible moves (as penguin -> target pairs) of the provided penguins. More...
 
int * bot_rate_moves_list (BotState *self, int moves_count, BotMove *moves_list)
 Applies bot_rate_move to every move in the provided list and returns a pointer to the list of scores. More...
 
int bot_rate_move (BotState *self, BotMove move)
 The heart of the bot, assigns a score to the given move according to its usefulness. More...
 
bool bot_quick_junction_check (BotState *self, Coords coords)
 A precondition for junction checks to know if a more expensive flood fill test is necessary. More...
 
short * bot_flood_fill_reset_grid (BotState *self, short **fill_grid, size_t *fill_grid_cap)
 Allocates a grid for use in bot_flood_fill_count_fish and fills it with zeroes. More...
 
int bot_flood_fill_count_fish (BotState *self, short *grid, Coords start, short marker_value)
 Counts all fish accessible within an enclosed area starting at the given point using the flood fill algorithm. More...
 
void flood_fill (int x, int y, bool(*check)(int x, int y, void *data), void(*mark)(int x, int y, void *data), FillSpan *(*alloc_stack)(size_t capacity, void *data), void *data)
 An implementation of flood fill using the span filling algorithm. More...
 

Enumeration Type Documentation

◆ BotPlacementStrategy

Values of BotParameters::placement_strategy.

Enumerator
BOT_PLACEMENT_SMART 

The standard "smart" algorithm, see bot_compute_placement for its description.

BOT_PLACEMENT_RANDOM 

Pick a random tile for placement.

BOT_PLACEMENT_FIRST_POSSIBLE 

Pick the first possible tile (first tile in the first row).

BOT_PLACEMENT_MOST_FISH 

Pick the tiles with the most fish in the vicinity.

Definition at line 18 of file bot.h.

◆ BotMovementStrategy

Values of BotParameters::movement_strategy.

Enumerator
BOT_MOVEMENT_SMART 

The standard "smart" algorithm, see bot_compute_move for its description.

BOT_MOVEMENT_RANDOM 

Pick a random move.

BOT_MOVEMENT_FIRST_POSSIBLE 

Pick the first possible move (also known as the "dumb" algorithm).

Definition at line 31 of file bot.h.

Function Documentation

◆ init_bot_parameters()

void init_bot_parameters ( BotParameters self)

Initializes all fields of the given BotParameters to default values.

Definition at line 31 of file bot.c.

Referenced by GamePanel::GamePanel().

◆ bot_state_new()

BotState* bot_state_new ( const BotParameters params,
Game game,
Rng rng 
)

Constructs a BotState (similarly game_new).

Definition at line 42 of file bot.c.

Referenced by BotThread::BotThread().

◆ bot_state_free()

void bot_state_free ( BotState self)

Recursively destroys a BotState and its substates (similarly to game_free).

Definition at line 75 of file bot.c.

◆ bot_enter_substate()

BotState* bot_enter_substate ( BotState self)

Allocates BotState::substate if necessary and returns it.

Definition at line 94 of file bot.c.

◆ bot_compute_placement()

bool bot_compute_placement ( BotState self,
Coords out_target 
)

Computes the best placement for the current player given the current game state.

The algorithm for the placement phase is very simple:

  1. Generate a list of valid tiles for placement.
  2. Assign a score to every tile according to the rules in bot_rate_placement.
  3. Pick the tile with the highest score.
Returns
true if the function managed to find a placement, false if there are no possible placements for the player or if the computation was cancelled. The coordinates of the resulting tile are written to out_target.

Definition at line 143 of file bot.c.

Referenced by BotPlacementThread::Entry().

◆ bot_rate_placement()

int bot_rate_placement ( BotState self,
Coords  penguin 
)

Assigns a score to the placement at the given coordinates.

Evaluates the nearby tiles to determine how good the placement location is.

Definition at line 185 of file bot.c.

◆ bot_compute_move()

bool bot_compute_move ( BotState self,
Coords out_penguin,
Coords out_target 
)

Computes the best move for the current player given the current game state.

The overall algorithm is roughly the following:

  1. Generate a list of all possible moves with bot_generate_all_moves_list.
  2. Evaluate all moves in the list by calling bot_rate_moves_list.
  3. Assign a score to every move using bot_rate_move.
    1. Some basic rules are considered first, such as: collecting more fish means a higher score, longer moves mean a lower score.
    2. Then, rules for making the bot aggressive are applied: if the move resulted in blocking an opponent's penguin, its score is increased dramatically.
    3. The scores of obviously stupid moves (such as blocking yourself with your own penguin) are reduced significantly so as to not take them seriously.
    4. And lastly, the most important part of the algorithm takes place: the move is applied to the Game instance, and the algorithm is repeated recursively from step 1 (generate a list of all moves available from there, assign scores to each and so on and so on). The best next move is selected and its score is added to the score of the currently considered move – this allows the bot to see future opportunities, including locating and blocking other penguins, which makes it REALLY aggressive (sometimes, though rarely, it even can discover combos). Afterwards, the applied move is undone.
  4. Finally, the move with the highest is selected and returned.

Note that when evaluating moves recursively, the provided Game is modified instead of creating a copy of it each time. However, recursive evaluation currently has a significant limitation – it only considers sequences of moves of a single penguin, and doesn't take the opponents' moves into account at all (doing otherwise consumes too much computing time).

Returns
true if a move was found, false if there were no moves available for the player or if the computation was cancelled. If found, the move is written to out_penguin and out_target.

The aggressive strategy proved to be very effective against other bots, which are usually passive (meaning that they simply try to collect fish) - it first blocks all other players and then collects the rest of the fish. It is even somewhat effective against humans because its aggressiveness essentially relies on the opponent making a mistake, which is not an unreasonable assumption when playing against a human (especially myself). Although, consequently, the stupidest bots, which are simply making literally the first available move after scanning the board (this is what the BOT_MOVEMENT_FIRST_POSSIBLE strategy does), ended up being the algorithm's fatal weakness (well, not exactly fatal, it can still win against those) – primarily because they are very persistent at just moving in a single direction.

Definition at line 295 of file bot.c.

Referenced by BotMovementThread::Entry().

◆ bot_generate_all_moves_list()

BotMove* bot_generate_all_moves_list ( BotState self,
int  penguins_count,
Coords penguins,
int *  moves_count 
)

Creates a list with all the possible moves (as penguin -> target pairs) of the provided penguins.

Returns
A pointer to the resulting list, whose length is written to the moves_count argument.

Definition at line 330 of file bot.c.

◆ bot_rate_moves_list()

int* bot_rate_moves_list ( BotState self,
int  moves_count,
BotMove moves_list 
)

Applies bot_rate_move to every move in the provided list and returns a pointer to the list of scores.

Definition at line 369 of file bot.c.

◆ bot_rate_move()

int bot_rate_move ( BotState self,
BotMove  move 
)

The heart of the bot, assigns a score to the given move according to its usefulness.

In case a cancellation is requested (see BotState::cancelled), this function starts unwinding the recursive calls and returns some garbage score, but otherwise the move computation is stopped.

Definition at line 433 of file bot.c.

◆ bot_quick_junction_check()

bool bot_quick_junction_check ( BotState self,
Coords  coords 
)

A precondition for junction checks to know if a more expensive flood fill test is necessary.

A "junction" is a state when then bot has to choose between two or more directions because once it makes a move the other paths will become inaccessible. This function only performs a quick heuristic check which can only tell if the current tile is definitely not a junction, so it is used to know if a further more time-consuming flood fill test is necessary.

Definition at line 565 of file bot.c.

◆ bot_flood_fill_reset_grid()

short* bot_flood_fill_reset_grid ( BotState self,
short **  fill_grid,
size_t *  fill_grid_cap 
)

Allocates a grid for use in bot_flood_fill_count_fish and fills it with zeroes.

Definition at line 620 of file bot.c.

◆ bot_flood_fill_count_fish()

int bot_flood_fill_count_fish ( BotState self,
short *  grid,
Coords  start,
short  marker_value 
)

Counts all fish accessible within an enclosed area starting at the given point using the flood fill algorithm.

Returns the number of reachable fish, the counted tiles are marked on the provided fill grid with marker_value, which must be non-zero. Additionally is used for junction checks.

See also
flood_fill

Definition at line 677 of file bot.c.

◆ flood_fill()

void flood_fill ( int  x,
int  y,
bool(*)(int x, int y, void *data)  check,
void(*)(int x, int y, void *data)  mark,
FillSpan *(*)(size_t capacity, void *data)  alloc_stack,
void *  data 
)

An implementation of flood fill using the span filling algorithm.

This is essentially the algorithm behind the "bucket" tool in paint programs. The code was pretty much copied (and translated from pseudocode) from Wikipedia.

Parameters
xthe starting point
ythe starting point
checka function that returns true if the given cell should be marked
marka function that marks the given cell
alloc_stacka function that allocates that stack for FillSpan s
dataextra data to be passed into the provided functions
See also
https://en.wikipedia.org/wiki/Flood_fill#Span_Filling
https://github.com/erich666/GraphicsGems/blob/c3263439c281da62df4a559ec8164cf8c9eb88ca/gems/SeedFill.c

Definition at line 725 of file bot.c.

Referenced by BotState::bot_flood_fill_count_fish().