Java Battleship: invalid input is still sent to next method - java

I wrote a piece of code to implement a Battleship game.
One of its methods (pickRow) is meant to let the user pick a row in the playing field (5x5) to fire in. This method checks checks whether the user input is within the boundaries of the playing board (as mentioned 5x5) and if the user input is an integer (see try-catch statement).
However, the program gives an error as the user input is for example 444 (=invalid input). Although forcing the user to provide another number, the 444 is still transferred on to the next methods (playerAttempt & adaptBoardAfterAttempt), raising an ArrayIndexOutOfBoundsException.
How can I fix the code so that the invalid user input is no longer transferred to subsequent methods? For my code, see below.
Thanks,
Sander
public static int pickRow(int row) {
//method to let the user pick a row
//
Scanner rowInput = new Scanner(System.in);
try { //checks if user input is an integer by using try-catch statement
System.out.print("Pick a row (1-5): ");
row = rowInput.nextInt();
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.");
pickRow(row); //asks for new user input because user input is outside boundaries of the playing board
} else {
row = row - 1; //adjusts the value of row to correct for programming indices
}
} catch (java.util.InputMismatchException e) {
System.out.println("Sorry, invalid input. Please provide a number from 1 to 5.");
pickRow(row); //asks for new user input because input is not an integer
}
return row;
}
public static int pickColumn (int column) {
//method to let the user pick a column
//
Scanner columnInput = new Scanner(System.in);
try { //checks if user input is an integer by using try-catch statement
System.out.print("Pick a column (1-5): ");
column = columnInput.nextInt();
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.");
pickColumn(column); //asks for new user input because user input is outside boundaries of the playing board
} else {
column = column - 1; //adjusts the value of column because java starts counting at 0, not 1
}
} catch (java.util.InputMismatchException e) {
System.out.println("Sorry, invalid input. Please provide a number from 1 to 5.");
pickColumn(column); //asks for new user input because input is not an integer
}
return column;
}
public static void playerAttempt(int[] playerAttempt) {
//method that incorporates player's picks of row and column into an attempt
playerAttempt[0] = pickRow(row);
playerAttempt[1] = pickColumn(column);
}
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;
}
}

Here's what worked for me (my comments start with ">>"):
public static int pickRow() {
//method to let the user pick a row
Scanner rowInput = new Scanner(System.in);
String response;
int row;
while (true) //>> loop is infinite as long as input is invalid. As soon as it's valid, method returns, exiting the loop.
{
//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 the value of row to correct for programming indices
return row; //>> row is valid, so return it
}
}
}
Here's what I changed:
I removed your argument row. It doesn't need to be an argument passed to the method, since your Scanner retrieves the value for row. Instead, I declared it as a method-level variable.
I added a String variable called response. I changed your code to retrieve this as the user's input, rather than row. (i.e., your Scanner uses nextLine() now rather than nextInt().
I placed the rest of the code in an infinite while loop and deleted your recursive pickRow() calls. This way, the method just keeps asking for input until it's finally a valid response, at which point the method returns the row number. In any other instance, the response is not valid and the loop continues.
I rearranged your exception handling to catch a NumberFormatException when I try to convert response to an integer (to assign to row). If an exception is thrown, it prints your error message and the loop continues again.
In short, when the method is called, the program asks for 1 - 5. If it's too big or small, isWithinBoundries catches it, and the program asks again. If it's not a number, the catch block catches it, and the program asks again. If it's a valid value, the method does your -1 procedure and returns the row.
I hope this helps!

Related

I need the input of the user to not skip the value 100

