Skip to content

Commit

Permalink
message to, action debug, more test runner
Browse files Browse the repository at this point in the history
  • Loading branch information
aghull committed Mar 28, 2024
1 parent ff20d46 commit 25ec988
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 26 deletions.
19 changes: 19 additions & 0 deletions docs/game/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,25 @@ We can also add more `{{handlebars}}` variables using the second argument, e.g.:
)
```

To message only a specific player or players, use the
[`messageTo`](../api/classes/Action#messageto) method instead. This is important
since the messages may include information that is otherwise invisible to some
players. E.g.:

```ts
.messageTo(
player, "You drew {{card}}"
).messageTo(
player.others(), "{{player}} drew a card"
)
```

Besides chaining the `message`/`messageTo` on to the actions, these can also be
called at any point using [`game.message`](../api/classes/Game#message) and
[`game.messageTo`](../api/classes/Game#messageto). This is useful if the rules
of the game generate messages outside of players taking specific actions. These
calls are the same but don't have any pre-supplied `player` or action arguments.

:::tip Using {{handlebars}}

Using Boardzilla's `{{handlebars}}` syntax in messages allows references to
Expand Down
19 changes: 17 additions & 2 deletions docs/game/board.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,9 @@ All elements are visible to all players by default. Often a game will require
that pieces are visible only to some players and hidden from others. In
Boardzilla, "hiding" a Game Element means that the properties of that element
are no longer visible. For example if one of our example `Card` instances was
flipped over, the player would be able to see that it was an instance of the
`Card` class, but `card.suit` and `card.number` would be `undefined`.
flipped over, the player would be able to see only that it was an instance of
the `Card` class, but it's properties, like `name`, `suit` and `number` or any
others would be `undefined`.

This can be accomplished in a number of ways, the simplest being
[`hideFromAll`](../api/classes/GameElement#hidefromall). There are many [other
Expand All @@ -253,6 +254,20 @@ properties with the static method
Card.revealWhenHidden("deck");
```

Players can also have invisible properties, such as hidden roles. To make a
property of your Player class invisible to other players, simple call the static
method [`Player#hide`](../api/classes/Player#hide). This property will be
undefined on other players when seen from a specific player's perspective
(i.e. in an action choice or in UI code).

```ts
class MyPlayer extends Player<MyGame, MyPlayer> {
secretRole: 'normie' | 'killer';
}

MyPlayer.hide('secretRole');
```

## Movement

Pieces can be created in any Space or on the game itself. They can then move
Expand Down
100 changes: 76 additions & 24 deletions docs/introduction/debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,56 +8,108 @@ Boardzilla provides many tools for the all-important task of testing your game
code, and troubleshooting issues that might arise. You should be familiar with
these tools as you develop so you can constantly check your work.

## Debug overlay

While working in devtools, a debug overlay is provided by clicking on the
magnifying glass icon in the upper left. This provides access to a breakdown of
the current state of the game.

<img src="/img/debug-overlay.png" style="width: 100%"/>

On the left half on the overlay is a snapshot of the flow defined in the game,
showing the current block highlighted in yellow, and any variables populated in
the colored headers of the flow block that provided them. See
[Flow](../game/flow.md) for a full understanding of what's in here. One thing to
note is that flow is defined once when the game is created and never changes, so
the structure here is always the same. Only the current position and the
populated variables change during game play. That also means if this structure
looks wrong, there is a problem in the flow definition that shouldbe resolved
first.

On the right half is a snapshot of the current actions available to the viewing
player. Actions are further broken down by the choices on each action that are
presented to the player in order for them to complete the action. If an action
in unavailable or one it's choices has been skipped, this view will explain
why. See [Actions](../game/actions.md) for more information on these.

## Test runner

Boardzilla includes a test runner class that mocks the playing environment of a
server plus a prescribed number of players all sending actions to the game so
you can set up automated tests that run through example games and test
assertions on the game state.

The test runner exposes both the player versions of the game and the server
version of the game which was complete knowledge of all hidden
information. It's important when testing player moves to use the correct version
for the given player.

```ts
import { TestRunner } from "@boardzilla/core";
import setup from '../src/game/index.js';

const runner = new TestRunner(setup);

const [player1, player2] = runner.start({ players: 2, settings: {} });
const [ui1, ui2] = runner.start({ players: 2, settings: {} });
```

If you need to create mock components that do not exist in the game already, You
can add additional setup when you create the test runner as a 2nd argument:

```ts
runner = new TestRunner(setup, game => {
game.create(Card, 'some-custom-card');
});
```

The test runner exposes both the player versions of the game and the server
version of the game which was complete knowledge of all hidden
information. It's important when testing player moves to use the correct version
for the given player.

// example move
player1.move("takeCard", { card: player1.game.first(Card) });
```ts
// example move from player 1's perspective
ui1.move("takeCard", { card: ui1.game.first(Card) });

// example assertion on server game
// example assertion on the game accessing a property that may be hidden
console.assert(runner.server.game.someProperty === "some-value");

// example assertion on player actions
console.assert(player1.actions().length === 0);
console.assert(ui1.actions().length === 0);
```

You can import the test runner and set up tests using the testing library of
your choice. The starter game includes an [example working test
suite](https://github.com/boardzilla/boardzilla-starter-game/tree/main/test)
using `vitest`.
When you call `runner.start` the players are updated with the game state. You
can inspect the player view and even manipulate the game state from their
perspective by using `ui1.game`. This is equivalent to a player looking at the
board and attempting a move, e.g.:

### Manipulating data in the test runner
It's often useful to be able to manipulate data within the test runner to
set up specific scenarios. Because you're in the test runner rather than inside
the game's logic, some things are not in the same locations.
```ts
ui1.player.first(Card)?.putInto(ui1.game.first('discard')!);
ui1.player.first('discard')!.first(Card) // => the Card just discarded
```

You can perform actions with `ui1.move` and test the results. After calling
`ui1.move` the players states are updated to reflect the results of the move.

```ts
// if you want to manipulate anything about the game from the server's pespective use `runner.server.game`
const top3cards = runner.server.game.first('drawPile')?.firstN(3, Card);
ui1.move("takeCard", { card: ui1.game.first(Card) });
ui1.allMy(Card); // => includes the Card just drawn
```

// if you want to manipulate anything from a player perspective, use `player1.player`
const player1hand = player1.player.my('hand')?.all();
You can also manipulate the state on the game using `runner.server.game`
as above and test the results. This is equivalent to the flow of the game making
an update, e.g.

// putting the above 2 together
player1.player.my('hand')?.first(Card)!.putInto(runner.server.game.first('discard')!);
```ts
const top3cards = runner.server.game.first('drawPile')?.firstN(3, Card);
runner.server.game.first('drawPile')!.all(Card).length // => 3
```

However these changes are not automatically propagated to the players
for viewing and cannot be immediately tested from a players perspective. You
must call `runner.updatePlayers()` in order to update the players view of the
game with whatever changes you have made.

You can import the test runner and set up tests using the testing library of
your choice. The token starter game includes an [example working test
suite](https://github.com/boardzilla/boardzilla-starter-game/tree/main/test)
using `vitest`.

## Browser developer tools

Boardzilla outputs some debug info about the current state of the game and the
Expand Down
Binary file added static/img/debug-overlay.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/img/element-inspect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 25ec988

Please sign in to comment.