Ahoy again, Code Cadets! Today, you level up from mere grid builders to full-on shipwrights and naval strategists. Welcome to Part II: Launching the Fleet, where you’ll learn how to build, track, and eventually sink entire ships — not just single spots on the board.
That’s right — we’re done with single-cell ships. Your fleet is growing up. From tiny patrol boats to mighty carriers, each ship you place will now span multiple squares and live its life as a real object in code: with a name, a size, and a list of its hidden coordinates.
This is how real games keep track of ships — so you can finally do cool things like:
“🔥 You sunk my Destroyer!”
“🚫 That was a miss, try again!”
“💀 That was the last hit — the Cruiser is down!”
Let’s go from pixels to Python-powered warships.
✅ Create a new data structure called fleet to store each ship as a dictionary
✅ Each ship has:
a name (like "Destroyer")
a size (number of tiles it takes up)
a coords list of its position on the board
✅ Update your ship placement function to:
Ask the player for direction (horizontal or vertical)
Use loops to generate multiple tiles
Make sure no ships overlap or go off the edge
Add each new ship to the fleet list
✅ Bonus: Display ships on the board only in debug mode, so players can’t cheat!
Ahoy, Captain Cadet! Up until now, you’ve been working with simple single-tile ships. But the seas are getting dangerous, and you need a real fleet.
Today’s mission: place ships manually on your 10x10 grid like a real naval strategist. But this isn’t just about clicking a square — you’ll need to choose where the ship starts, how long it is, and whether it should be horizontal or vertical. Then we’ll show you a preview of your placement so you don’t accidentally ram into another ship or go flying off the edge of the board.
Ready to launch the first ship? Let’s get coding.
You will write a function that lets the player place a ship of a given size on the grid. It must follow three rules of the sea:
The ship must fit — no part of the ship can go off the edge of the board
It must go in a straight line — horizontal or vertical
It can’t overlap any other ship
Ask the player how big the ship is
Ask them to choose H (horizontal) or V (vertical)
Ask them to enter a starting coordinate, like “D4”
Use a loop to calculate all the spaces the ship would take
Show them a preview of that ship’s location on a temporary board
Let them say yes or no to confirm
If confirmed, place the ship on the real board and add it to your fleet list
Instead of just one big list of ship tiles, we now track each ship as its own dictionary in a list.
Now you know:
What the ship is
How Big It Is
Where Every part of it lives on the board
We have to make sure we define our list of ships to place as a dictionary. You can either hard code this as in the actual game, the ships have definite names and sizes which you can't change
Then, when you're ready to place ships, loop through the list and call:
for ship in ships_to_place:
place_a_ship(ship["name"], ship["size"])
NOTE: place_a_ship is a function we have to create!!
Prompt the player to place the ship horizontally (H) or vertically (v)
Make sure to check their response for validity! Must be H or V!!
Remember:
.strip() removes extra spaces
.upper() makes sure 'h' becomes 'H', etc.
⚓️ What You’re Doing:
Let the player type something like "D4" and convert that into the grid coordinate system.
In Battleship, "D4" = column 3, row 3 (because Python starts counting at 0).
YOU ALREADY HAVE A HELPER FUNCTION THAT YOU CREATED EARLIER IN THE GAME THAT CONVERTS A COORDINATE TO A GRID POSITION
x, y = get_coordinate()
Inside get_coordinate():
It extracts the letter ('D') and converts it to a column number using ord()
It grabs the number ('4') and subtracts 1 to get the row number
It returns (x, y) as the grid position
⚓️ What You’re Doing:
Now that we know:
the starting point (x, y)
the orientation (H or V)
the ship size
We need to calculate all the grid spaces the ship will take up.
Let's Explain This Below...
📌 new_ship_coords = []
We start with an empty list. This list will hold every coordinate that makes up the ship.
For example, if we’re placing a size 3 ship starting at position (2, 5) going horizontally, this list will eventually look like: [(2, 5), (3, 5), (4, 5)]
📌 if orientation == "H":
We're checking if the user chose Horizontal. If so, we want the ship to go across the row (left to right).
📌 else: # orientation == "V"
If the player picked Vertical ("V"), the ship goes down the column.
📌 for i in range(ship_size):
This is a loop that runs once for each part of the ship.
If the ship is size 3, this loop runs 3 times:
When i = 0
When i = 1
When i = 2
📌 coord = (x + i, y)
This is for Horizontal Placement
We’re building each part of the ship based on the loop number.
x + i means we move rightward across the row
y stays the same because we’re on the same row
🧭 Example:
Starting at (2, 5) and going horizontal with size 3:
i = 0 → (2, 5)
i = 1 → (3, 5)
i = 2 → (4, 5)
📌 coord = (x, y + i)
This is for Vertical Placement
This time:
x stays the same (same column)
y + i increases as we go downward through the rows
🧭 Example:
Starting at (4, 2) and going vertical with size 3:
i = 0 → (4, 2)
i = 1 → (4, 3)
i = 2 → (4, 4)
📌 new_ship_coords.append(coord)
Each time we calculate a new coordinate, we add it to our ship list. By the end of the loop, new_ship_coords will have all the ship’s locations!
This code says:
“Hey, depending on which way the ship is facing, figure out each square it will take up, and put those squares into a list.”
That list will later be used to:
Preview the ship placement
Make sure it fits on the board
Check for overlapping ships
Place the ship on the board once confirmed
Your ships may be mighty, but the ocean grid has boundaries. You can’t let part of a ship hang off the side of the board like a floating glitch in the Matrix.
If a ship starts too close to the edge, and its size would cause it to go beyond the last row or column, we need to stop the placement and try again.
🚫 Think of the grid like a dock with ten parking spots. You can’t park a limousine in slot #9 if it’s five spaces long — part of it would fall into the water!
Let’s say you want to place a 3-tile ship horizontally, starting at column 8.
You'd be trying to place:
(8, y)
(9, y)
(10, y) ❌ Uh oh! There is no column 10.
So we need to check first before placing.
x and y are the starting position on the grid.
ship_size is how many tiles long the ship is.
MAX_COLUMNS and MAX_ROWS are both 10 in a standard Battleship grid.
Examples:
Horizontal: So for a ship starting at x = 8, size = 3:
8 + 3 = 11 → ❌ Ship doesn't fit
Start at x = 3, size = 4
3 + 4 = 7 → ✅ Ship fits
Vertical Check: Start at y = 5, size = 4
5 + 4 = 9 → ✅ Ship fits
Start at y = 7, size = 4
7 + 4 = 11 → ❌ No room on the board!
You wouldn’t park your car on top of another car, would you? The same goes for ships in Battleship. Once a ship is placed on the board, its coordinates are off-limits to any other ship.
If a new ship tries to use a space that’s already taken, we have to cancel the placement and try again — otherwise, our fleet would become a pile-up of overlapping code bugs.
“Before placing the ship, go through every tile it would take. If even one of those tiles is already in use, stop and try again.”
⚓️ What You’re Doing:
You already created a list called new_ship_coords that contains every space the new ship wants to take.
Now, we check each one of those coordinates and ask:
❓ “Is this tile already taken by another ship?”
If the answer is yes, we raise the alarm and stop the ship from being placed.
Let’s say you already placed this ship:
Now the player tries to place another horizontal ship starting at (1, 0):
new_ship_coords = [(1, 0), (2, 0), (3, 0)]
You can see that yje mew ship overlaps two spaces: (1, 0) and (2, 0)
So during the loop:
You cancel the placement by either:
Using a raise ValueError("Collision detected")
Using return to exit the function early
Or using a while True: loop so the player just tries again
Before you officially launch your ship onto the board, wouldn't it be cool to see what it would look like first? That’s what this step is all about — we’ll show you a "ghost preview" of your ship using a temporary board before anything is locked in.
Why? Because in real naval strategy (and game dev!), testing your plan before committing helps avoid critical mistakes like crashing into another ship or drifting off the map.
Here’s how it works:
We make a copy of the real board.
We temporarily draw your ship as question marks ("?") so you can preview its placement.
You decide: Do you want to place it there? If not, we cancel and go back to try a new spot.
It’s like trying on an outfit before buying it — no commitment unless it fits and looks good.
🧱 Creating a Manual Board Copy
We create a copy of the board so we're not messing with our actual game board. There are other ways to do this (import copy and use deepcopy() but we're sticking to Good Ol Python Basics)
🧪 Preview the Ship on That Board
Now that we have a fresh preview board, we mark it with ? symbols where the new ship would go
🖨️ Show the Board and Ask the Player
print_board_with_border(preview_board)
This uses the function you created already
confirm = input("Do you want to place the ship here? (Y/N): ").strip().upper()
If they type Y, you’ll place the ship.
If they type N, you cancel and ask them for a new spot
🎉 Why This Is Awesome:
Think of it as Ghost Mode in a game — you see where something will go before committing. It’s safe, smart, and shows good coding practice.
It teaches players:
How to test before committing
The difference between a temporary state and a permanent state
How to give users control over their decisions
Welcome, Shipwright Commander! Before your vessel officially sets sail, you’re granted a supernatural ability — to preview your ship before placing it on the battlefield.
Why? Because even in high-seas warfare (and programming), mistakes like crashing into another ship or floating off the grid can sink your strategy. And let’s be real — nobody wants to be that captain.
Now that the player has previewed their ship and confirmed with a "Y", it’s time to:
Add the ship to the real board
Save each ship's coordinates to a master list (ship_locations)
Track the entire ship as a dictionary inside a list called fleet
If the player says “No”, nothing happens and they just try again.
Each ship is stored as a dictionary in a list called fleet.
This lets you later:
Loop through ships to check if one has been sunk
Print cool messages like “🔥 You sank the Battleship!”
🧠 This list helps track every spot that contains a ship — useful for checking hits later.
🧠 The full ship is saved with its name, size, and all positions it occupies.
Then, you simply start over — no placement is made, and the board remains unchanged.