Sander Sander - 1 year ago 72
Java Question

Java Batteship: text is printed twice instead of once in case of a hit

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.");

}
}

Answer Source

Your code calls the isHit method twice after a hit.

First time at the correct place inside the main method:

First call

After this you call adaptBoardAfterAttempt(playerAttempt, ships, board); inside the main method but this leads to the second call of isHit:

Second call

You may fix the problem by not letting isHit print itself. Then add the print logic inside your main:

if (isHit(ships, playerAttempt)) {
    nrOfHits++
    // Add the print stuff here
    System.out.println("Hit...");
} else { ...

How can you debug such stuff on your own next time? Are you using an IDE like Eclipse? If so, you may add a breakpoint where the prints are made (you do so by double-clicking on the line number of this line).
After that, start the program in the debugging mode, that is the little bug here: Debug-Mode Eclipse

Play the game until the first print, then use Step-Over (F6) to execute the next line of the code and then stop again: Step-Over

Repeat the process until you reach the isHit method again, then you see why it was called again :)

You also can simply click on the isHit method, then Eclipse will highlight all occurrences of this method. Then you will immediately see at which places the problem may occur.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download