Building a Battleship Game in React
Last year I was at a junction in my personal life. I moved to Amsterdam from San Francisco, and switched careers after twelve years working as a strategist and product manager in London and San Francisco. I took up coding about six months months ago, and learning React about two months ago.
In order to learn React, I made a React battleship game from scratch. The programming community has been helpful and welcoming; I had some positive feedback and feature suggestions at the end, but I also benefited from reading many "how I built this" posts. This entry is my attempt to give back and share my experience with others on that path - though there are no shortcuts here.
Why a React game?
Primarily to learn the technology, beyond a few simple tutorials and courses. I intend to get a job working with React or similar front-end frameworks.
Last month I attended Women in React, a conference where several React fans and experts admitted they hadn't thought of React as a game development tool or used it in that way much. After all, it's a UI library primarily aimed at single page apps or progressive web apps. Games are a lesser-explored application, particularly as early adopter interest is going towards WebAssembly. Nonetheless, it's not impossible to make good or great ones, like this example for how to make Steam games with React or local play ones, like draw.wtf.
The internet at large suggested making a game because it's complex but also fun - it doubles as something fun to talk about in a future interview, and people are more likely to click around and explore functionality.
I was initially skeptical because I don't gravitate towards games or game development (many people's gateway drug to programming), but went with the wisdom of the crowd. And I have to admit it's been really cute to see friends and strangers have fun with it and offer helpful feedback along the way.
All in all, it took me about a month, but that's because I had other stuff going on in my life, like learning Dutch. But it turns out learning a new language at the same time as learning a programming one helps; recent research shows they activate similar brain regions, so it was welcome stimulation.
Game history and motivation
I chose to build this particular game because my dad taught me how to play the pen and paper version when I was young, and I have fond memories of the many hours we spent sinking each others’ ships to pass the time. We were in different countries during lockdown, and I wanted to remind him of something we both enjoyed as I was growing up.
At its heart, battleship is a naval war-themed strategy game. You and your opponent are competing navy commanders at sea. You have a fleet of ships positioned at secret coordinates on a grid, and take turns firing torpedoes at each other. The goal is to be the first to sink the other person’s whole fleet. Many variations have spawned since them, but the core remains the same.
The Game Development Document
Getting started is the hardest part. The blank page, the endless possibilities, the what ifs and coulds, the cool things you'd like to make but will later realize are too hard for your level, and so on.
I'd never made a game but thought a lot about game mechanics since my gamer days as a teenager. I loved the early research part, reading about game development planning. The most useful thing I learned was to make a 'game development document', much like you'd write a business plan. It helps clear the path for what will come later. For a day or so I was in my (old) element planning and strategizing, but I didn't want to dwell on it too much.
I decided a few things early on:
- The game would be played against a computer AI as opposed to player-versus player
- The AI had to be intelligent but not brutal;
- A structure for the component hierarchy, and having a welcome screen before starting play.
Everything else was more or less a given or decided during development, since battleship has been played with pen and paper since at least World War I, according to Wikipedia. In some countries there are are mild variations in terms of ship size, name and number and whether the ships can touch or not, but I knew the 'Western' version, where ships can touch, and went with that.
Building: Structuring React projects and Design Patterns
The first lesson was on how to structure larger-scale React projects than I'd done until then - both in terms of file structure as well as separating the logic from the view layer.
There's little online about patterns like MVC these, at least in the way they pertain to React projects. There are some older posts and a book from Addy Osmani, which were helpful in this respect.
I followed a pattern I'd used before, with a main component to handled most state-related logic, a document for utils and reusable functions (e.g. generating the board, ensuring ship placement within the squares available without overflowing, etc.), and the rest of the "view" components mostly concerned with displaying things and handling interactions.
The hardest part was 'thinking in components' and wondering early on if I could or should recycle things like board component for both player and computer, but later decided against it once it became obvious they only had so much in common.
In the end, I used create-react-app, functional components and hooks all throughout for the first time. It was a smooth transition, and would now find it hard to go back to the old way; class components and component lifecycle functions (shouldComponentUpdate, etc.)
This may be the least exciting part. I didn't use any CSS library, or any specific CSS-in-JS approach. I did loosely follow design principles from the Refactoring UI book, which I read before this project and learned a lot from. There's more to improve in this department, but the book gave a good foundation on how to balance colors.
For color, I used Open Color, an open source color scheme with lots of nice colors, shades and hues. This removed the pressure of having to come up with custom colors.
Most of my early screenshots are ugly, since I polished the CSS last. I want to explore this area more in the future, but this game didn't lend itself to that.
Game logic and core React principles
Most of the game’s logic came out of using (or failing to use and then correcting) React's first principles, like unidirectional data flow. Although I'd done this before, it took me a while to internalize the concept that the view (board) is a data structure (array) that gets modified based on information from state, collected as players interact with the app.
I stung myself a few times making the beginner mistake and forgetting that setState is asynchronous; this became an issue when every click on the board queues up a lot of actions and checks behind the scenes: is the game over? Were any ships sunk? Is it time to change turn?
The computer AI
There are some brutal battleship AIs out there, and I learned a lot from a very deep insight piece that ran some numbers on different algorithms.
Implementing it was not the hardest technical challenge, but it intrigued developers and non-developers who played it: it's what I call semi-intelligent, but you can still lose to it if you play non-ironically.
As a casual player, if you want to sink a ship, you kinda fire at random until you hit something. Then you fire around that, and keep going in one direction until you sink a ship. My implementation does something similar, but it doesn't home in on a ship once it hit two successive squares.
There's a much harder game made by someone else that you can play, called Battleboat, where the computer fires in a certain pattern to ensure maximum savageness. Perhaps one day I'll add difficulty levels, but my goal was to have a casual game you wouldn't get too upset with (to begin with).
The boring but interesting parts
Other than figuring out the logic, the most fun part were the helper functions, most of them used during ship placement on the board.
There are functions to convert cartesian coordinates to indices, rotate ship orientation on mouse click, several to check if a ship will fit or if you're trying to place it over another entity, etc. These were the most fun to write, alongside the component that makes little replica ships to display in the buttons, to match the number of squares the real ships have. Of all things, that's the most trivial thing but also the one that pleased me the most.
Having practiced on CodeWars before, these almost wrote themselves, for most part. I'd recommend that alongside watching other people code, but from my experience so far doing it yourself beats watching others.
Staying sane and healthy
I'd be amiss not to mention that I did not pull all-nighters or burned the candle on both ends to make this. It's proven time and time again that you don't learn if you're tired and that you can get maybe a handful of productive hours per day, but I see a lot of new developers think the opposite, and encourage bizarre practices.
It took me a month to finish while also getting a foreign language diploma in parallel. There were moments when I really wanted to code after the language lessons but it just didn't work. Nope, nuh-uh. Brain said no.
Learning is hard because it's uncomfortable, and if it's not hard then you're probably not learning. I wish I'd done it faster sometimes, but not at the expense of my own sanity - especially because I made this during the coronavirus pandemic.
What can you do?
Read Cal Newport's 'Deep Work' (or summaries of it) for different approaches on how to structure your day and remove digital distractions to achieve deep focus for several hours per day. Unlike other time management gurus, he's a computer scientist and professor. Even then, you might get 4-5 truly useful hours in a day.
Focus the rest of your time on eating well, sleeping well, seeing friends and family, doing things for others, not getting your blood pressure up with the news, focusing too much on what others think, and things that aren't the project that will move you forward in your learning or career (you know, like tinkering with your IDE all day).
If you start projects but never finish them, don't blame yourself for procrastination, or think it's a personality trait, or a result of poor time management skills. In some cases it can be about anxiety or fear of failure, but far more often it suggests you're not doing things that excite you, or matter to you. Investigate those avenues first.
Reflection and next steps for me and the game
Is React fun? Once you get into the groove and internalize how React works and have a workflow based on a mental model, it's very developer and beginner-friendly, and smooth sailing from there onwards. Hooks and functional components made that a lot easier, and it's hard to go back to regular class usage. It enjoys a lot of popular support and developments, at least for now, and the documentation can be helpful to beginners.
Would I make another one? Not in the near future because I'd rather focus on stuff that uses APIs to progress towards paid work. But do I want to return to bring new functionality to this, e.g. multiplayer via Pusher.
What's next? A few more projects to help me get closer to a paid job, hopefully. I'm playing around with Next.js for a self-hosted portfolio page in the near term.
Part of the switch to engineering a few months ago meant aligning my actions with my values, so I hope to find companies that care about the same things.
Hiring? I’d be glad to talk and get to know each other if you're looking to hire, particularly in Amsterdam or Netherlands, and even US remote. From prior experience in San Francisco with a fully distributed team, this can work out well.