-
Notifications
You must be signed in to change notification settings - Fork 2
Creating a New Variant
Warning: THIS PAGE IS A WORK IN PROGRESS
This guide is for creating a new variant from scratch. Often it can be quicker to inherit from or copy an existing variant, but this is a good exercise to understand the interface between a variant and the framework. If you'd prefer to look at code, please check out example PR (TODO: add PR), which implements Tic-Tac-Toe.
Check out the README for instructions on how to set up the development environment. At a high level, this involves:
- Downloading the source with git
- Pulling in dependencies with yarn
- Installing MongoDB
We will use the new-variant script in shared:
yarn workspace @ogfcommunity/variants-shared run new-variant
This will generate two files:
-
shared/src/variants/your_variant.ts
- This is the code file where you will implement all the logic for your game.
-
shared/src/variants/__tests__/your_variant.test.ts
- This is where you can add tests for the variant. Adding tests can be a good way to verify that your code works as expected, and it can help prevent breakages in the future.
// Initialize state of the game for a given config
constructor(config?: object) {
// AbstractGame will set this.config to the defaultConfig() if config is undefined.
// Therefore, we access config through this.config in the rest of this function
super(config);
// Validate the config before trying to build an object.
// Setup the empty board state and other variables
this.board = new Grid<Color>(this.config.width, this.config.height).fill(null);
}
Variants may have a variety of player orderings. For example, Chess and Go have 2 players who alternate turns. In contrast, F-Go allows both players to play at the same time. Other variants allow for more than two players.
In the govariants codebase, it's possible to customize these behaviors using a few different functions.
numPlayers()
specifies the number of players that are allowed to join the game. This number should not change throught the course of the game.
override numPlayers(): number {
return 2;
}
nextToPlay()
function that returns an Array
of players who may submit a move in the current round.
override nextToPlay(): number[] {
// Return empty array if game is over.
// You can check the phase property of AbstractGame
if (this.phase === "gameover") {
return [];
}
return [this.next_to_play];
}
In playMove()
, you can run some validation on the player, and throw an Error if the incorrect player has moved. You will also need to update state related to the player order.
// Validate move and update the state accordingly
override playMove(player: number, move: string): void {
// ...resignation and timeout handling...
// Validate the current player by comparing to a member variable
// that is tracking the active player
if (player !== this.next_to_play) {
throw new Error(`Not ${player}'s turn!`);
}
// ...process move...
// Switch players
this.next_to_play = player === 0 ? 1 : 0;
// It is important to increase the round in order for game playback
// and timers to work
super.increaseRound();
}