Search code examples
javatext-based

Battleship coordinate input (with letter) Java


I'm new here (stackoverflow and programming in general), and am trying to create a battleship game in Java. I am quite happy with what I have, and am aware that the code might not be the most effective, but that is not the problem I want to solve here.

When inputing where to place your ship or where to attack, before I had it so that it would ask what the column was, and then separately what the row was. Now I am trying to make a method (called coordinates, it is the last one) that will allow you to input the coordinates together (in the format 'A1'). It worked perfectly, until the input was wrong (had either two letters, two numbers, nothing in it, etc...)

Another thing is that in the coordinates method, in the catch I had to put return 20 because it said missing return statement, but I don't actually need to return anything as that is in a do-while loop. What can I do to take that away?

This is my first question on this site, so I don't know if anyone will need this (I don't know if there are any rules against unnecessary code), but I will leave the whole program here, as well as an example output.

public static int firing(String[][] board, int hits, int torpedoes, int shipSize, int shipAmount, int whatPlayer)
{
    Scanner userInput = new Scanner(System.in);
    System.out.println("What coordinate do you want?");
    String coordinate = userInput.nextLine();
    int col = coordinates(coordinate, "col");
    int row = coordinates(coordinate, "row");
    while (col > 8 || col < 1 || row > 8 || row < 1)
    {
        System.out.println("That is not a valid coordinate.");
        Scanner input = new Scanner(System.in);
        System.out.println("What coordinate do you want?");
        coordinate = input.nextLine();
        col = coordinates(coordinate, "col");
        row = coordinates(coordinate, "row");
    }

}

public static int letterToNumber(String colLet)
//colLet = column in letter
//This method is to change the column letters to numbers
{
    int num = 1;
    while (!colLet.equalsIgnoreCase("A")&&
           !colLet.equalsIgnoreCase("B")&&
           !colLet.equalsIgnoreCase("C")&&
           !colLet.equalsIgnoreCase("D")&&
           !colLet.equalsIgnoreCase("E")&&
           !colLet.equalsIgnoreCase("F")&&
           !colLet.equalsIgnoreCase("G")&&
           !colLet.equalsIgnoreCase("H"))
    {
        System.out.println("That is not a valid coordinate (not one of the letters)");
        Scanner input = new Scanner(System.in);
        System.out.println("Please enter a valid coordinate: "
                + "\nIt should be in the format 'A1' (column then row)");
        String coordinate = input.nextLine();
        colLet = "" + coordinate.charAt(0);
    }
    switch (colLet.toLowerCase())
    {
        case "a": num = 1; break;
        case "b": num = 2; break;
        case "c": num = 3; break;
        case "d": num = 4; break;
        case "e": num = 5; break;
        case "f": num = 6; break;
        case "g": num = 7; break;
        case "h": num = 8; break;
        default: System.out.println("That wasn't a letter!");
    }
    return num;
}


public static int coordinates(String coordinate, String RorC)
// RorC is for Row or column
{
    boolean isValid;
    if (RorC.equals("row"))
    {
        do
        {
            try
            {
                String rowStr = "" + coordinate.charAt(1);
                int row = Integer.parseInt(rowStr);
                isValid = true;
                return row;
            }
            catch(Exception e)
            {
                System.out.println("Error");
                Scanner userInput = new Scanner(System.in);
                System.out.println("That is an invalid coordinate (row probably only had one character)"
                        + "\nPlease enter a valid coordinate."
                        + "\nIt should be in the format 'A1' (column then row)");
                coordinate = userInput.nextLine();
                isValid = false;
                return 20;
            }
        }while(isValid = false);
    }
    else if (RorC.equals("col"))
    {
        do
        {
            try
            {
                String colLet = "" + coordinate.charAt(0);
                int col = letterToNumber(colLet);
                isValid = true;
                return col;
            }
            catch (Exception e)
            {
                System.out.println("Error");
                Scanner userInput = new Scanner(System.in);
                System.out.println(""
                        + "That is an invalid coordinate (col probably had nothing inside it)"
                        + "\nPlease enter a valid coordinate."
                        + "\nIt should be in the format 'A1' (column then row)");
                coordinate = userInput.nextLine();
                isValid = false;
                return 20;
            }
        }while(isValid=false);
    }
    else
    {
        return 0;
    }
}
}

I know my code isn't very efficient, but I will clean it up after I am done. Here is an example of output (I don't know how to put this is in a box without code format)

It is now player 1's turn to choose where to place their ships.
Can the other player please turn around.

You will now be placing ship number 1.
Please write 'H' if you want to create a horizontal ship,
or 'V' if you want it to be vertical.
h
________________________________

   A  B  C  D  E  F  G  H  
