Building a ludo variation in JS

Edward Heaver
6 min readMay 13, 2020

Building a Ludo game in JS with a Rails backend

Before I start speaking through my idea, I want to first plug a piece of software that has really helped me with the planning process and tracking progress on this project. Notion is a productivity app that tracks your notes and tasks in a workspace. It’s designed with a great variety of use cases in mind and seems to have an answer for virtually any scenario. I had started using this at work extensively to keep track of the different tasks I was engaged in during this project and found it much easier to do inside Notion. I also used it as a knowledge base, making it easy to find the answers to questions I’d already researched. I started translating this into this project and it worked really well, addressing a key problem I’ve had with all of my projects previously. I’d often find that the work needed to put a project together would increase far beyond what I had initially planned. While this project was no different in that, it was much easier to track down where I was losing time based on the Kanban style approach I took to allocating the tasks I needed to do.

Notion made this extremely easy to manage and allows for a variety of different layouts, so you can choose what suits you. You can even type markdown directly into the app and it works great. I’d say if you’re a dev who is working out how best to manage your productivity, give Notion a look. Now, enough evangelising other people’s software. Let’s talk about this JS project.

As part of my study with Flatiron, we were tasked to create a website with a Ruby on Rails backend and a Javascript front end. Working with separate front and back ends can sometimes pose a slight issue when it comes to handling communication. But, I found working with Javascript and Ruby in this way very easy. Before I speak about the way I handle the communication between the front and back end, I want to introduce my project idea. I wanted to make a multiplayer version of the game ludo. To do this, I created the following user stories:

User stories

  • Shmichal Stein wants to play Ludo with friends during lockdown
  • Alise Nolan wants to be able to see games she’s played before to see if she was just unlucky
  • Steven Schmitt doesn’t want to spend forever getting out of the starting area!
  • Steven Schmitt also wants to play a more strategic version of Ludo, without blockers

These user stories helped me to think about the features I wanted my app to have. Ultimately though, I was slightly too ambitious and haven’t really been able to implement the features in a way that satisfies each of these users. But, these have given me great learning points for the future and will allow me to truly develop my skills with Javascript as a programming language and working with a pubsub client.

Building the game

Building the game was a complicated exercise for me. At first, I thought it would be simple to capture a roll of the dice and apply it to a token on a board for a move. But, there were a variety of different states that I had to account for and built on the fly. One of the key pieces of logic I had to work out was how a piece interacted with another if it landed on the same space as another. In ludo, if you land on the same space as another piece, you send that piece home. Sounds simple enough, right? Well, making sure this behaviour only triggered on the player’s turn was a little tricky at first. Also, the rule should only to every other coloured piece and not the colour you are playing.

Needless to say, the logic of the game was a difficult point for me in this project and I’m not sure I’ll be pursuing a career in game development anytime soon.

The main focus of this project though was the use of a Rails API. I chose to create an API that would create and store the turns of a game being played and could show them at a later time. This was very simple to create and I encapsulated the logic for creating game turns inside of a games class, making the code very modular. I also used the fetch keyword to handle a variety of AJAX requests for account creation and creating game objects. Let’s look at the AJAX request I used to create a game object to be stored by the API.

const req_url = "<http://localhost:3000/>";function createGame(){

let formData = {};
formData["user_id"] = current_user();
let confObj = {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
},
body: JSON.stringify(formData)
}
let req_url = base_url + "games";
fetch(req_url, confObj).then((req)=>
req.json()).then(response => {
if(response.game_id){
sessionStorage["game_id"] = response.game_id;
} else {
sessionStorage.removeItem("game");
}

})}

This function has a number of aspects to it. The main focus is the creation of a game object by using the current_user function. The current_user function is a Javascript function that is really similar to the Devise current_user helper I implemented for roughly the same function. Because I was working in HTML that was outside of the back end Rails directory, I needed a way to ensure a user was logged in, and if they were, what user was actually logged in.

I use the current_user function through out my code to control app behaviour. As part of this function, I create a formData object to hold the information I’d like to submit for the AJAX request. I also set up a configuration object (confObj) to hold all the data for the AJAX request like the method and mode. The createGame function uses the HTTP verb POST as I send the current user’s ID held inside of current_user to create a game for that specific user. The API then serves this back to us and it’s captured in the response and saved within the sessionStorage too.

I utilise sessionStorage a lot in this project as a way to ensure information is accessible across different parts of the Javascript code. The use of arrow functions and the ‘then’ keyword in the fetch request make the process of capturing the game_id and saving it in the correct place very intuitive. I also validate the response with an if statement to ensure we get a return that is meaningful.

Now, let’s look at how the turn objects are generated.

createTurn = (colour, pawn, roll) => {
let formData = {};
formData["game_id"] = sessionStorage.getItem("game_id");
formData["colour"] = colour;
formData["pawn"] = pawn;
formData["roll"] = roll
let confObj = {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
},
body: JSON.stringify(formData)
}
let req_url = base_url + `games/${formData["game_id"]}/turns`;
fetch(req_url, confObj).then((req)=>
req.json()).then(response => {
console.log(response);

})}
}

Here we can see the use of sessionStorage to serve the game_id for creating the game’s turn. The function accepts the colour that has just rolled the dice, the piece that was most recently moved and the dice roll. These 3 things are passed into the function and saved within a formData object similar to the createGame function. A key difference here is in the request url (req_url). This is a nested route within the Rails API, so needs to have a specific route in order to create turns for the right game. Using back ticks and string interpolation, I was able to create the route that will update dynamically with what is present in the sessionStorage. Once the turn is created, there isn’t a need to use the turn in the game’s logic. This function sits within a turn class. The turn class is responsible for creating and reading instance’s of itself from the API. But, only one instance of the class is instantiated for use in the project’s code.

End result

The end result of this project was good. I learned a great deal in putting it together and have some great places to further my learning with Javascript. One thing that has been a struggle with Javascript throughout this project is the prototypal inheritance model Javascript uses. It means that working with classes is slightly different from other languages and can lead to seeing unexpected behaviour from class definitions. Also, the lack of protected or private properties in Javascript means that controlling game logic from a class solely is tricky. Nevertheless, I feel I accomplished the end goal of producing a working product, despite not satisfying the user stories I created at the project’s outset.

One final thing, Notion has a great tool called web clipper. It allows you to transpose pages from your browser into pages in the platform. These get produced with tags, a link to the original page and content. It works very well on stackoverflow and part of my planning process was implementing a ticket focused approach. Attaching individual tasks or problems to tickets and doing background research to preempt any road blocks. I found this approach worked extremely well and will definitely be implementing it in the future.

--

--

Edward Heaver

Backend Engineer with a passion for clean code and fun features