Ryan Dorman
Ryan Dorman

Reputation: 317

How can I ensure user enters a valid choice?

I'm making a simple rock paper scissors program but am unsure how to ensure that the user only enters a valid choice. I need to be able to reprompt them if they don't type some varriant of "rock", "paper", or "scissors" (capitalization doesn't matter) and later "yes" or "no". Suggestions?

import java.util.*;

public class RockPaperScissors {

private int wins = 0;
private int losses = 0;
private int ties = 0;

public static void main(String[] args) {
    // TODO Auto-generated method stub

    RockPaperScissors model = new RockPaperScissors();
    Scanner scan = new Scanner(System.in);

    while (true) {
        System.out.println("Rock, Paper, Scissors... Pick one! (Type Rock, Paper, or Scissors)");
        String playerChoice = scan.next();

        String computerChoice = model.getRandomChoice();

        System.out.println("You chose " + playerChoice + ".");
        System.out.println("The computer chose " + computerChoice + ".");

        RockPaperScissors.GameOutcome outcome = model.getGameOutcome(
                playerChoice, computerChoice);

        if (outcome == RockPaperScissors.GameOutcome.WIN) {
            System.out.println("You won! Congratulations");
        } else if (outcome == RockPaperScissors.GameOutcome.LOSE) {
            System.out.println("You lose! Better luck next time!");
        } else {
            System.out.println("Tie!");
        }

        System.out.print("Do you want to play again? (Yes/No):");

        String answer = scan.next();
        if (answer.equalsIgnoreCase("no")) {
            break;
        }
    }

    System.out.println("Thanks for playing!");
    System.out.println("Wins: " + model.getWins());
    System.out.println("Losses: " + model.getLosses());
    System.out.println("Ties: " + model.getTies());
    scan.close();
}

public static enum GameOutcome {
    WIN, LOSE, TIE;
}

public GameOutcome getGameOutcome(String userChoice, String computerChoice) {

    if (userChoice.equalsIgnoreCase("Rock")) {

        if (computerChoice.equalsIgnoreCase("Paper")) {
            losses++;
            return GameOutcome.LOSE;
        } else if (computerChoice.equalsIgnoreCase("Scissors")) {
            wins++;
            return GameOutcome.WIN;
        }
    } else if (userChoice.equalsIgnoreCase("Paper")) {

        if (computerChoice.equalsIgnoreCase("Scissors")) {
            losses++;
            return GameOutcome.LOSE;
        } else if (computerChoice.equalsIgnoreCase("Rock")) {
            wins++;
            return GameOutcome.WIN;
        }
    } else if (userChoice.equalsIgnoreCase("Scissors")) {
        if (computerChoice.equalsIgnoreCase("Rock")) {
            losses++;
            return GameOutcome.LOSE;
        } else if (computerChoice.equalsIgnoreCase("Paper")) {
            wins++;
            return GameOutcome.WIN;
        }
    }
    ties++;
    return GameOutcome.TIE;
}

public String getRandomChoice() {
    double d = Math.random();

    if (d < .33) {
        return "Rock";
    } else if (d < .66) {
        return "Paper";
    } else {
        return "Scissors";

    }
}

public int getWins() {
    return wins;
}

public int getLosses() {
    return losses;
}

public int getTies() {
    return ties;
}

}

Upvotes: 5

Views: 862

Answers (3)

Jean-Fran&#231;ois Savard
Jean-Fran&#231;ois Savard

Reputation: 21004

Keep the valid choices in a list, loop asking the input from the user until he enter something valid.

List<String> validChoices = Arrays.asList("rock", "paper", "scissors");
Scanner sc = new Scanner(System.in);
String choice = null;
do
{
    System.out.println("Enter a choice (rock|paper|scissors)");
    choice = sc.next().toLowerCase();//Retrieve as lower case
}
while(!validChoices.contains(choice));

Upvotes: 5

Mick Mnemonic
Mick Mnemonic

Reputation: 7956

If I were to play a session of roshambo against the computer, I'd like to be able to make my selection by only typing in the first letter of "Rock" , "Paper" or "Scissors".

Using a rich enum is a natural choice here:

private enum Choice {

    ROCK ("Rock"),
    PAPER ("Paper"),
    SCISSORS ("Scissors");

    private String displayName;

    private static final List<Choice> VALUES =
            Collections.unmodifiableList(Arrays.asList(values()));
          private static final int SIZE = VALUES.size();
    private static final Random RANDOM = new Random();        

    private Choice(String dn) {
        displayName = dn;
    }

    /**
     * Returns a random Choice.
     */
    public static Choice getRandomChoice() {
        return VALUES.get(RANDOM.nextInt(SIZE));
    }

    /**
     * Returns a Choice that matches the input string. The input is considered a match if it starts with the same character 
     * as the displayname of a Choice. If no match is found, returns null.
     */
    public static Choice fromInput(String input) {

        if (input == null || input.length() == 0) {
            return null;
        }

        for (Choice c : VALUES) {
            if (Character.toLowerCase(c.displayName.charAt(0))
                    == Character.toLowerCase(input.charAt(0))) {
                return c;
            }
        }
        return null;
    }

    /**
     * Returns the text to display to the user, asking for input to #fromInput().
     */
    public static String promptText() {
        StringBuilder sb = new StringBuilder();
        for (Choice c : VALUES) {
            if (sb.length() > 0) {
                sb.append(", ");
            }
            sb.append(c.displayName).append(" (")
                    .append(c.displayName.charAt(0)).append(")");
        }
        sb.append(". Pick one!");
        return sb.toString();
    }
}

Having most of the functionality coded declaratively in the enum, your client code will become much simpler. The enum also handles the computer's random selection (idea from this answer).

 while (true) {

        Choice choice = null;

        while (choice == null) {
            System.out.println(Choice.promptText());
            choice = Choice.fromInput(scan.next());
        }

        String computerChoice = Choice.getRandomChoice().displayName;
 // ...

You could also encapsulate most (if not all) of the logic you have in the getGameOutcome method, into Choice.

Upvotes: 3

saroff
saroff

Reputation: 698

Somesing like

if (!playerChoise.equalsIgnoreCase("paper") && ...) continue;

Or you can store valid choises in list as shown above, and use foreach loop. But it will be more difficult, becouse you need to check all variants. Maybe something like.

private boolean checkValid(String userChoise, String... variants) {
    for (String s : variants) {
        if (playerChoise.equalsIgnoreCase(s)) return true;
    }
    return false;
}

And call it for both cases:

if (!checkValid(userChoise, "rock", "papper", "scissors")) continue;

Upvotes: 1

Related Questions