Motivation
When programming games or other software systems, it is very important to understand how the user will use the application and how they will interact with it. In the case of games, it is especially important to identify the phases of the game loop, the actions the player can trigger, and the effects these actions may have on the game. You will focus on these key tasks when implementing the console user interface.
To implement a console user interface for a game, tile-based games require the following three methods:
play(Field field)- the game loop that combines printing the text representation of the game field and interacting with the user,show()- used to print the text representation of the game field, called after each game move,handleInput()- processes user input and performs the corresponding action on the game.
Objectives
- Implement the game loop in which the game will run.
- Implement printing of the text representation of the game field.
- Implement processing of user input and communication with the user.
Instructions
Step 1
Task 1.1
Implement the method providing the functionality of the game loop, e.g. play(Field field), in the class representing the console user interface, e.g. ConsoleUI.
The method should perform the following actions:
- initialize the private
fieldvariable, which will be used as the internal model of the game, - in a loop:
- print the text representation of the game field to the console using the
show()method, - read and process user input using the
handleInput()method, - react to changes in the game state (
FAILED,SOLVED, etc.).
- print the text representation of the game field to the console using the
Comment
If needed, split the source code into additional private helper methods to improve readability.
Example implementation of the play method for the game Minesweeper:
public void play(Field field) {
this.field = field;
do {
show();
handleInput();
} while(field.getState() == GameState.PLAYING);
show();
if(field.getState() == GameState.SOLVED) {
System.out.println("Solved!");
} else if(field.getState() == GameState.FAILED){
System.out.println("Failed!");
}
}
Step 2
For displaying the game field on standard output, the following requirements apply in the case of the game Minesweeper:
- Rows are labeled with uppercase letters in alphabetical order (A, B, ..., I).
- Columns are labeled with numbers (0, 1, ... , 8).
- To represent an uncovered tile (
OPEN) of type mine (Mine), use the characterX. - To represent an uncovered tile (
OPEN) of type clue (Clue), use the number representing the tile value. - To represent a marked tile (
MARKED), use the characterM. - To represent an unmarked tile (
CLOSED), use the character-.
For tile-based games, we recommend proceeding in a similar way: define how the game field will be rendered (a two-dimensional matrix, a one-dimensional array printed horizontally or vertically, card games arranged according to players' positions or card layout rules). Then define the encoding of individual cells or cards using letters or special characters, similarly to the game Minesweeper: M, -, and numbers for clues.
The data needed to display individual tiles can be obtained from the game logic (in our case, the Field class instance).
Task 2.1
Implement the rendering of the game field in the void show() method in class ConsoleUI.
Comment
To print formatted output (for example, when displaying row labels), you can use the System.out.printf(...) method, which can format output to a predefined number of characters. For example, System.out.printf("%3d", 4) prints the number 4 in a field of width 3. Look up the method in the Javadoc for more details.
Comment
To make it easy to print the text representation of the game field to standard output, define the text representation of individual tile classes (e.g. Mine and Clue) by overriding the standard toString() method. For example:
public class Mine {
@Override
public String toString() {
return "X";
}
}
Step 3
Task 3.1
Implement the handleInput() method in class ConsoleUI, which is used to process user input.
In the game Minesweeper, the handleInput() method performs the following actions:
- Prints a prompt for entering input with the expected input format:
X– end the game, individual moves such asMA1– mark the tile in row A and column 1,OB4– open the tile in row B and column 4. - Reads the input from the user (
nextLine()). - Verifies the correctness of the input string using regular expressions.
- If the input is not in the required format, asks the user to enter it again.
- Performs the corresponding operation (mark tile, open tile, end the program) based on the identified user action.
Comment
To define the input pattern using a regular expression, create an instance of the Pattern class (for example, Pattern.compile("O([A-I])([0-8])") for input in the form OA9 to open a tile). To verify that the input matches the pattern, use an instance of the Matcher class. A Matcher instance can be obtained using the Matcher matcher(CharSequence input) method of the Pattern object. Use the methods boolean matches() and String group(int group) defined in the Matcher class to validate the input.
Step 4
Task 4.1
Upload (commit and push) your implementation to your GitLab repository. You can continue updating the project gradually. Before the next exercise, make sure your latest files are in the repository. Also prepare questions you would like to discuss in the next exercise.
Additional Tasks
Task A.1
For better usability, accept user input in both uppercase and lowercase (for example, in Minesweeper, both oa9 and OA9).
Task A.2
Implement support for starting a new game after winning or losing:
Congratulations, you won!
Do you wish to start a new game (Y/N)? _
Resources
- Java Tutorial dedicated to regular expressions.