Game logic

v3 · Preview

Framework v3 is an opt-in preview available to everyone. New games use the stable framework by default.

Hidden information

Boardweaver hides from each seat what it may not see. Put each secret where the rules below hide it. The game logic never filters a view itself.

Who sees what

Part Seen by
a piece's public_kind and public_state everyone
a piece's private_kind and private_state the piece's owner; if it has none, its space's owner; if neither, nobody
a space's public_state everyone
a space's private_state the space's owner
a player's public_state everyone
a player's private_state that player
meta_data everyone

A spectator sees only public parts. Keep a secret that nobody may see yet, such as the order of a deck, as a private part with no owner.

To show a card only to its holder, put it face down in a space the holder owns:

table.add_space(
    Space::new(format!("hand-{player}"), "hand")
        .collection()
        .owned_by(player)
        .with_pieces(vec![Piece::new("back").hiding("queen-of-hearts")]),
);

To turn a card face up for everyone, set its public kind: table.set_public_kind(space_id, index, &face).

Collections hide order

A space is Ordered by default: every viewer sees every piece in its true position, without the private parts that viewer may not see.

A space made with .collection() shows each viewer the pieces it can see first, in order. The hidden pieces follow, grouped by their public part. A viewer cannot follow a hidden piece through a collection. So a hand of face-down cards gives nothing away when its holder sorts or plays it.

A click on a hidden piece

A seat that clicks a hidden piece in a collection cannot tell that piece from the others of the same public part. Boardweaver picks one piece of that group, which the seat cannot predict. The game logic receives the index of the picked piece, never the clicked position.

Reveals

A step is a reveal when its result depends on something the seat cannot see: drawing the top card of a deck, taking a card from another hand, looking at a hidden tile. Mark its offer with .revealing(). Hexbloom's draw is a reveal:

Offer::new(Action::command(DRAW), Intent::Confirm)
    .label(msg!("offer.draw"))
    .revealing()

A reveal ends the seat's decision (Offers, intents and decisions). The board shows its result only after Boardweaver applies it. Without the mark, the client can show the player a result that then changes.

The game logic also runs on a view

offers and apply_action also receive the seat's own view, where every private part the seat may not see is missing. Write them so they work on a view:

  • Read private parts with Option handling, not expect. On another seat's hidden piece, private_kind is None.
  • Read private maps with types whose fields all have defaults. A hidden map reads as {}.
  • Mark every step whose result needs a missing part as a reveal. Boardweaver then applies it with every part present.

Log lines and labels

  • Every viewer reads every log line, so write only public facts in a line (Changing the state).
  • Only the seat that receives an offer sees its label, so a label may name that seat's own hidden cards.

Need a hand with this?

Join our Discord