Engine Written from scratch in TypeScript
Concept Peg Solitaire-style game which players must jump over pegs to clear the game board.
As part of a programming challenge, I was tasked with recreating a peg solitaire game. Peg solitaire is a game you’ve probably played before:
- You must jump each peg over another peg, but only if there is an open space.
- Each peg you jump over must be removed from the game.
- You win if only one peg is left.
Given the time constraint of three days, and self-assigning the task of building the game from scratch, I’m very happy with the result! I tackled a lot of technical challenges that probably weren’t really meant to be touched on, but ultimately it was a very fulfilling learning experience. The game itself is enjoyable, too, and I feel I made the right choice in secondary mechanics.
Outside of the standard peg solitaire rules outlined above and the ‘peg jump’ mechanic, I added a handful of secondary mechanics to the game. These mechanics work with helping/hindering board clearing and affording the user more points, which in turn leads the user to play differently than they might play normal peg solitaire.
The ‘engine’ for the game was written from scratch in TypeScript, which was a fun challenge to tackle. I wanted to build the entire experience within canvas, simply more as a challenge to myself than anything. Prior to this game, I had done a bit of canvas work (see: demos from other blog posts), so I felt prepared for the most part. At the end of the project, the only third-party package used in the code was for a psuedo-random number generator, which was pretty cool.
VCR’s and the Rendering Pipeline
VCR (or Virtual Canvas Renderer) is a primary component in the game engine. Essentially, VCR’s are responsible for creating off-screen canvas instances and holding them in memory. Game objects (such as the score or time displays) each have their own personal VCR, dedicated to drawing whatever is needed for that instance. The game timer, for instance, has a VCR with which it prints text to on each tick of the game clock.
Another component in the system, the
RenderingPipeline, handles printing each game object to the main output canvas. Each game object simply passes the image of their VCR to the pipeline’s rendering context with appropriate positioning, reducing draw calls while keeping the stage as up to date as possible.
An interesting feature of using VCR’s in this manner is that it’s quite trivial to set up a cached render (or a flyweight-esque pattern). For instance, the Peg game objects all pretty much look the same, so there’s no need to redraw each one individually on each frame. By using a shared/static VCR, the visual display for each peg is rendered once and then simply re-printed during the draw call. This optimization was actually a huge boon to performance early on in development.
VCR’s also offer support for double buffering, though that feature isn’t used in this game.
Having only a few days to put this game together, I decided to simply pass the asset loading off to the browser. The game only has a few assets to load: a couple webfonts, and a few sounds. Most of these files are not mission critical to the player, either, which is great; I didn’t need to hold the game up to load assets.
Ideally, the Asset Manager would handle listening for
onload events and all that, showing progress until the game is sufficiently loaded. For the purposes of getting the game out the door on time, I simply cut the ‘preloading’ corner, but in the future I won’t be so lucky.