Battleships with 2D arrays
Learning intentions
In this lesson, you will build a simple Battleships-style game using 2D arrays.
- Create and update a 2D grid of cells,
- place ships using row and column indexes,
- and process guesses safely without index errors.
You will also practise decomposing the game into functions.
- Write helpers for printing the board,
- and use parameters and return values to keep logic reusable.
Game overview
Battleships is a guessing game on a grid.
- One grid stores your hidden ships,
- another grid stores the player’s guesses,
- and each guess reveals a hit or a miss.
We will use two 2D arrays of String symbols:
"~"for empty water,"S"for a ship,"X"for a hit,"O"for a miss.
Step 1: Create the grid
Start with a 6 by 6 grid of water.
- A 2D array is an array of rows,
- so each row is an array of symbols.
let size = 6
var ocean = Array(repeating: Array(repeating: "~", count: size), count: size)
var guesses = Array(repeating: Array(repeating: "~", count: size), count: size)
Step 2: Place ships
For a starter version, place ships manually so you can test easily.
- A ship is a symbol stored in the grid,
- so you just assign a value at a row and column.
ocean[1][3] = "S"
ocean[2][3] = "S"
ocean[4][0] = "S"
ocean[5][4] = "S"
Step 3: Print a grid for the player
The player should only see their guesses, not the ships.
- Print row and column labels to help choose coordinates,
- and display one symbol per cell.
Suggested function signature (adjust as needed):
/// Parameter:
/// - board: The 2D grid to display.
func printBoard(_ board: [[String]])
Step 4: Check a guess safely
When the player enters a row and column, you need to validate and update.
- Use guard checks to avoid out-of-range indexes,
- print a message and return the updated guesses grid.
Suggested function signature (adjust as needed):
/// Parameters:
/// - row: The row index for the guess.
/// - col: The column index for the guess.
/// - ocean: The hidden ships grid.
/// - guesses: The player's current guesses grid.
///
/// Returns: The updated guesses grid after the guess is applied.
func processGuess(row: Int, col: Int, ocean: [[String]], guesses: [[String]]) -> [[String]]
Step 5: Count remaining ships
Track when the game ends by counting ships.
- Each hit reduces the number of ships left,
- and you stop when it reaches zero.
Suggested function signature (adjust as needed):
/// Parameters:
/// - ocean: The hidden ships grid.
/// - guesses: The player's current guesses grid.
///
/// Returns: How many ships remain unhit.
func remainingShips(in ocean: [[String]], guesses: [[String]]) -> Int
Task A
Create a 6 by 6 ocean grid and a 6 by 6 guesses grid.
Place 4 ships at different coordinates.
Print the empty guesses board using printBoard.
Task B
Write a loop that allows the player to make 5 guesses.
For each guess:
- Ask for row and column input.
- Call
processGuess. - Save the updated guesses grid (the function prints the message).
- Print the guesses board.
Task C
Add a win condition.
Requirements:
- After each guess, compute the remaining ships with
remainingShips. - If the result is
0, print a victory message and end the loop early. - If the loop ends with ships remaining, print a “Game over” message.
Task D
Write a helper called randomShipPlacement that returns a new ocean grid with 4 ships.
Suggested function signature (adjust as needed):
/// Parameters:
/// - size: The width and height of the square grid.
/// - shipCount: How many ships to place.
///
/// Returns: A new ocean grid with ships placed.
func randomShipPlacement(size: Int, shipCount: Int) -> [[String]]
Requirements:
- Ships must not overlap.
- The function should return the updated grid instead of using
inout. - Test by printing the ocean grid once at the start (for debugging), then comment that line out.
Extension for Super Players!
Add one or more improvements.
- Let the player choose the grid size.
- Add ship sizes (length 2 or 3) instead of only single cells.
- and create a
displayOceanfunction that shows ships for a “reveal” mode at the end.
Summary
Battleships is a great practice project for 2D arrays.
- You store ships in one grid and guesses in another,
- use row and column indexes to access cells,
- and write functions to keep the logic clean and safe.
Review
Check your understanding with these prompts.
- Why do we use two separate grids?
- What does
processGuessreturn and why? - How do you keep your guesses grid updated without
inout? - and how do you avoid index-out-of-range errors?