-3

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

    }
}
jmoerdyk
  • 5,544
  • 7
  • 38
  • 49
Sander
  • 29
  • 6
  • 4
    Use a debugger, its what it was made for – user1231232141214124 Jul 11 '16 at 17:42
  • Can you provide the full program? – Nicholas Hirras Jul 11 '16 at 17:48
  • You call `isHit()` inside a loop. Why wouldn't it print multiple times? – PM 77-1 Jul 11 '16 at 17:49
  • Your provided code seems fine. What exactly is the output? Does the output *say* that a ship was sunk twice? Be aware that if you give your loop the same attempt again, then it will sink the same ship again as there is no logic that holds if a given ship was already sunk. So maybe the part where the player attempt gets fetched, fetches it twice. – Zabuzard Jul 11 '16 at 17:50
  • If a shit is hit, it prints to the console: That's a hit! You sunk the ship at coordinates x,y. That's a hit! You sunk the ship at coordinates x,y. (i.e., it prints it twice while I want it to print it only once) – Sander Jul 11 '16 at 17:53
  • I already told you to use a debugger, it will tell you exactly where and why it is happening – user1231232141214124 Jul 11 '16 at 17:56
  • Yeah, the code you provided seems fine. There is nothing which leads to your result. Please give us more code. I assume the *player-attempt-logic* fetches the same player attempt twice. – Zabuzard Jul 11 '16 at 17:56
  • 1
    I edited the post, it now includes the full code – Sander Jul 11 '16 at 17:58
  • Awesome, see the answer :) – Zabuzard Jul 11 '16 at 18:07
  • Possible duplicate of [What is a debugger and how can it help me diagnose problems?](http://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems) – Raedwald Jul 11 '16 at 18:22

1 Answers1

2

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.

Zabuzard
  • 25,064
  • 8
  • 58
  • 82
  • Aah, I see now. Works fine after moving the print statements to the main method. I use NetBeans, I assume it has a similar debugging function? – Sander Jul 11 '16 at 18:17
  • Indeed, here's a small tutorial I've just found: https://www.youtube.com/watch?v=joWldbcp1So – Zabuzard Jul 11 '16 at 18:20