Battleship — The Game (Step1: UserInterface and game logic)
In my continuous learning journey, I set a goal to learn Node.js and specifically how to use sockets, and because I wanted to build something more than just a chatting app, I decided to build one of my favorite games “Battleship”.
I will use this article to explain the thought process I went through to build the game and an attached recording will show you a demo of my solution.
Battleship (also Battleships or Sea Battle) is a strategy type guessing game for two players. It is played on ruled…
Above is a link to the Wikipedia article, explaining the strategy game, rules, and the setup for you who aren't familiar with the game.
First things first… Where to start?
What are the main components of our game?
Player grid: a square grid for each player representing the battleground. The player will have the freedom to place their ships either horizontally or vertically wherever it pleases them within the grid.
Every little square in that grid represents a slot for the ships, so a five slot ship occupies five squares, and once hit once, the whole square will be marked.
Opponent grid: same as the player grid, the opponent has his own. In our case, the opponent will be the computer. This means our solution will have to cover the opponent’s functionalities.
Ships: for simplicity ships are rectangles of different sizes, 2, 3, 4, and 5 squares. We need to be able to drag and change the direction of the ships.
Score and turn: a small header to display info, like who’s turn it is and what is the current score.
My HTML page is simple, I have two main parts, one for the ships’ container (containing all my ships before I place them on the board) and the board where I have the grids displaying the game information.
You can see in the screenshot how the container is a DIV element containing five different DIV elements, one for each ship.
And also every ship depending on its type and size contains DIV elements with ids.
My CSS is very simple also, I am using it to set the dimensions (width and height) of my elements.
and I also use CSS to set background colors for different divs depends on what those divs represent.
below is a screenshot showing a sample of my CSS file.
Where all the magic happens!! kidding there is no magic here, only logic and code.
Two main things are happening in my script:
first, finish rendering the interface. Second, set up listeners for the different functionalities.
I created this method to help me render the 100 square Divs in my Grid div.
it accepts as parameters grid: the parent Div where I want my grid to be rendered.
squares: an array where I am keeping a copy of my squares for programmatic purposes.
And width: an integer value for the width of my square in case I decided to change it at some point.
The method then loops through width*width creating a new div element every time and assigning an id to it.
Then it appends that element to the parent grid and to the array to keep track of it.
This method gets called twice, once passing the player grid and array and once passing the opponent’s grid and array.
The method “generate” accepts dir: an array with the possible directions of a ship, horizontal and vertical in our case.
ship: a specific ship that will be placed at a random position in the grid.
squares: an array with the grid squares’ in question.
“generate” does pick a random direction and uses the ship in that direction to check for a random start that all the squares are available and within the borders of the board.
if so it marks the squares by adding classes ‘taken’, ‘<ship.name>’, and ’ship’ to the square div otherwise it calls itself again to choose another random position.
A method called “rotate” accepts ‘ship’ as a parameter — a ship is a div element representing one of the 5 ships in the game — and it rotates it by toggling the name of the ship-vertical class name.
The rotate function is called on click event on any ship while it is still in the ships’ container.
Another functionality needed is to allow the player to manually place his own ships on his board. For that, I created a method called “dragDrop” which is called on drop event of a ship.
“dragDrop” accepts as parameters
e: the event object referencing the div receiving the drop, and other data related to the drop event.
target: an object holding info about the ship that is being dropped, its direction, and its length.
squares: the array holding the player’s grid information where we are placing the ships.
container: which is the ship’s container so we can remove the ship in question once it is placed.
once called the method figure the ship, the ship size, the receiving square in the grid, the start and the end of the dropped ship.
knowing the direction of the ship the method makes sure that no square where the ship should be placed is already taken and that the ship fits the grid div.
if so it places it and removes it from the container, otherwise the ship appears back in the container and no changes happen in the grid.
Until now we covered the automated random ship placement and the manual one, in the following I will show you the function responsible for running the game. If you guys are interested in the other functions used for reset and drag/drop events you can check the code in my GitHub repo below.
“playGame” function accepts as parameters game: an object that keeps a record of the score, and players’ turn.
turnsDisplay: the div where we display whose turn it is.
guestGrid: which is the div for the opponent grid.
the function checks whose turn it is, and updates the turnsDisplay text content correspondingly same as making the opponent grid clickable or not correspondingly.
In our case, and because the opponent is the computer, the method reveals a random square in the player’s grid when it is the opponent's turn.
if the revealed square was taken, which means has a ship in it, a red X appears there, else, a gray background appears there.
The game is over once all the ships are fully hit.
As you can see, it was pretty simple to implement the game once we learned what it is and what is needed to make it happen. Always take a problem and divide it into smaller problems.
Next, I will make it possible to play the game with a human opponent. This means multiplayer, which means two computers communicating the game updates step by step.
I will be using Node.js and Sockets, so feel free to follow me and stay tuned.