Battleship

User plays against an Ai in this interactable web app. Users can drag and drop their game pieces into the grid. The app detects if the user input is valid, when to begin the game, when a user has made a move, and when someone has won the game.

Technologies

CSS
HTML
JavaScript
SCSS
Figma
Git
GitHub

Intro

I always wanted to make a version of Battleship, so I did! This was done completely in vanilla JavaScript. This opened up some concepts that I never knew about.

Design

Board & Grid

The approach that I took in this project was to first create the HTML and CSS. Having the game pieces made would make creating the elements in JavaScript later a lot easier. There were a couple of things I knew I wanted to do right away:

  • Illusion of water moving underneath the game tiles
  • Make the game tiles look like foam

To make it seem like water was moving, I made a gradient for the background. The background had lighter parts and some contrasting colors to resemble the sun reflecting off the water. For the actual moving water part, I animated the positioning of the background to move. It took some fine tuning, but gave a real subtle effect once it was done. Who would've thought, it really seemed like there was water moving!

As for the tiles, I choose to use a transparent white to allow the gradient to seep through. Using CSS Grid made the formatting of the tiles very simple.

Ships

Creating the ships took advantage of the grid. Since a patrol boat was two long, we could have it span two grid columns or rows. This idea could then be applied to every ship variant. I then used another grid for the ship itself. This grid allowed me to space out the "pegs" evenly. Later on I would add a hitbox to sit on top of each ship. More on that later.

To display the user's ships, I created an area below the gameboards for them. This also contained buttons to rotate the ships and randomly place them.

JavaScript

I'm not going to cover every detail because a lot went into this. Here are the main things I had to figure out:

  • Grid detection
  • Ship placement
  • Game start and end detection
  • Random ship placement
  • AI tile choice
  • Score
  • Game replay

Grid Detection

The first obstacle was, "How the heck can I get the tiles to detect a ship being placed on them?" This led me to discover a particular set of JavaScript events: dragstart, dragend, dragover, and drop. We could add listeners to the cells for dragover and drop events.

To allow a drop, I needed to prevent the default behavior of the dragover event. The events were setup and I could move onto the next step.

Ship Placement

We can start adding the ships to the board. The main problems at this stage were:

  • Is the ship being placed out of the grid bounds
  • Is there a ship where the user is trying to place
  • How do we detect a vertical ship

All cells were added to an array when creating the grid. We'd use the index of the array to see if the move would be valid or not. When adding the ship, we'd update the values within the array to match the HTML representation of the game. Now, all that needed to happen was if any cell the ship would take up was anything other than empty the move became invalid.

Game Start and End Detection

Each time a ship was placed a method would be triggered. Now, each ship has a property called placed on it. This method would check to see if every ship had been placed using this property. If every ship had been placed a custom shipsPlaced event would be triggered.

The window had an event listener attached to it that listened for this specific event. When triggered, the game would start. For the end of the game, every turn a check was run to see if all the ships on either side were sunk. If one set of ships were sunk the game would end.

Random Ship Placement

Instead of laying out the ships themselves, a user could opt to randomly place their ships. We try to place every ship a use rhas, but first we needed to make sure a user didn't already place the ship. Next, I generated a random number, either 0 or 1, to simulate a coin toss. The result determined if a ship was rotated or not.

We then attempted to place the ship at randomly generated coordinates. I figured since the board was small it wouldn't be a bad idea to try and brute force it. Once the ship was placed, we'd perform a check to see if that was the last ship that needed placed. If it was we'd add the start game button.

AI tile choice

The AI would generate two random numbers within the grid range. This coordinate would then be checked. If the cell was empty or was an un-hit part of a ship, it'd be updated. If the cell was already interacted with before another loop would happen. We'd keep looping until it finally got a correct choice.

Score

Inside of a Game class, I kept track of the computer and player score. I also kept a reference to the HTML elements for the scores. When a winner was detected we updated the correct score then gave the new score to the HTML element.

Game Replay

After updating the score, a method runs that creates a prompt. This prompt has an event listener attached that resets the game. The game is reset by removing all the game elements and initializing the game properities over again.

Future Updates

  • Instructions on how to play
  • Smarter AI
  • Two Player