// Arduino Nano Battleship
/*
Ships:
Carrier 5
Battleship 4
Cruiser 3
Submarine 3
Destroyer 2
*/
const byte BOARD_SIZE = 10;
const byte SHIP_COUNT = 5;
const char WATER = '.';
const char SHIP = 'S';
const char HIT = 'X';
const char MISS = 'o';
byte parsedColumn;
byte parsedRow;
struct Ship {
const char *name;
byte size;
};
Ship ships[SHIP_COUNT] = {
{ "Carrier", 5 },
{ "Battleship", 4 },
{ "Cruiser", 3 },
{ "Submarine", 3 },
{ "Destroyer", 2 }
};
char playerBoard[BOARD_SIZE][BOARD_SIZE];
char enemyBoard[BOARD_SIZE][BOARD_SIZE];
byte playerShipCells = 0;
byte enemyShipCells = 0;
byte playerShots = 0;
byte enemyShots = 0;
const byte HORIZONTAL = 0;
const byte VERTICAL = 1;
void setup() {
Serial.begin(115200);
randomSeed(analogRead(A0));
clearBoard(playerBoard);
clearBoard(enemyBoard);
Serial.println();
Serial.println(F("================================================"));
Serial.println(F(" ARDUINO BATTLESHIP"));
Serial.println(F("================================================"));
Serial.println(F("Ships: Carrier (5) Battleship (4) Cruiser (3)"));
Serial.println(F(" Submarine (3) Destroyer (2)"));
Serial.println(F("Place ships using grid coordinates (A1 to J10)"));
placePlayerShips();
placeEnemyShips();
Serial.println();
Serial.print(F("All ships are placed."));
printBoards();
}
void loop() {
if (playerShipCells == 0) {
Serial.println(F("\nYou lost! The enemy sank all your ships."));
while (true) {
delay(10); // Stop the game
}
}
if (enemyShipCells == 0) {
Serial.println(F("\nYou won! You sank all enemy ships."));
while (true) {
delay(10); // Stop the game
}
}
Serial.println();
Serial.print(F("Your turn. Enter a coordinate. "));
if (!readCoordinateFromSerial())
return;
byte column = parsedColumn;
byte row = parsedRow;
if (enemyBoard[row][column] == HIT || enemyBoard[row][column] == MISS) {
Serial.print(F("You already fired there."));
return;
}
playerShots++;
if (enemyBoard[row][column] == SHIP) {
enemyBoard[row][column] = HIT;
enemyShipCells--;
Serial.print(F("Hit at "));
printCoordinate(column, row);
Serial.print(F("!"));
} else {
enemyBoard[row][column] = MISS;
Serial.print(F("Miss at "));
printCoordinate(column, row);
Serial.print(F("."));
}
if (enemyShipCells == 0) {
printBoards();
Serial.println(F("\nCongratulations! You destroyed the enemy fleet."));
while (true) {
delay(10); // Stop the game
}
}
// printBoards(); // show enemy shot
enemyTurn();
if (playerShipCells == 0) {
printBoards();
Serial.println(F("\nThe enemy destroyed your fleet."));
while (true) {
delay(10); // Stop the game
}
}
}
void clearBoard(char board[BOARD_SIZE][BOARD_SIZE]) {
for (byte row = 0; row < BOARD_SIZE; row++)
for (byte column = 0; column < BOARD_SIZE; column++)
board[row][column] = WATER;
}
void printBoards() {
Serial.println();
Serial.println();
Serial.println(F(" FRIENDLY BOARD ENEMY BOARD"));
Serial.println(F(" A B C D E F G H I J A B C D E F G H I J"));
for (byte row = 0; row < BOARD_SIZE; row++) {
if (row < 9)
Serial.print(' ');
Serial.print(row + 1);
Serial.print(F(" "));
for (byte column = 0; column < BOARD_SIZE; column++) {
Serial.print(playerBoard[row][column]);
Serial.print(' ');
}
Serial.print(F(" "));
if (row < 9)
Serial.print(' ');
Serial.print(row + 1);
Serial.print(F(" "));
for (byte column = 0; column < BOARD_SIZE; column++) {
char cell = enemyBoard[row][column];
// Hide enemy ships
if (cell == SHIP)
cell = WATER;
Serial.print(cell);
Serial.print(' ');
}
Serial.println();
}
Serial.println(F("Legend: . = water, S = your ship, X = hit, o = miss"));
}
void placePlayerShips() {
Serial.println(F("For each ship, enter a starting coordinate."));
Serial.println(F("Then enter H for horizontal or V for vertical."));
for (byte shipNumber = 0; shipNumber < SHIP_COUNT; shipNumber++) {
bool placed = false;
while (!placed) {
Serial.println();
Serial.print(F("Place "));
Serial.print(ships[shipNumber].name);
Serial.print(F(" ("));
Serial.print(ships[shipNumber].size);
Serial.print(F(" cells). "));
Serial.print(F("Enter starting coordinate. "));
if (!readCoordinateFromSerial())
continue;
byte startColumn = parsedColumn;
byte startRow = parsedRow;
Serial.print(F("Enter direction H or V. "));
String direction = readLine();
direction.toUpperCase();
if (direction.length() == 0 || (direction[0] != 'H' && direction[0] != 'V')) {
Serial.println(F("Invalid direction. Use H or V."));
continue;
}
byte directionValue;
if (direction[0] == 'H') {
directionValue = HORIZONTAL;
} else {
directionValue = VERTICAL;
}
if (!canPlaceShip(
playerBoard,
startColumn,
startRow,
ships[shipNumber].size,
directionValue)) {
Serial.println(F("The ship does not fit there or overlaps another ship."));
continue;
}
placeShip(
playerBoard,
startColumn,
startRow,
ships[shipNumber].size,
directionValue);
playerShipCells += ships[shipNumber].size;
placed = true;
Serial.print(F("Ship placed."));
if (shipNumber < SHIP_COUNT - 1)
printBoards();
}
}
}
void placeEnemyShips() {
for (byte shipNumber = 0; shipNumber < SHIP_COUNT; shipNumber++) {
bool placed = false;
while (!placed) {
byte startColumn = random(0, BOARD_SIZE);
byte startRow = random(0, BOARD_SIZE);
byte direction = random(0, 2);
if (canPlaceShip(
enemyBoard,
startColumn,
startRow,
ships[shipNumber].size,
direction)) {
placeShip(
enemyBoard,
startColumn,
startRow,
ships[shipNumber].size,
direction);
enemyShipCells += ships[shipNumber].size;
placed = true;
}
}
}
}
bool canPlaceShip(
char board[BOARD_SIZE][BOARD_SIZE],
byte startColumn,
byte startRow,
byte shipSize,
byte direction) {
for (byte i = 0; i < shipSize; i++) {
int column = startColumn;
int row = startRow;
if (direction == HORIZONTAL) {
column += i;
} else {
row += i;
}
if (column < 0 || column >= BOARD_SIZE || row < 0 || row >= BOARD_SIZE) {
return false;
}
if (board[row][column] != WATER) {
return false;
}
}
return true;
}
void placeShip(
char board[BOARD_SIZE][BOARD_SIZE],
byte startColumn,
byte startRow,
byte shipSize,
byte direction) {
for (byte i = 0; i < shipSize; i++) {
byte column = startColumn;
byte row = startRow;
if (direction == HORIZONTAL) {
column += i;
} else {
row += i;
}
board[row][column] = SHIP;
}
}
void enemyTurn() {
byte column;
byte row;
do { // a smarter game would only try adjacent cells after a HIT
column = random(0, BOARD_SIZE);
row = random(0, BOARD_SIZE);
} while (
playerBoard[row][column] == HIT || playerBoard[row][column] == MISS);
enemyShots++;
Serial.println();
Serial.print(F("Enemy fires at "));
printCoordinate(column, row);
if (playerBoard[row][column] == SHIP) {
playerBoard[row][column] = HIT;
playerShipCells--;
Serial.print(F(". The enemy hit your ship!"));
} else {
playerBoard[row][column] = MISS;
Serial.print(F(". The enemy missed."));
}
printBoards();
}
// Serial input
String readLine() {
while (Serial.available() == 0) {
delay(10);
}
String line = Serial.readStringUntil('\n');
line.trim();
return line;
}
bool readCoordinateFromSerial() {
String coordinate = readLine();
coordinate.toUpperCase();
if (!parseCoordinate(coordinate, parsedColumn, parsedRow)) {
Serial.println(F("Invalid coordinate. Enter values such as A1 or J10."));
return false;
}
return true;
}
bool parseCoordinate(
String text,
byte &column,
byte &row) {
text.trim();
if (text.length() < 2) {
return false;
}
char columnCharacter = text.charAt(0);
if (columnCharacter < 'A' || columnCharacter > 'J') {
return false;
}
column = columnCharacter - 'A';
String rowText = text.substring(1);
int rowNumber = rowText.toInt();
if (rowNumber < 1 || rowNumber > 10) {
return false;
}
row = rowNumber - 1;
return true;
}
void printCoordinate(byte column, byte row) {
Serial.print(char('A' + column));
Serial.print(row + 1);
}