Question 2: Classes

“Students will be asked to write program code to define a new type by creating a class and satisfy method specifications using expressions, conditional statements, and iterative statements.”

This is a pretty easy to identify “Classes” question. The question asks us to create just one class with a specific constructor and a method with a specific function, which will require conditionals and iteration in determining how to analyze the word.

Question Description

Consider a guessing game in which a player tries to guess a hidden word. The hidden word contains only capital letters and has a length known to the player. A guess contains only capital letters and has the same length as the hidden word.

After a guess is made, the player is given a hint that is based on a comparison between the hidden word and the guess. Each position in the hint contains a character that corresponds to the letter in the same position in the guess.

The HiddenWord class will be used to represent the hidden word in the game. The hidden word is passed to the constructor. The class contains a method, getHint, that takes a guess and produces a hint.

For example, suppose the variable puzzle is declared as follows.

HiddenWord puzzle = new HiddenWord("HARPS");

Write the complete HiddenWord class, including any necessary instance variables, its constructor, and the method, getHint, described above. You may assume that the length of the guess is the same as the length of the hidden word.

Code

The entire class’s code is shown below.

public class HiddenWord {
    private String word;

    public HiddenWord(String word) {
        this.word = word;
    }

    private String getHint(String guess) {
        String output = "";
        for (int i = 0; i < this.word.length(); i++) {
            String thisLetter = guess.substring(i, i + 1);
            if (this.word.indexOf(thisLetter) > -1) {
                if (this.word.substring(i, i + 1).equals(thisLetter)) {
                    output += thisLetter; // letter found in the proper place
                } else {
                    output += "+"; // letter found elsewhere in word
                }
            } else {
                output += "*"; // letter not found anywhere
            }
        }
        return output;
    }

    public static void main(String[] args) {
        HiddenWord puzzle = new HiddenWord("HARPS");

        System.out.println(puzzle.getHint("AAAAA"));
        System.out.println(puzzle.getHint("HELLO"));
        System.out.println(puzzle.getHint("HEART"));
        System.out.println(puzzle.getHint("HARMS"));
        System.out.println(puzzle.getHint("HARPS"));
    }
}

HiddenWord.main(null);
+A+++
H****
H*++*
HAR*S
HARPS

The required code alone is hardly a finished word game, however. Here’s a version of the class that contains an actually playable word guessing game, with error handling for invalid guesses (which were specifically asked to be not accounted for in the CB question).

public class HiddenWordGame {
    String word;

    public HiddenWordGame(String word) {
        this.word = word;
    }

    private String getHint(String guess) {
        String output = "";
        if (guess.length() != this.word.length()) {
            System.out.println("Invalid guess. Your guess must be " + this.word.length() + " characters long.");
            return null; // returning for invalid guess
        }
        for (int i = 0; i < this.word.length(); i++) {
            String thisLetter = guess.substring(i, i + 1);
            if (this.word.indexOf(thisLetter) > -1) {
                if (this.word.substring(i, i + 1).equals(thisLetter)) {
                    output += thisLetter; // letter found in the proper place
                } else {
                    output += "+"; // letter found elsewhere in word
                }
            } else {
                output += "*"; // letter not found anywhere
            }
        }
        return output;
    }

    private void guessingGame() {
        // starting guessing loop
        Scanner scanner = new Scanner(System.in);
        int numberOfGuesses = 0;
        boolean guessing = true;
        while (guessing) {
            numberOfGuesses++;
            System.out.println("\nGuess a " + this.word.length() + "-letter word.");
            String guessWord = scanner.nextLine().toUpperCase();
            String hint = this.getHint(guessWord);
            if (hint != null) { // if guess is invalid, null is returned
                if (hint.equals(this.word)) {
                    System.out.println("Congratulations! You guessed the word (" + this.word + ") in " + numberOfGuesses + " guess" + (numberOfGuesses != 1 ? "es." : "."));
                    guessing = false; // ending loop if the game is won
                } else {
                    System.out.println("Here is your hint: " + hint); // hint if the guess wasn't right
                }
            }
        }
        scanner.close();
        return;
    }

