I’ve completed a touch table version of the board game Notre Dame. In Notre Dame, players add influence to different sections of their city by playing action cards. Some sections give the player additional influence or money which can be used later while others are better for generating the prestige needed to win the game.
There were several interesting things about implementing this game that may be useful in future projects.
The action cards are private, and while I probably could have just put them on the touch table, I used React to build a web page to display them and other decisions. I didn’t do anything new with the web interface for this game.
I also kept the “State” design pattern from Broom Service. When the game sends something to a web client, it keeps track of that message to easily handle client disconnects and other errors.
By reusing a lot of the web code and design from Broom Service I saved quite a bit of effort.
In Notre Dame, it is common for players to act semi-simultaneously. The game is technically turn based, and each player is supposed to act in order; but in practice there is not a lot of interaction between the players and people take advantage of that to speed up the game by overlapping their turns.
I didn’t want the touch table version to slow down play by making everyone wait for their turn, so I allow players to act out of turn when possible. The game still keeps track of the real turn order and enforces it when the player’s actions might affect others.
Accepting actions out of order didn’t really make the game logic more complex, but it did complicate undo. Our normal event system uses a global stack of events (actions and game flow events). Undo removes the last action and re-executes the whole stack. But with player’s acting out of order, a player may want to undo their action, possibly without even noticing that another player or players have taken actions. To allow this, I created a per-player undo button. I implemented it in a similar way to Caverna. The player undo finds the last action taken by the player and removes it from the list of events, then re-executes all the events. The difficult part was separating the cases where that would cause problems and turning off the undo button in those situations.
In the last few games, I’ve been experimenting with different ways to model the game state. In this game I went all-in on state modeling and implemented a per-player state machine. The player model has a stack of PlayerState classes to track what state the player is in (ie: played action card, selecting new district for friend. Or hired personality, selected garage, moving car). Each state class in the stack knows which know which parts of the player interface to enable/disable and can handle player input. The state classes update the game model when the players take actions and tell the GUI what to animate.
The state classes basically replace the functionality that used to live in the events. Now the events just ask the current state to handle the input. So all game logic is still done within an event execution, but it is the state doing the work instead of the event.
This design worked quite well for this game. In Notre Dame, the state is fairly complex: like Broom Service, actions can trigger other actions. The stack never gets quite as deep as Broom service, but there are a much wider variety of actions. Another thing that made this design work well is that there are very few distinct player actions. Most of the time players are either selecting a worker or a region. The effect of that selection depends on the current state of the game, but having very few actions means that the states only have to have a couple functions for handling player actions.
Notre Dame ended up taking about 90 hours. The game doesn’t seem that complicated when you are playing it and I originally thought that it would be a 40 hour game. But when I read the rules with conversion in mind I realized that it was going to be a large project.
Adding to the complexity, I implemented two expansions for the game that add more “personality cards”. Many of these cards end up being special cases with custom code.
I’m pretty happy with this conversion. The simultaneous play keeps the game moving, and I don’t have to worry about other players making a mistake on their turn when I’m not paying attention.
I’m also happy with the web interface and state machine designs that I used in this game and hope that they will be useful in the future.