Architecture: the Rules Engine
You might hear us talk about the “Rules Engine”. We realized that this term could be confusing to developers coming from the video game industry who might think we are talking about the “Game Engine”. The Rules Engine is something very different.
The Game Engine: the traditional definition
The Game Engine include the Rendering Engine, the Audio Engine, the Physics Engine, the Artificial Intelligence tools, and other similar middleware such as animation modules, 2D and 3D libraries, authoring tools, etc. Unity 3D is a good example. A very long list of Game Engines can be found on Wikipedia.
The Rules Engine: the tabletop games situation
Board games and card games have this special situation that they are governed by strict rules. The gameplay is very precisely defined by the rulebook of the original physical game, and it is important to comply with these rules.
Over the past 15 years, we have found that a specific architecture works well to address this constraint - especially in order to implement the numerous game modes that players now expect: solo, pass-and-play, local play, online play, etc.
The basic idea goes like this: there should be a separate part of the code in charge of controlling the rules and managing the game data. This code is collectively called “the Rules Engine”. Its features are:
- It should be a self-contained, stand-alone piece of code that can be run completely independently of any user interface or os-specific environment.
- It should be covered by automated unit and integration tests (which you can do only if you followed the previous item).
- It should provide an abstract layer to handle incoming actions and send outgoing data. This allows “connecting” or “switching” players through the same communication channels, whether they are a human player, an AI, a Game State replay list or an automated test, and whether they are local or remote.
- It should be self-defensive, i.e. control the validity of all incoming actions and refuse illegal ones. It’s not the job of the User Interface (UI) to be responsible for game constraints decisions (like “can I play this card now?”). The UI needs to ask the question to the Rules Engine.
- The Rules Engine should be able to construct its internal data by reading a Game State constructed as a list of actions - see the Event Sourcing explanation in the documentation. Likewise, it should be able to build a Game State using this pattern.
- While reading a Game State, the Rules Engine should control its validity by running the events through the rules controls, and therefore detect and reject any Game State that would be illegal (either because of a bug or because of tampering).
- The internal game data should never be accessible directly from outside the Rules Engine.
Because all the actions and queries use the same abstract and common paths, it becomes easy to switch a player with an AI (“robot hot-swap”) or to use different communication medias: code on the same device (Solo and Pass-and-Play modes) or network channel (LAN or Online gaming) or even a mix of all these.
With such an architecture, it becomes easier to develop the various game modes one after the other, so that you don’t chew too much at the same time. We recommend starting by the Pass-and-Play mode with Unit Tests and a very basic UI. This allows you to develop and test your Rules Engine and define your Game State events.
Once it works, you can develop a more sophisticated and nicer UI.
After that, you can move on developing an AI, which plugs itself to the same calls as the UI to perform actions or read information about the game. In order words, the AI cannot cheat, it gets the exact same privileges as a human player. And don’t forget to unit-test your AI too! Because the Rules Engine verifies everything, it becomes easy to detect a bug causing an illegal move performed by the AI. This way, your AI tests can focus more on its “smart” decisions.
Once the IA is finished, you can move to Online gaming. Normally at that point it’s about getting a Game State from the server and giving it to the Rules Engine to construct the Game Data. Every time the player performs an action (“commit”), get the Game State from the Rules Engine and send it to the server. Of course you will have to manage all the other server interactions, such as dealing with the Player Clock, implementing robot hot-swap, etc. Refer to the documentation of the Scalable Server for more details.