How to make Deep Sea Adventure (a board game) with React, HTML, CSS

Taylor Coon
9 min readMar 22, 2020

You will have to forgive my lack-luster design skills. By the end of this blog, you should feel confident that you could make this game as well.

Here is what the entire model looks like on the developer’s side of things:

If you’re feeling up to it I would encourage you to look over the code and follow along :

There are a few things I want to achieve with this blog.

  1. I want to demonstrate how classic data structures can be used to simulate just about anything. If you’ve ever thought to yourself, “Yea this is interesting but when or why am I ever going to use it?” This is for you.
  2. I want to teach people the basics of making something real with programming. Something that you can see and interact with.
  3. Reading about programming is not enough for me. I need to actually make the stuff to truly understand it. So this blog also serves as a solidarity tool for my own learning.

So let’s jump right in.

This is the board game we are making

https://boardgamegeek.com/boardgame/169654/deep-sea-adventure

The stack I’m going to use is React.js, CSS, HTML.

The data structures I will build to model the program is a doubly-linked list and a circular linked list. These items will be carefully tangled together to represent the entire game.

We have a submarine with 25 air, up to 6 players, and 32 positions.

Each player holds tokens, has a score and is either escaped or not. The player’s travel direction is either down or up and it can only change once per round.

Once a player picks up a position, that token goes into the player’s inventory and the token is removed from the board, leaving an empty space that can no longer be looted but must still be walked through for opposing players.

When a player moves he/she cannot land on the same spot as another player and when he/she encounters another player they will skip over the opposing player without any loss of movement.

At the beginning of each player’s turn air is removed for everybody depending on how many tokens the player has. Once the air reaches zero, the current player finishes his/her turn and the round ends.

If a player makes it back to the submarine after grabbing tokens, they are then escaped where they wait for the round to end.

At the end of the round, any players that have not escaped will drown and must drop all of their tokens. Only players who pick up tokens and escape are allowed to score.

The next round begins with all players alive again and ready to dive for more treasure, but any of the 32 positions that are empty will be removed from the game so that the deeper treasure is more accessible. The value of the tokens on average gets higher, the deeper the player goes so there is an incentive to go deeper.

At the end of the third round, the player with the highest score is the one that wins.

One of the most popular ways to represent abstract things in programming languages is with objects and properties. This is one of the first things we learn in Reduction theory.

Considering that programmatical objects are cheap to make in terms of computing power and space complexity, we can repeatedly make them far and beyond whatever our use case is, and if we apply little tweaks and rules to how they work, we can represent even colossal systems either through inductive or recursive methodology.

We know, that these game pieces are made up of billions of atoms or molecules, but it would be unfeasible to represent all of those little physical things programmatically with our current computing power, so when we want to simulate things we reduce the items down to their most functional or relevant parts.

For example, if we wanted to keep track of or simulate all of the doors in a hospital programmatically, we don’t need the computer to know about the state of every little atom in the door. Instead, we only pass information to the computer that is relevant for whatever application we are making.

This is how we could define a grouping of door objects programmatically.

class Door {constructor(){this.open = truethis.hinges = "present"this.material = "wood"}}

This way we can make a bunch of different doors, millions or billions of doors even that each keep track of some core properties, like is it open or not? what is it made out of? From there we can build systems that do all sorts of things that would be useful in real life.

For example, we could attach some motors that close the doors automatically from a control panel, or we could track statistics about how many doors are open on an average daily basis and how that correlates with the energy bill.

Using this same logic, we are going to represent our 32 positions and our 6 players like this:

class Player {constructor(){this.nextPlayer = null || Playerthis.direction = "down"this.tokens = 0this.score = 0this.finalScore = 0this.color = "blue"this.escape = falsethis.position = null || Position}}class Position {constructor(){this.value = 0this.empty = falsethis.player = null || Playerthis.down = null || Positionthis.up = null || Position}}

Now let’s talk about how our data structures are going to come into play. Going into intimate detail about how to build these data structures would take too long. I would suggest you read my other blog posts about doubly circular linked lists and singly-linked lists if you get confused.

In short, you would programmatically assemble these structures using a simple for loop or while loop of some kind. Every programming language has looping abilities.

You’ll notice that our players have a “nextplayer” property and our positions have a “down” and “up” property. These properties are going to be pointing at the next relevant element of our game.

Since our players are going to be taking their turns in a cyclic sequence a circular linked-list is ideal. Each player object’s “nextPlayer” property will simply reference the next player object. It will look like this:

Now let’s move onto our board representation. Since players will be able to move in two directions on our board, a doubly-linked list is ideal for this application.

The position’s “down” property will point at the adjacent position beneath it, and the position’s “up” property will point at the adjacent position above it.

It will look like this:

Now you’ll notice that each position object has a “player” property and each player has a “position” property. That’s because these players will be on the positions during the game and it will be very convenient to be able to access one or the other programmatically. As the players move through the board our model will look like something like this:

Once we have modeled our player cycle and board like this using objects, it becomes almost trivial to work out all of the functionality of the game. When a player is headed “down” we simply need to roll the dice, and call player.position = player.position.down as many times as the numerical value that the dice returned.

When a player’s player.position.up call returns “undefined” we know we have reached the top of the board and the player has now escaped.

When the player’s player.position.down call returns “undefined” we know the player has reached the bottom of the board and must turn around whether they like it or not.

This model is a graph of sorts. It’s a circular, singly-linked list, that has been carefully attached to a doubly-linked list. The references can be carefully moved around or changed depending on what the players are doing, and it can be done programmatically, meaning you can build functions (or maybe even a classic, decision tree AI) that automatically carries out elements of the game.

Well there you have it. On a very high level, this is how I have implemented the game on my website as depicted in my video.

After modeling the game in this way, all I had to do was make a function for my board object that generated an array of React components that would be used to update State whenever a function was completed that needed to update the render.

I did the same for the player cycle object, and viola. My game was functioning as it should and it was rendering on the page as it should. Then I put in a couple of buttons to represent the decision switches at each point that would call my functions on click.

But before I conclude, I want to go over how to solve a particular hurdle in this game implementation.

Players cannot land on the same spot! When they pass over each other they get to jump over the opposing player without penalty. In order to do that I used dancing links.

Dancing Links

If you’ve read my other blog, you’ll know that dancing links is this nifty little trick where if you remove items from a doubly-linked-list and then save the deleted item in some other data structure, you can use the existing references to reassemble the doubly circular linked list to its original composition.

This remains true regardless of how many deletions you make from the list. As long as you reassemble the list in the exact same inverse order as your deletion sequence, the list will be perfectly restored.

The “skipping” mechanic could be handled in a hundred different ways. One popular method is to recursively check if the position has a player, but my friend challenged me to use dancing links in a practical way so here we are.

For this demonstration, Let’s assume we are playing a four-player game and the current board state looks like this:

When a player begins their turn execution, I loop over my player cycle and access each opposing player’s position in sequence and remove it from the board.

During the process, I store each deleted position in an ordered list.

After that, my board state looks like this :

By removing my other players from the board, I don’t need to do any complicated recursive checks or whatever else in order to execute the move of my player 1.

If my player 1 rolled a 3 they would land on the position marked with the light blue 3, since my reference logic would make the skips explicit.

After the move is executed I would reassemble my board in inverse sequence using my ordered list of deleted positions. Finally, I call my function that updates my React component rendering array and my player 1’s new position is correctly represented on the screen.

Well. That’s all I have time for today folks. Until next time.

I plan to keep publishing games on my website until I get noticed by people who want to hire me so expect more to come.

--

--