1  -  -  -  -  -  -  -  -
2  -  -  -  -  -  -  -  -
3  -  -  -  -  -  -  -  -
4  -  -  -  -  -  -  -  -
5  -  -  -  -  -  -  -  -
6  -  -  -  -  -  -  -  -
7  -  -  -  -  -  -  -  -
8  -  -  -  -  -  -  -  -
________________________________

What coordinates do you want your ship to start on?
If it is horizontal, it will go right from there,
and if it is vertical, it will go down from there.
Please enter the coordinates in the format 'A1' (column then row).
If you enter anything after that, it will be ignored.
u9
That is not a valid coordinate (not one of the letters)
Please enter a valid coordinate: 
It should be in the format 'A1' (column then row)
a1
That is not a valid coordinate (addship).
What coordinates do you want your ship to start on?
Please enter them in the format 'A1' (column then row).
If you enter anything after that, it will be ignored.
d7
________________________________

   A  B  C  D  E  F  G  H  
1  -  -  -  -  -  -  -  -
2  -  -  -  -  -  -  -  -
3  -  -  -  -  -  -  -  -
4  -  -  -  -  -  -  -  -
5  -  -  -  -  -  -  -  -
6  -  -  -  -  -  -  -  -
7  -  -  -  S  S  S  -  -
8  -  -  -  -  -  -  -  -
________________________________

This is your board.

Would appreciate any help, even if it is about something different (not the problem I am addressing).


Solution

  • I wrote a small program that inputs a string and converts it to a row and column. It will keep asking if user enters an invalid string.

    Sample run:

    Provide a coordinate:
    hello?
    That is not a valid coordinate! Try again.
    X4
    That is not a valid coordinate! Try again.
    C4
    Coordinate{col=3, row=4}
    

    I created a helper class to store coordinates.

    Notice how all of reads/writes are in one function (readCoordinate) and all of string manipulations are in another (parseCoordinate).

    You could also throw an exception if user string is invalid, I returned null for simplicity.

    package com.company;
    
    import java.util.Scanner;
    
    class Coordinate {
        private final int col, row;
    
        public Coordinate(int col, int row) {
            this.col = col;
            this.row = row;
        }
    
        @Override
        public String toString() {
            return "Coordinate{" +
                    "col=" + col +
                    ", row=" + row +
                    '}';
        }
    }
    
    
    public class Main {
        private final Scanner input;
    
        public static void main(String[] args) {
            Main m = new Main();
            System.out.println(m.readCoordinate());
        }
    
        Main() {
            input = new Scanner(System.in);
        }
    
        private Coordinate readCoordinate() {
            System.out.println("Provide a coordinate:");
    
            while (true) {
                String line = input.nextLine();
                Coordinate c = parseCoordinate(line);
    
                if (c != null)
                    return c;
    
                System.out.println("That is not a valid coordinate! Try again.");
            }
        }
    
        /**
         * Converts a string like A2 into a Coordinate object. Returns null if string is invalid.
         */
        public Coordinate parseCoordinate(String line) {
            line = line.trim().toLowerCase();
    
            if (line.length() != 2)
                return null;
    
            char letter = line.charAt(0);
    
            if (letter > 'h' || letter < 'a')
                return null;
            int col = letter - 'a' + 1;
    
            char number = line.charAt(1);
            if (number > '8' || number < '1')
                return null;
            int row = number - '1' + 1;
    
            return new Coordinate(col, row);
        }
    }
    

    Answer to the questions:

    What is @Override?

    It's an optional decorator, marking that I'm overriding a method, not creating a new one. If I misspelled toString like tostring my IDE will warn me that I'm not actually overriding something. You could ignore it.

    Why are col and row final? Doesn't that mean that you can't change them later?

    It means you can't assign to them after the first assignment (In constructor). It's a good practice to create immutable (immutable == can't be changed) data classes. For example you are sure that if you pass a Coordinate to a method, the method could not modify your instance and you won't get a messed up Coordinate.

    What is trim?

    Trim remove whitespace (space, tab, ...) from both ends of a string. " hello world! ".trim() is "hello world!".

    What is that Main() thing with a Scanner(System.in) after the main method?

    I created the scanner once and used it many times. You created a new scanner each time you would like to read a string. I don't think it matters for scanner but in some cases you may lose things by re-creating an object.

    In readCoordinate, what is the true in the while conditioning?

    while (true) means loop forever. There is a return statement that breaks the otherwise infinite loop.

    In parseCoordinate, when defining col, how are you adding a character to an int?

    Characters are represented using numbers so characters are numbers under the hood. In Java char is like a 16-bit unsigned integer, you could do normal integer calculations on them.

    why is readCoordinate of type Coordinate and why is it private?

    Don't bother with access modifiers until you're better with programming. It doesn't make much sense in small programs. You could mark which methods should be called from the outside and which methods shouldn't.