The game functions
A game is a type that implements the Game trait. Write all five functions. They take no self, so the game keeps nothing between calls: everything lives in the state.
pub trait Game {
fn setup(table: &mut Table, players: &[PlayerId], rng: &mut Rng);
fn offers(table: &Table, player_id: PlayerId) -> Vec<Offer>;
fn apply_action(
table: &mut Table,
player_id: PlayerId,
action: &Action,
rng: &mut Rng,
) -> Result<(), Message>;
fn scores(table: &Table) -> Scores;
fn is_game_over(table: &Table, scores: &Scores) -> bool;
}
Write pub struct MyGame;, then impl Game for MyGame { ... } with the five functions, then export_game! (below). Every new game starts as Hexbloom, a complete game. To read its game functions, call get_example with name: "hexbloom" and path: "/src/lib.rs".
setup
Builds a new match: add spaces and pieces, write maps, shuffle and deal, and choose who starts with set_active_players. Write the opening's log lines with table.log when players should see them.
- Boardweaver seats
playersin seat order beforesetupruns. Every seat is active untilsetupcallsset_active_players. setupcannot refuse. Make it work for every seat count the game allows.
offers
Returns every offer player_id may take now, each with its intent and label. See Offers, intents and decisions.
- Boardweaver calls it only for seats in
active_player_ids, and never onceis_game_overreturnstrue. - It also runs on the seat's view, where hidden parts are missing (Hidden information).
- Return an empty list when the seat has nothing to do.
apply_action
Applies one step. Before the call, Boardweaver checks that the game offered this action to this seat, and that a command's value fits its input. Handle only the actions your offers returns.
- Return
Ok(())after the change. - Return
Err(reason)to refuse a step by a rule the offer cannot express, such as a city name another seat already took. Boardweaver discards every change the call made. - Write
reasonas a message from the game's catalogs:Err(msg!("refusal.city-taken", city = name)). The player sees it in their language. - A reason follows the rules of a label (Offers): it has no
{player}, and every seat it names must be seated. Otherwise the call panics. - Narrow the offers instead of refusing wherever you can.
- A panic is a bug. Boardweaver fails the step and records a match error (Builds, limits and errors).
Read a command's value with action.text() or action.integer(). Match a command by its name:
match action {
Action::Command { name, .. } if name == "pass" => {
table.log(msg!("log.passes"));
}
Action::PieceClick { space_id, index } => {
table.move_piece(space_id, *index, "discard");
}
_ => {}
}
scores
Returns every seat's score. Boardweaver calls it after every step.
pub struct Score {
pub private_points: Vec<f64>,
pub public_points: Vec<f64>,
}
- Column
iof a seat's score ispublic_points[i] + private_points[i]. A missing column counts as 0. Name each column in the client: see Scores. - Seats rank by the first column, highest first. The second column breaks a tie on the first, and so on.
- A seat sees every seat's public points and only its own private points. Use private points for a score other seats must not see yet, such as a hidden objective.
is_game_over
Returns whether the match has ended. It receives the scores that scores just returned. Once it returns true, Boardweaver offers nothing to any seat and refuses every later step as NotOffered.
export_game!
The last line of /src/lib.rs exports the game: boardweaver_sdk::export_game!(MyGame);. To ship bots, list them: export_game!(MyGame, bots: [Greedy, Careful]) (Bots). For a game without bots, leave the list out: the build refuses an empty list, bots: [].