0

So my TicTacToe game feels practically perfect besides the fact that I'm at a loss on how to prevent non-integer (character or string) input. I want to make it so "That is not a number please try again." whenever a letter or symbol is inputted through the scanner. Instead whenever I try that I just get

Exception in thread "main" java.util.InputMismatchException
    at java.base/java.util.Scanner.throwFor(Scanner.java:939)
    at java.base/java.util.Scanner.next(Scanner.java:1594)
    at java.base/java.util.Scanner.nextInt(Scanner.java:2258)
    at java.base/java.util.Scanner.nextInt(Scanner.java:2212)
    at TicTactoe3.main(TicTactoe3.java:23)

It also seems that java.util.InputMismatchException; is never used so the block can't be accessed somehow. I have it in a try catch statement in a while loop that seems practically inaccessible, and I'm aware that it's because of playerPos() being different from playerPos but I'm still at a loss for a solution. Placing the try catch into the main method doesn't work either, returns can't be used in void statements and without it it just doesn't do anything.

This is all of my code for reference. I really have no idea how to fix this issue and any answers/advice would be appreciated though I'd prefer not having to rewrite all of my code.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.InputMismatchException;
import java.util.List;
// import java.util.Random;
import java.util.Scanner;

public class TicTactoe3 {
    
    static Scanner move = new Scanner(System.in);
    static ArrayList<Integer> playerPositions = new ArrayList<Integer>();
    static ArrayList<Integer> playerPositions2 = new ArrayList<Integer>();

    public static void main(String[] args) {
        
        char[][] gameBoard = {{' ', '|', ' ', '|', ' '}, 
                              {'-', '+', '-', '+', '-'}, 
                              {' ', '|', ' ', '|', ' '}, 
                              {'-', '+', '-', '+', '-'}, 
                              {' ', '|', ' ', '|', ' '}};
        
        printGameBoard(gameBoard);  
        int playerPos = move.nextInt();
        
        while(true) {
            

            System.out.println("Enter your move (1-9) player one ");
            while(playerPositions.contains(playerPos) || playerPositions2.contains(playerPos)) {
                System.out.println("Position taken");
                playerPos = move.nextInt();
            }

            while(playerPos >= 10 || playerPos <= 0) {
                System.out.println("Incorrect Input");
                playerPos = move.nextInt();
            }
            

            placePiece(gameBoard, playerPos, "player 1");
            printGameBoard(gameBoard);

            String result = checkWinner();
            if(result.length() > 0) {
                System.out.println(result);
                break;
            }
            
            if (Character.isLetter(playerPos)) {
                System.out.println("Invalid Input. Numbers 1-9 only!");
            }

            System.out.println("Enter your move (1-9) player two ");
            int playerPos2 = move.nextInt();
            while(playerPositions.contains(playerPos2) || playerPositions2.contains(playerPos2)) {
                System.out.println("Position taken");
                playerPos2 = move.nextInt();
            }

            while(playerPos2 >= 10 || playerPos2 <= 0) {
                System.out.println("Incorrect Input");
                playerPos2 = move.nextInt();
            }

            placePiece(gameBoard, playerPos2, "player 2");  
            printGameBoard(gameBoard);

            result = checkWinner();
            if(result.length() > 0) {
                System.out.println(result);
                break;
            }
        }   
    }
    
    
    public static int playerPos() { // This is where I try to prevent non-integer input but it doesn't do anything
                                    // That and it seems to regard playerPos() as different from playerPos but
                                    // simply using playerPos leads to other issues
        while (true) {
            try {
                return move.nextInt();
            }
            catch (InputMismatchException a) {
                move.next();
                System.out.println("That is not a number please try again.");
            }
        }
    } 
    
    public static void printGameBoard(char[][] gameBoard) {
        for(char[] row : gameBoard) {
            for(char c : row) {
                System.out.print(c);
            }
            System.out.println();
        }
    }
    