    public static void main(String[] args) {
        // starting scanner and game
        boolean playing = true;
        while (playing) {
            Scanner scanner = new Scanner(System.in);
            System.out.println("Welcome to Almost Wordle! Please enter a word to be guessed.");
            String gameWord = scanner.nextLine();
            HiddenWordGame puzzle = new HiddenWordGame(gameWord.toUpperCase());
            puzzle.guessingGame();
            
            // possible play again option
            System.out.println("Would you like to play again? (Enter \"y\" or \"n\")");
            String playAgain = scanner.nextLine();
            if (!(playAgain.equals("y"))) {
                System.out.println("Thanks for playing!");
                scanner.close();
                playing = false;
            } else {System.out.println();}
        }
    }
}

HiddenWordGame.main(null);
Welcome to Almost Wordle! Please enter a word to be guessed.

Guess a 5-letter word.
Invalid guess. Your guess must be 5 characters long.

Guess a 5-letter word.
Here is your hint: H*++*

Guess a 5-letter word.
Here is your hint: HAR*S

Guess a 5-letter word.
Congratulations! You guessed the word (HARPS) in 4 guesses.
Would you like to play again? (Enter "y" or "n")

Welcome to Almost Wordle! Please enter a word to be guessed.

Guess a 6-letter word.
Here is your hint: CHEET*

Guess a 6-letter word.
Congratulations! You guessed the word (CHEETO) in 2 guesses.
Would you like to play again? (Enter "y" or "n")
Thanks for playing!

Reflection

There wasn’t much complexity to this problem aside from the limitations that prevented me from using the “char” class with only the provided String object methods from the Java Quick Reference, which forced me to use .substring throughout instead. I could be wrong, but since none of the specifically provided String object methods allow for conversion to an array of characters, which is how I’ve previously iterated through Strings like this, I don’t think College Board intends for the char primitive to be derived from a String. In the code below, I prevent unnecessary calls to substring by defining the relevant substring of the guess once and referring to the variable throughout.

String thisLetter = guess.substring(i, i + 1);
if (this.word.indexOf(thisLetter) > -1) {
    if (this.word.substring(i, i + 1).equals(thisLetter)) {
        output += thisLetter; // letter found in the proper place
    } else { // etc.

By referencing the Java Quick Reference guide, I was able to find the method .indexOf (shown above) for String that I hadn’t ever used before. Using this method allowed me to check if a certain character was contained in the word at all before checking if it was in the proper place, with a larger else statement connected that added a “*” if it wasn’t contained in the word. This made it more convenient to use .substring, too, as the .indexOf method allowed by the Java Quick Reference takes a String argument.

Aside from that, there wasn’t much complexity to speak of in this problem aside from in my larger, fully-functional rendition of the game in the block above. I’m not sure if we’re allowed to use Scanner in College Board responses, but that added complexity to the overall interactions with the getHint method. I also added an error handler for invalid word length below.

if (guess.length() != this.word.length()) {
    System.out.println("Invalid guess. Your guess must be " + this.word.length() + " characters long.");
    return null; // returning for invalid guess
}

I was able to return null instead of a String here to ensure that no logic is performed on an invalid guess, and there’s even a conditional check if the input was valid in the custom game method I made. Some other error prevention came in the form of using .toUpperCase on all word inputs, which the question just asks to be implied.

Overall Notes and Reminders

  • The Java Quick Reference provided by College Board provides many potentially useful methods that save some time.
  • null can be output in place of the expected type of output for a method, but ensure that any logic performed on the output of that method accounts for the possibility of returning null.
  • If a String is not initialized with a value, unlike how it works with creating an array of integers, for example, the String will be a null until specified otherwise.
  • There weren’t many takeaways from this CB problem overall, but in general, it’s important to ensure that all class methods speak to each other as intended.