Game

The Game class should handle all the logic related to one particular "session" of the game. This applies if the game is a persistent MMO that is one long session, or if it's a much shorter game with higher turnover.

The game's initial state should be set up in the constructor. Recall the constructor receives a list of QueueEntry objects, specifying the players and their stakes. For our game we simply copy the staked balances in. This isn't necessary but is the most flexible and safe default start for handling the points.

module{
  public class Game(queue: [T.QueueEntry]) = {

    var balances = M.HashMap<Principal, Nat> (10, Principal.equal, Principal.hash);
    
    let XEntry = queue[0];
    balances.put(XEntry.player, XEntry.amount);
    let OEntry = queue[1];
    balances.put(OEntry.player, OEntry.amount);
 ...
 }

The stakes can be handled based on the scoring method used by the game. They may be added as initial points, currency for upgrades, or ignored completely if the game is winner-takes-all.

In most cases a timer will be started to keep track of in-game time, and for turn timeouts. This code creates a 1-second turn clock:

    var myStatus : T.GameStatus = #Active;
    var gameTime : Nat = 0;
    var timerID : Nat = 0;
    func gameTick() : async () {
      gameTime += 1;
      await mainLoop();
      if (gameTime > 10000) {
        myStatus := #Complete;
        Timer.cancelTimer(timerID);
      }
    };
    timerID := Timer.recurringTimer(#seconds (1), gameTick);

The input method called by the router does the checks on whose turn it is, and whether the placement is valid:

    public func place(player: Principal, x: Nat, y: Nat) : async(T.TurnResult) {
      if (turn == #X and player != XEntry.player) {
        return #NotYourTurn;
      };
      if (turn == #O and player != OEntry.player) {
        return #NotYourTurn;
      };
      ...
    }

Last updated