    public static void placePiece(char[][] gameBoard, int pos, String user) {
        
        char symbol = ' ';
        
        if(user.equals("player 1")) {
            symbol = 'X';
            playerPositions.add(pos);
        } else if(user.equals("player 2")) {
            symbol = 'O';
            playerPositions2.add(pos);
        }
        
        switch(pos) {
        case 1:
            gameBoard[0][0] = symbol;
            break;  
        case 2:
            gameBoard[0][2] = symbol;
            break;  
        case 3:
            gameBoard[0][4] = symbol;
            break;
        case 4:
            gameBoard[2][0] = symbol;
            break;
        case 5:
            gameBoard[2][2] = symbol;
            break;
        case 6:
            gameBoard[2][4] = symbol;
            break;
        case 7:
            gameBoard[4][0] = symbol;
            break;
        case 8:
            gameBoard[4][2] = symbol;
            break;
        case 9:
            gameBoard[4][4] = symbol;
            break;
        default:
            break;
        }
    }
    public static String checkWinner() {
        
        List topRow = Arrays.asList(1, 2, 3);
        List midRow = Arrays.asList(4, 5, 6);
        List botRow = Arrays.asList(7, 8, 9);
        List leftCol = Arrays.asList(1, 4, 7);
        List midCol = Arrays.asList(2, 5, 8);
        List rightCol = Arrays.asList(3, 6, 9);
        List cross1 = Arrays.asList(1, 5, 9);
        List cross2 = Arrays.asList(7, 5, 3);
        
        List<List> winning = new ArrayList<List>();
        winning.add(topRow);
        winning.add(midRow);
        winning.add(botRow);
        winning.add(leftCol);
        winning.add(midCol);
        winning.add(rightCol);
        winning.add(cross1);
        winning.add(cross2);
        
        for (List l : winning) {
            if(playerPositions.containsAll(l)) {
                return "Congrats, player 1 wins!";
            }
            else if(playerPositions2.containsAll(l)) {
                return "Congrats, player 2 wins";
            }
        }
        
        for (List l : winning) {
            if(playerPositions.size() + playerPositions2.size() == 9) {
                return "NOBODY WINS";
            }
        }
        
        return "";
    }
}

Edit: I came up with a partial solution. First I made it so playerPos and playerPos2 start at zero before any player input.

int playerPos = 0;
int playerPos2 = 0;

Then I wrote a simple try and catch statement but I'm unable to make it loop so it has to break. Continue just types the error ad nauseum.

try {
                playerPos = move.nextInt();
            } catch (InputMismatchException e) {
                System.out.println("1-9 only, please try again.");
                break;
            }

In place of break I tried playerPos = move.nextInt(); like my other while loops but that just leads to the "Exception in thread main" error which halts the program. There's probably a pretty simple solution to this that I'm just not realizing somehow.

Beryl
  • 1
  • 1
  • [This](https://stackoverflow.com/a/62435357/1552534) should help – WJS Jun 17 '20 at 23:43
  • You're not catching the `InputMismatchException` thrown at line 23. And you're never calling `playerPos()`. How do you expect this to work? – jarmod Jun 17 '20 at 23:46
  • I didn't expect it to work, but I was lost. That's the point of this post – Beryl Jun 21 '20 at 23:14

1 Answers1

0

A typical pattern I enjoy for this:

public Optional<Integer> getInt(String input) {
    try {
        return Integer.parseInt(input);
    } catch (NumberFormatException ex) {
        return Optional.empty();
    }
}

Then, within your main logic:

//...
Scanner move = /* your scanner object */;
Optional<Integer> input;
while ((input = getIntLine(move.nextLine())).isEmpty()) {
    System.out.println("Invalid input, expected (number)"); 
}
int value = input.get();

If you want to make your loop repeat the input request, that's simple enough as well:

//...
Scanner move = /* your scanner object */;
Optional<Integer> input;
boolean read = false;
while (!read) {
    System.out.print("Please enter a number: ");
    input = getInt(move.nextLine());
    read = !input.isEmpty();
    if (!read) {
        System.out.println("Not a number " /* ... etc */);
    }
}
int value = input.get();
Rogue
  • 11,105
  • 5
  • 45
  • 71
  • `while ((input = getIntLine(move.nextLine())).isEmpty()) {` is needlessly complicated, just create a local `var line = move.nextLine()` variable and use that. – Maarten Bodewes Jun 18 '20 at 14:46
  • I appreciate the answer, though both of these are a bit beyond my learning at the moment compared to the answer I came up with, which was to one, set playerPos and playerPos2 as starting at 0 and then write a try and catch statement – Beryl Jun 21 '20 at 23:17