I'm currently working on a program for an O Level project where I have chosen to make a class management system. In my method class, I have various methods which control different functions of my program, such as one which collects the name of the students or one which displays a histogram of the student's grades. However, I have discovered a flaw in one of my methods. This is the method that lists the names of the students, one by one (which are saved in an array from a method that is executed before this method) and asks for the students marks. Here, the user is able to enter any number, which is inconvenient, considering that numerical grades normally range from 0-100. I have tried the following code but I have reached a predicament. The code does in fact stop the user from entering a mark over 100, but instead of allowing the user to re-enter a correct mark, it skips over to the next student, leaving the previous student without a mark. The following is said code:
//mark input
public void markin() {
System.out.println("=====================================");
System.out.println("Please enter the mark of the students");
System.out.println("=====================================");
for (int g = 0; g != marks.length; g++) {
System.out.println(names[g]);
marks[g] = Keyboard.readInt();
while(marks[g]<0||marks[g]>100){
System.out.println("Kindly enter a number that is less than 100");
break;
}
}
}
Help would be very much appreciated and thank you in advance :)
Apologies if my English is not very good.
You almost got it - you need to read in your while-loop instead of breaking without reading. Also a do-loop would be more appropriate for not having to set an initial invalid value.
//mark input
public void markin() {
System.out.println("=====================================");
System.out.println("Please enter the mark of the students");
System.out.println("=====================================");
for (int g = 0; g != marks.length; g++) {
System.out.println(names[g]);
do {
System.out.println("Kindly enter a number that is less than 100");
marks[g] = Keyboard.readInt();
} while(marks[g]<0||marks[g]>100);
}
}
Set marks[ g ] to a number that isn't allowed before the loop, like - 1 then check the keyboard input inside of the while loop,
(and set It there every time as long as the while loop isn't stopped,
marks[g] = Keyboard.readInt();
and don't break the loop, as the loop would end anyways when the input is valid
The valid answers has to get into the array sequentially.
Use this simple trick to reset index [g] to the previous value
then you will overwrite the marks[g] value until you get a valid one:
while(marks[g]<0||marks[g]>100){
System.out.println("Kindly enter a number that is less than 100");
g--; // Resetting index to previous value
break;
}

Making a program repeat within itself, but you can't make a method(?): Java

I have a project for my computer science class and we're making battleship. Part of the program is that we have make sure that the piece the player puts down does not go off of the board.
I've made a method to check to see whether it goes off the board:
private static boolean test(String s, int row, int column,int spaces)
{
if(s.equals("right")&&column+5<=10)
{
return true;
}
if(s.equals("up")&&row-spaces>=0)
{
return true;
}
if(s.equals("left")&&column-spaces>=0)
{
return true;
}
if(s.equals("Down")&&row+spaces<=10)
{
return true;
}
return false;
}
But once I've gotten it to print out an error message, I'm not sure how to make it so that the program can re-recieve the new position for the piece, without putting an if statement in and if statement in an if statement (and on and on), because you need to check the new position to make sure it doesn't go off of the board.
Here is the part where I get the position of the playing piece (although I don't think you need it)
Scanner sonic= new Scanner(System.in);
System.out.println("Please input the row where you want the aircraft carrier (5 spaces) to begin: ");
int beginrow = sonic.nextInt();
System.out.println("Please input the column where you want the aircraft carrier (5 spaces) to begin: ");
int begincolumn = sonic.nextInt();
System.out.print("Please input what direction (up, down, left, right) \nyou want your battle ship to face, making sure it doesn't go off of the board.");
String direction = sonic.next();
And here's one of the if statements that I use to check/place the pieces
if(direction.equals("left")&&test("left",beginrow,begincolumn,5))
{
for(int i = beginrow; i>beginrow-5; i--)
{
battleship[begincolumn-1][i-1] = ('a');
}
}
else if(!test("left",beginrow,begincolumn,5))
{
System.out.println(" ");
System.out.println("*****ERROR: your piece goes off the board, please re-enter your position and direction*****");
}
This may be a duplicate, but I didn't know how to reword my search to find what I wanted. (So if anyone could direct me to the right article, that'd be nice as well)
What you should do is split your code appropriately into methods and call that methods repeatedly until your program is satisfied with the outcome.
For example:
create a method startGame() which has the job call methods getting user input until satisfied
make a method to request the user to input all the different ships and other required data
That might look something like
public void startGame() {
// do some setup
while(!requestShipInput()) { // request ship data until the data is valid
System.out.println(" ");
System.out.println("*****ERROR: your piece goes off the board, please re-enter your position and direction*****");
}
// do some more ship setup
// get the actual playing started
}
public boolean requestShipInput() {
Scanner sonic= new Scanner(System.in);
System.out.println("Please input the row where you want the aircraft carrier (5 spaces) to begin: ");
int beginrow = sonic.nextInt();
System.out.println("Please input the column where you want the aircraft carrier (5 spaces) to begin: ");
int begincolumn = sonic.nextInt();
System.out.print("Please input what direction (up, down, left, right) \nyou want your battle ship to face, making sure it doesn't go off of the board.");
String direction = sonic.next();
if(direction.equals("left")&&test("left",beginrow,begincolumn,5)) {
for(int i = beginrow; i>beginrow-5; i--) {
battleship[begincolumn-1][i-1] = ('a');
}
return true; // valid ship data
}
return false; // invalid ship data
}
As a first step, separate input validation from taking the action based on that input - you already have the validation logic in a separate function, so this is easy. Then figure out what needs to be done in case of invalid input - in your case, you need to ask for new input until you get a valid position:
do {
System.out.println("Please input the row where you want the aircraft carrier (5 spaces) to begin: ");
beginrow = sonic.nextInt();
System.out.println("Please input the column where you want the aircraft carrier (5 spaces) to begin: ");
begincolumn = sonic.nextInt();
System.out.print("Please input what direction (up, down, left, right) \nyou want your battle ship to face, making sure it doesn't go off of the board.");
direction = sonic.next();
} while (!test(direction, beginrow, begincolumn, 5))
After that, you know you've got a valid position.
My next step would probably be to group the information required to describe a ship on the board (i.e. beginrow,begincolumn,direction, probably also size) in a separate Object - possibly named Ship.
I think you could pretty naturally use recursion here:
public void getInput() {
// scanner code to get input
if (!test("left",beginrow,begincolumn,5)) { // test failed
getInput()
return
}
// test succeeded, continue
}
You already have something to the limits of you board? If you execute the check first, you don't need to execute a cascade of if-else
if(!test(direction,beginrow,begincolumn,size))
{
System.out.println(" ");
System.out.println("*****ERROR: your piece goes off the board, please re-enter your position and direction*****");
} else {
// check for collision with already placed ships
}
Keep in mind that there is a chance to combine up/down and left/right. The calculation rules are nearly the same and you only have to decide if you have to look to the one or the other direction.

Why is my exception handling causing an infinite loop?

I'm writing a method that will return an integer value. In the method I am prompting the user for the integer via console through the scanner class.
Because I am using the scanner method "scan.nextInt()", I am also checking for "InputMismatchException" error. I have placed the exception handling in a loop so that if the exception is caught the user is notified and the loop is reiterated. This will require the user to keep entering values until only an integer value has been entered.
However, my issue is after the first time it checks for the error, when it loops back, something is happening and the user is not prompted to enter a new value and the exception is thrown again. This of course results in an infinite loop.
I've researched and found a few cases related to the issue and I've tried performing relevant fixes but nothing I do seems to work and I don't understand what exactly is happening. Is the try block being skipped? Is there something wrong with my notation for the try block?
public static int inputCheck() {
int check=0;
int money = 0;
while (check==0) {
boolean error = false;
System.out.println("Please enter the amount of money your player has.");
while (true) {
try {
money = scan.nextInt();
}catch (InputMismatchException wrongInput) {
System.out.println("Error. Please enter an integer value." + wrongInput);
error = true;
}
break;
}
if (error==false)
check++;
}
return money;
}
EDIT Code has been edited and the "error" boolean value has been adjusted
The problem is in your catch when you set error equal to true. Once error is set to true, your loop is constructed in a way that has no way of exiting because error is never set to false therefore check is never incremented. I would re-structure this this loop system in general, there are much better approaches to handling this.
Try with this:
public static int inputCheck() {
int check = 0;
int money = 0;
while (check == 0) {
boolean error = false;
System.out.println("Please enter the amount of money your player has.");
try {
money = Integer.parseInt(scan.next());
} catch (Exception e) {
System.out.println("Error. Please enter an integer value." + e);
error = true;
}
if (error == false) {
check++;
}
}
return money;
}
while(true) is never false, so the loop never terminates if it doesn't hit the break statement

Calling "guess" variable for two objects in Java

I'm creating a game for homework, and I'm very stuck in the beginning stages. The game takes inputs from two players. The player class controls the guesses by each player. I've been able to correctly set up the player class so that it only accepts the allowed range of inputs.
However, I'm having difficulty calling the guess variable for two player objects. I've tried to determine how to call the value of each guess variable for each object, but it keeps giving me errors. Am I calling the variable correctly? Or is my logic of having two different guesses values for each player object correct? Or is there another way that I need to write the code to have a guess variable for each player?
player class code:
import java.util.Scanner;
public class player {
//handles the input from the player
public player() {
while (true) {
// Prompt the user to guess the number
System.out.println("Enter your guess: ");
Scanner input = new Scanner(System.in);
int guess = input.nextInt();
System.out.println(guess);
if (guess < 0) {
System.out.println("You entered a negative number.
+ "The number you enter must be between 0 and 4. " +
"Please try again. ");
}
else if (guess >= 5){
System.out.println("You entered is greater than 4. "
+ "The number you enter must be between 0 and 4. " +
"Please try again. ");
}
else{
break;
}
} // End of while loop
}//end of player method
}//End of player class
Main code:
public class HW2 {
public static void main(String[] args) {
System.out.println("It's Player A's turn to guess!");
player playerA = new player();
System.out.println(playerA.guess);
System.out.println("It's Player B's turn to guess!");
player playerB = new player();
System.out.println(playerB.guess);
}//end of main
} // end of HW2 class
Thank you in advance for any help!
Declare your guess variable as an instance field instead of a method variable. It's simply a matter of scope
public class player {
int guess;
.....
and instead of int guess = input.nextInt();in the method simply write guess = input.nextInt();
This should solve the call issue
You are trying to access the guess variable outside of its scope (this variable is only accesible in the player method).
In order to fix it, define a public field in the Player class and assign the guess value to it (better use setter and getter). Then you will be able to access it in your main method.
With regards to your initial question, about accessing the "guess" variable for each player:
The guess variable for each player is a local variable declared within the constructor. As such, it can't be seen outside the constructor scope and so when your main method tries to access playerB.guess, the compiler can see that there is no variable called guess in the player class. It doesn't, and can't, realize that you're actually trying to access a local variable inside the constructor. So, to solve this particular problem - you could make the guess variable a field in the player class. e.g.
//handles the input from the player
public player() {
public int guess = 0;
while (true) {
// Prompt the user to guess the number
System.out.println("Enter your guess: ");
Scanner input = new Scanner(System.in);
guess = input.nextInt();
System.out.println(guess);
if (guess < 0) {
System.out.println("You entered a negative number.
+ "The number you enter must be between 0 and 4. " +
"Please try again. ");
}
else if (guess >= 5){
System.out.println("You entered is greater than 4. "
+ "The number you enter must be between 0 and 4. " +
"Please try again. ");
}
else{
break;
}
} // End of while loop
}//end of player method
}//End of player class
This will resolve the particular compile problem - but one thing to note, because the logic for making a guess is in the constructor of the player object, this means it will only be called once, when the class is created.

While loop using Scanner method hasNext() fails to loop, exits unexpectedly

So I've been working on an assignment for school. It says to make a program that does the following:
1. Prompt user for input
2. Take input from user three times, store as floats
3. Pass variables to method minimum() which uses Math.min() to calculate the minimum.
4. Print the result
5. Prompt user again and loop until EOF is received
Well, I did that but it didn't feel like a challenge. I've modified the code so that it keeps prompting the user for input and adding elements to an ArrayList which gets passed to minimum() which returns a float that gets printed, then the program should prompt the user for input once more and it should loop again until an EOF is received.
// imports
import java.util.Scanner;
import java.lang.Math;
import java.util.ArrayList;
public class FindMin{
public static void main(String args[]){
// define vars
float x;
int iter = 1;
// create new instance of Scanner called input
Scanner input = new Scanner(System.in);
// create new instance of ArrayList called nums with Float elements
ArrayList<Float> nums = new ArrayList<Float>();
// print the instructions and prompt the user for a number
System.out.printf("%s\n %s\n %s\n",
"Type the end-of-file indicator to terminate",
"On UNIX/Linux/Mac OS X type <ctrl> d then press Enter",
"On Windows type <ctrl> z then press Enter");
System.out.print("Enter a number: ");
// loop until EOF, then quit
while(input.hasNext()){ // get user input and exit if input is EOF
// assign input to x, add x as a new element of nums, then prompt user
x = input.nextFloat();
nums.add(x);
iter++;
System.out.print("Enter a number: ");
// loop until EOF, then calculate min
while(input.hasNext()){
x = input.nextFloat();
nums.add(x);
iter++;
System.out.print("Enter a number: ");
} // end while
// calculate min and set iter back to 1
System.out.printf("\n Minimum is: %f\n\n", minimum(nums));
iter=1;
// re-prompt user
System.out.printf("%s\n %s\n %s\n",
"Type the end-of-file indicator to terminate",
"On UNIX/Linux/Mac OS X type <ctrl> d then press Enter",
"On Windows type <ctrl> z then press Enter");
System.out.print("Enter a number: ");
} // end while, should continue looping but doesn't
} // end method main
public static float minimum(ArrayList<Float> n){ // returns a float, takes an ArrayList with <Float> elements
// assigns element at index 0 of n to min
float min = n.get(0).floatValue();
// iterates through i and assigns min to Math.min() called with previous
// value of min and element at index i of n
for(int i=1;i < n.size();i++){
min = Math.min(min, n.get(i).floatValue());
} // end for
return min;
} // end method minimum
} // end class FindMin
The problem is that the outer loop exits unexpectedly. It's my understanding that input.hasNext() prompts the user for input and returns true if there is input, if not, it returns false. It doesn't seem to check for input though. Can someone tell me what's happening?
One problem that you have going there that is likely doing what you think is the nested whiles.
Basically, once the inner while loop terminates, because it has the same condition as the outer while loop, the outer loop will execute the code between the inner loop and the ending brace and then exit as well. You said it yourself in the comments in the code- once the EOF is given, both loops terminate.

Categories