⌛⌛ Word game

There is material for this question in the remote material repository:

  • The material is located at the round5/wordgame directory.

  • WordGameTest.java: a program to test your implementation.

  • words.txt: words known by the program.

  • input1.txt ja output1.txt: a test input and a matching output.

  • input2.txt ja output2.txt: a test input and a matching output.

Introduction

One classic word guessing game proceeds as described below. The game involves a gamemaster and a player.

  1. The gamemaster selects a word without showing it to the player. In addition a maximum limit for mistakes (wrong guesses) is defined. The game then proceeds by alternating steps 2 and 3 until either the player wins or the mistake limit is exceeded.

  2. The gamemaster displays the current status of the gamemaster’s word in such a manner, that all still unknown letters are hidden by showing the character ‘_’ in their place. Note that initially all letters are unknown and thus hidden.

  3. The player’s turn. The player may either try to guess a single letter or the whole word.

    1. If the player tries to guess the whole word, then:

      1. If the player’s guess differs even slightly from the gamemaster’s word, the guess is a mistake. The player’s mistake count is incremented by one and no new information is revealed to the player, unless the game ends (see below).

      2. If the guess was correct, the game ends in the player winning.

    2. If the player tries to guess a single character, then:

      1. If the character occurs in the gamemaster’s word and was still unknown, the gamemaster reveals all occurrences of the character in the gamemaster’s word and the character thus becomes known. If as a result all characters of the gamemaster’s word became known, the game ends in the player winning.

      2. If the character does not occur in the gamemaster’s word or was already known, the guess is a mistake. The player’s mistake count is incremented by one and no new information is revealed to the player, unless the game ends (see below).

    3. If the player’s mistake count exceeds the mistake limit, the game ends in the player losing and the gamemaster’s word is revealed completely to the player.

Implementation of the game

The exercise is returned as a Maven project. Place the pom.xml file in the round5/wordgame directory of your local repository and create to this directory the src/main/java subdirectory. Create class files WordGame.java and GameStateException.java and attach them to the fi.tuni.prog3.wordgame package. Your files should be in the round5/wordgame/src/main/java/fi/tuni/prog3/wordgame directory.

Implement the word guessing game as a class WordGame that has the following public members:

  • A static nested class WordGameState whose objects can store the current game state: the word, mistake count, mistake limit and the number of still missing characters (the number of ‘_’). There are differences between the inner classes (non-static nested class) and the static nested classes. For example, a static nested class can access only the static features of its outer class. However, you do not need to worry now about the qualities of this type of a nested class, because WordGameState does not need to know anything of its outer class WordGame. Just be sure to include the static modifier in the header of the class.

    • Public member functions String getWord(), int getMistakes(), int getMistakeLimit() and int getMissingChars() that return the respective values.

    • Define in WordGame a private attribute of the WordGameState type, if you want to utilise the WordGameState class as a storage of the data needed to implement the game logic. In this case, return the possibly updated value of the attribute from WordGame functions that return the state of the game. The other approach is to define additional private attributes in WordGame to store data of WordGameState and to create a WordGameState object based on the values of these attributes, when a game state needs to be returned. In the latter case, the WordGameState class is just a vessel to return attribute values and has no part in the game logic.

    • No public constructors! To prevent Java from automatically creating a public default constructor, write a private constructor.

  • A constructor WordGame(String wordFilename) that reads a list of words from the file specified by the parameter.

    • The file is expected to contain one word per line without extra white space.

    • Define in WordGame a private container attribute for storing the words. The ArrayList<String> container might be best suited for this task. In the constructor, create a container object and assign it as a value to the attribute. The container should store the words in the same order as they appear in the file.

    • When we later talk about the index of a word, we mean a zero-based index. The first word has index 0, the second word index 1, and so on. We denote the overall number of words by N.

    • Define in WordGame a private attribute that indicates whether the game is running (active) or not (inactive). The boolean type is recommended for this attribute. Initialise the attribute in the constructor with a value that symbolises a inactive game.

  • A member function void initGame(int wordIndex, int mistakeLimit) that initialises a new game. The gamemaster’s word is the word stored by the container attribute at index wordIndex % N. Define a private attribute in WordGame and set the selected word as the value of the attribute. The maximum mistake limit is mistakeLimit. This value can be stored either to the WordGameState object created and set as an attribute value here or to another attribute. Initialise also other relevant attributes, if you implement WordGame without a WordGameState attribute. A new game starts immediately regardless of whether a previous game was still active or not. Therefore, initialise the relevant attribute with a value that symbolises an active game.

  • A member function boolean isGameActive() that returns true if a game is currently active and otherwise false.

  • A member function WordGameState getGameState() that returns a WordGameState object that describes the current game state.

    • Throws an exception GameStateException("There is currently no active word game!"), if the game is inactive.

    • Return either the value of the WordGameState attribute or a WordGameState object created using the values stored in the attributes of WordGame.

  • A member function WordGameState guess(char c) that updates the game state in accordance to the player guessing the character c and returns a WordGameState object that describes the new state.

    • First, check that the game is active. Throw a GameStateException("There is currently no active word game!") exception, if the game is inactive.

    • Compare the guessed character to the characters of the gamemaster’s word in a case-insensitive manner. For example, the uppercase A and lowercase a are considered to be the same character.

    • The game becomes inactive if the whole word is now revealed, or the maximum number of wrong guesses has been exceeded. Update the value of the relevant attribute.

    • Return either the updated value of the WordGameState attribute or a WordGameState object created using the values stored in the attributes of WordGame.

  • A member function WordGameState guess(String word) that updates the game state in accordance to the player guessing the word word and returns a WordGameState object that describes the new state. This function behaves like the previous one except for the comparison. Instead of comparing individual characters, whole words are compared.

The description above mentioned exceptions of type GameStateException. You need to implement also this class in such a manner that it inherits the Java library class Exception and has only one member: a public constructor GameStateException(String msg) that only forwards msg to the constructor of its superclass. This is a very simple class.

The testing material provided below should clarify how the WordGame class is expected to work.

The automatic tests, and the ones given below, assume that you make the following definitions in your pom.xml project file:

  • The value of artifactId is wordgame.

  • The value of version is 1.0.

  • The values of the maven.compiler.source and maven.compiler.target elements are 17 or lower. The grader uses Java 17, so any newer versions won’t work.

  • A Onejar plugin definition where the value of mainClass is WordGameTest which is the name of the given test class (see below).

Testing

You may test your implementation by using the test program given in the file WordGameTest.java, the test data given in the files words.txt, input1.txt and input2.txt, and the example output given in the files output1.txt and output2.txt.

Set WordGameTest.java into the root of the src/main/java subdirectory of your Maven project, and the other files into the root directory of your Maven project, that is, where the pom.xml is. Note that WordGameTest.java does not include a package definition and therefore is not placed into a deeper subdirectory.

After this you can compile the program with mvn package and run the test X as java -jar target/wordgame-1.0.one-jar.jar words.txt inputX.txt in the root directory of the project. The test number X should produce the output depicted in the corresponding file outputX.txt. Note that all output is produced by the test program. The functions of the WordGame class do not print anything.

A+ presents the exercise submission form here.