I recently decided to make a game after a company sent me a coding challenge based around the same concept (which I never actually ended up completing). The challenge was to build an API based around the game Battleship. I thought it would be fun to turn it into a front end project, which is what I ended up doing.
To start, I decided that I would use a 6×6 grid, wherein each piece would take up a single point on said grid. This makes the game go a bit faster, while also making the project a bit easier to code for. I also had a few goals I wanted to achieve for this game. I wanted it to be completely responsive, available offline, installable on a user’s device, and for the code to be as simple as possible. That said, I’m happy to have accomplished all of those goals. The frontend (there’s no backend at all) uses no transpilation, preprocessing, or build tools. The UI is fully responsive, available offline, and installable thanks to TLS, a service worker, and a manifest.json file.
While there is a lot happening behind the scenes in order to create the game, there were a few particularly challenging aspects that I think would be interesting to share here.
Challenge #1 – Creating a random set of coordinates for the computer game board
Since we’re playing against the computer, we’ll need it to come up with 6 unique and non-repeating pairs of numbers ranging from 1 to 6 ([1,1], [2,2], [3,3], etc). In order to do this I wrote a recursive function that generates a random set and compares it against any existing sets, pushing the new set to the existing collection, or discarding it if it already exists. Here’s what that looks like:
First off we have an empty array called usedCoords, which will store the coordinate pairs as they are generated. Next, we have another empty array which will store the value of the coordinate pairs converted to a string, which will make them easier to test against when we want to ensure that our computer gameboard doesn’t have any duplicate coordinates(This is because [2,2] === [2,2] returns false while [2,2].toString() === [2,2].toString() returns true). returnNewCoords is used to generate an array containing 2 random numbers from 1 – 6, and returnAvailableCoords is a recursive function which returns a new coordinate pair or itself if the pair has been used already. The final part of this function is a loop through our internal state which assigns each of the computer generated coordinate pairs to a corresponding vessel object.
Collecting coordinates from the user is a little bit less challenging, which we accomplish by storing the user’s input on 12 individual input elements, and matching those inputs against a collection of DOM elements with corresponding data attributes (user chose 1,2 as a coordinate pair, so we add a game piece to the div with a data attribute that matches data-coord-point=”1-2″).
Challenge #2 – Pretending that the computer needs time to guess
This is one that surprised me, but none the less I realized that the game isn’t fun to play if the computer makes a guess immediately after you’re done guessing yourself. Instead, the computer needs to be personified so that the game doesn’t feel to mechanical. Translation: it needs to feel like the computer is “thinking”. Even though it takes the computer a few milliseconds to come up with a coordinate guess, we need to wait to show that guess to the user until a few seconds have passed.
As I slowly began to realize, the whole game needs a bit of artificial delay in order to feel more real. I decided to keep it simple, using setTimeout to create the delay after each specific action. You’ll find quite a few of these throughout the codebase.
Challenge #3 – CSS
While the design for this game is minimal (non-existent would be a better term) a unique challenge presented itself in the form of having to create a responsive checkerboard. My initial strategy was to display each checker as an inline-block item with a percentage based width, but that quickly failed after the screen size got smaller. Eventually, I figured out that using a flexbox based layout, combined with a padding top percentage value to create the height of each item, would allow each checker to scale proportionately no matter the screen size. As for the tiled backgrounds, I used the nth-child selector to give every other tile a purple background instead of an orange one. I’d love to say that I used some magical formula to calculate what the nth-child block should be to make it look perfect, but it really was just an hour or so of frustrated googling combined with trial and error until it looked right. You can see it all working here:
The next feature I’d like to add to this game is to enable the browser to remember the user’s game state if they navigate away from the page or accidentally refresh. This would be accomplished by saving a serialized state object to the browser localStorage whenever the state gets updated, followed by checking for that state object on page load and using that object as the new state. Additionally, the ability to auto-fill the user’s board at the click of a button would create a better experience in the case where a user doesn’t feel like filling out a form every time they want to play.