I wrote a piece of code to implement a Battleship game. The method isHit
is meant to check whether player input leads to hitting a ship or not. It does so by looping through an int[3][2] (ships
) holding coordinates of the ships and comparing these with the coordinates provided by the user. If it's a hit, isHit
prints text to let the user know they hit a ship. However, while running the code and hitting a ship, the text is printed to the console twice instead of once. I can't seem to figure out why, so I would be glad if someone could help me out here. Full code is included below:
import java.util.Random;
import java.util.Scanner;
public class BattleShip {
static int[][] board = new int[5][5];
static int[][] ships = new int[3][2];
static int row, column;
static int[] playerAttempt = new int[2];
static int nrOfAttempts = 0, nrOfHits = 0;
public static void welcome() {
//Welcomes the user and introduces rules of the game
System.out.println("WELCOME TO BATTLESHIP!");
System.out.println("In this game, it's your task to localize the three ships that are present in the see.");
System.out.println("Now, the board is filled with ~ signs, representing waves.");
System.out.println("You will be asked to provide coordinates, first a row number and then a column number.");
System.out.println("When you hit a ship at the provided coordinates, an X will appear on the map. If you miss, a 0 will appear.");
System.out.println("You have unlimited attempts to sink the ships, but the less attempts, the better.");
System.out.println("Good luck!\n");
}
public static void createBoard(int[][] board) {
//creates a new playing board of 5x5
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
board[i][j] = 0;
}
}
}
public static void placeShips(int[][] ships) {
//places 3 battleships at random places in the board
Random randomCoordinate = new Random();
//generates random coordinates for the first ship
for (int i = 0; i < ships.length; i++) {
ships[i][0] = randomCoordinate.nextInt(5);
ships[i][1] = randomCoordinate.nextInt(5);
//checks if coordinates of new ship are the same as previous ship
//by using j<i in for-statement, this will only be checked from the second ship onwards
//if coordinates are the same, new random coordinates are generated
for (int j = 0; j < i; j++) {
if (ships[i][0] == (ships[j][0]) && ships[i][1] == (ships[j][1])) {
do {
ships[i][0] = randomCoordinate.nextInt(5);
ships[i][1] = randomCoordinate.nextInt(5);
} while (ships[i][0] == (ships[j][0]) && ships[i][1] == (ships[j][1]));
}
}
}
}
public static void showBoard(int[][] board) {
//displays the board to the user
System.out.println("Playing board:\n");
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
if (board[i][j] == 0) {
System.out.print("~ "); //represents unknown territory
} else if (board[i][j] == 1) {
System.out.print("0 "); //represents a miss
} else {
System.out.print("X "); //represents a hit
}
}
System.out.println("\t"); //prints a return after each row
}
System.out.println(""); //prints a blank line below the board
}
public static void printShips(int[][] ships) {
//shows coordinates of ships for testing purposes
System.out.println("Ships are located at these 3 coordinates:");
for (int i = 0; i < ships.length; i++) {
for (int j = 0; j < ships[i].length; j++) {
System.out.print((ships[i][j]+1)+" ");
}
System.out.println("\t");
}
System.out.println("");
}
public static boolean isWithinBoundaries (int input) {
if (input >= 1 && input <= 5) {
//input is within boundaries of the playing board
return true;
} else {
//input is outside boundaries of the playing board
return false;
}
}
public static int pickRow() {
//method to let the user pick a row
Scanner rowInput = new Scanner(System.in);
String response; //temporary variable to store user input
while (true) { /*loop is infinite as long as input is invalid.
as soon as it's valid, the method exits the loop by
the return statement*/
//checks if user input is an integer by using try-catch statement
System.out.print("Pick a row (1-5): ");
response = rowInput.nextLine();
try {
row = Integer.valueOf(response);
} catch (NumberFormatException e) {
System.out.println("Sorry, invalid input. Please provide a number from 1 to 5.");
continue;
}
if (!isWithinBoundaries(row))
{ //checks if user input is within boundaries of the playing board
System.out.println("That's outside the sea. Please provide a number from 1 to 5.");
} else {
row = row - 1; //adjusts value because Java starts counting at 0, not 1
return row; //value is valid, so return
}
}
}
public static int pickColumn () {
//method to let the user pick a column
Scanner columnInput = new Scanner(System.in);
String response; //temporary variable to store user input
while (true) { /*loop is infinite as long as input is invalid
as soon as it's valid, the method exits the loop by
returning*/
//checks if user input is an integer by using try-catch statement
System.out.print("Pick a column (1-5): ");
response = columnInput.nextLine();
try {
column = Integer.valueOf(response);
} catch (NumberFormatException e) {
System.out.println("Sorry, invalid input. Please provide a number from 1 to 5.");
continue;
}
if (!isWithinBoundaries(column))
{ //checks if user input is within boundaries of the playing board
System.out.println("That's outside the sea. Please provide a number from 1 to 5.");
} else {
column = column - 1; //adjusts value because Java starts counting at 0, not 1
return column; //value is valid, so return
}
}
}
public static void playerAttempt(int[] playerAttempt) {
//method that incorporates player's picks of row and column into an attempt
playerAttempt[0] = pickRow();
playerAttempt[1] = pickColumn();
}
public static boolean isHit(int[][] ships, int[] playerAttempt) {
//checks whether the player attempt hits a ship
for (int i = 0; i < ships.length; i++) {
if (playerAttempt[0] == ships[i][0] && playerAttempt[1] == ships[i][1]) {
System.out.println("That's a hit!");
System.out.println("You sunk the ship located at coordinates "+(playerAttempt[0]+1)+","+(playerAttempt[1]+1)+".\n");
return true;
}
}
return false;
}
public static void adaptBoardAfterAttempt (int[] playerAttempt, int[][] ships, int[][] board) {
//adapts the playing board after a player attempt to indicate a hit (X) or a miss (0)
if (isHit(ships,playerAttempt)) {
board[playerAttempt[0]][playerAttempt[1]]=2;
} else {
board[playerAttempt[0]][playerAttempt[1]]=1;
}
}
public static void main(String[] Args) {
//carries out the main program
welcome();
createBoard(board);
placeShips(ships);
//printShips(ships);
//player has opportunity to make attempts until all 3 ships are hit
do {
showBoard(board);
playerAttempt(playerAttempt);
nrOfAttempts++;
if (isHit(ships, playerAttempt)) {
nrOfHits++;
} else {
System.out.println("You missed! But keep trying...\n");
}
adaptBoardAfterAttempt(playerAttempt,ships,board);
} while (nrOfHits <= 2);
//finishes the game
System.out.println("\nBATTLESHIP IS OVER.\n");
showBoard(board);
System.out.println("");
System.out.println("You needed "+nrOfAttempts+" attempts to sink all ships.");
}
}