TP 3
Halma controller implementation.
Meta
- Deadline: Monday Apr 7th, 11:59 PM
- Submission: GitLab
- Teams: Triplets of students, as announced in class
- Late submission: Not possible. You will be graded by whatever contents is on your main branch at the deadline
It is your responsibility to submit code in a timely manner. Do not wait until the last minute, GitLab may fail.
Objectives
-
For the third TP you will complete your Halma
Controller
implementation, and integrate advanced concepts to ensure code quality into the build process: -
You have to implement your own tests and ensure advanced test coverage with a build configuration.
- Your code must comply to industry grade code formatting rules.
- Your code must be fully documented with JavaDoc comments for all public methods and classes.
-
Your code must not be overly complex.
-
You will start with a provided maven configuration that enforces some standards. You will then extend this configuration to enforce additional code quality standards.
- Your build configuration will produce a self-contained deliverable, which is a first playable version of your game.
Context
For the TP-1 and TP-2 milestone, you already implemented the Halma model, and parts of the Halma controller. Your submission has been graded based on a set of predefines interface tests, as well as code standards.
Carefully read the test report
The submission tests used for grading contained additional unit test scenarios. Carefully read the test report and make sure to fix any potential issues in your model. Depending on your model implementation quality, you might need to invest a little extra time for milestone 3.
Instructions
Several changes are necessary to pass this third milestone. In the following comes a detailed breakdown of what is expected.
Project configuration files
Update your launcher and xml files
Update your configuration, or your game will NOT be playable. You will lose MASSIVE points if you ignore this.
- Visit the
tp3-files
branch in your repo and merge bothgoogle_checks.xml
andpom.xml
into your codebase. - In the remainder you will also be asked to add three additional plugins to the
pom.xml
configuration files. Note that these are the only changes allows, and any other modification of thepom.xml
will lead to a desk-rejection.
Especially not allowed is:- Integrating other JUnit versions.
- Integration additional libraries.
Runtime arguments
At the moment of submission, your star-shaped game implementation must be playable using the provided launcher and textual UI / command line client. The same holds for the JAR file produced by your build configuration.
On start, your game must be able to process the following runtime arguments:
- board base-size, for example
1
, to produce a star with base size1
:
y\x | 00 01 02 03 04
----+--------------------
00 | [ ] [ ]
01 | ( )
02 | ( ) ( )
03 | [ ] ( ) [ ]
04 | ( ) ( )
05 | ( )
06 | [ ] [ ]
-
Names of involved players. These must be either 3 or 6. Other inputs must be rejected.
-
Example: Your program must be launchable with the following commands:
mvn exec:java "-Dexec.args=1 Max Quentin Hafedh"
-> Creates 5*7 star shaped cell grid with 3 players.java -jar target/halma.jar 1 Max Quentin Hafedh
-> same but starting from JARmvn exec:java "-Dexec.args=2 Max Quentin Ryan Hafedh Maram Roman"
-> Creates 9*13 star shaped cell grid with 6 players.java -jar target/halma.jar 2 Max Quentin Ryan Hafedh Maram Roman"
-> same but starting from JAR
Sample launcher
Your game will be advanced by a main control loop, that (until game end) does the following:
1) Determine valid moves for current player. (Blackboard pattern see explanations in class.) 2) Present valid moves on console and prompt player for a choice. 3) Feed choice back to controller and advance game state, based on the player's selection.
You are not required to implement UI / prompting functionality, so you can concentrate on the game-logic components. Classes for printing and prompting options are provided, below is a short sample code to illustrate how to use the provided functionality.
Blackboard pattern control loop
Below is a sample implementation to keep the game going until game end. Feel free to use the provided UI classes for printing game state and retrieving user choices via command prompt.
private static void runTp03(String[] args) {
// Parse runtime parameters
int baseSize = Integer.parseInt(args[0]);
String[] playerNames = // ... trouvez une façon d'interpréter les arguments.
// Appellez votre implementation "StarModelFactory" ici.
ModelFactory modelFactory = new StarModelFactory();
// Set move selectors // ... plus tard on va utiliser des IA pour choisier des actions.
// Pour l'instant le choix des options est interactif (et l'implémentation est fournie)
MoveSelector[] moveSelectors = playerNamesToMoveSelectors(playerNames);
// Initialize controller
Controller controller = new ControllerImpl(modelFactory, baseSize, playerNames);
// Initialize visualizer
boolean useColours = true; // Set to false if you're on windows and textual output looks weird.
TextualVisualizer visualizer = new TextualVisualizer(useColours);
// Proceed until game end
while (!controller.isGameOver()) { // Voila la boucle prinicipale... continuer le jeu jusqu'à une personne a gagné
printAndRequestAndPerformAction(controller, visualizer, moveSelectors);
}
System.out.println(visualizer.stringifyModel(controller.getModel()));
System.out.println("GAME OVER!");
}
// Plus tard (TP4), quand on utilise des IAs, il y aura des autres "MoveSelectors" qui ne sont pas interactifs mais font leur propre choix.
private static MoveSelector[] playerNamesToMoveSelectors(String[] playerNames) {
MoveSelector[] moveSelectors = new MoveSelector[playerNames.length];
for (int i = 0; i < moveSelectors.length; i++) {
moveSelectors[i] = new InteractiveMoveSelector();
}
return moveSelectors;
}
/**
* Prints mode, possible moves and requests player interaction.
*/
private static void printAndRequestAndPerformAction(Controller controller, TextualVisualizer visualizer,
MoveSelector[] moveSelectors) {
// Clear the screen (Works only on native ANSI terminals, not in IDE / windows)
visualizer.clearScreen(); // Comment out this line if you're on windows.
// Retrieve and visualize model
System.out.println(visualizer.stringifyModel(controller.getModel()));
// Print all possible actions:
System.out.println(visualizer.getCurrentPlayerAnnouncement(controller.getModel()));
System.out.println(visualizer.announcePossibleMoves(controller.getPlayerMoves()));
// Request action and visualize choice (for current player)
int currentPlayer = controller.getModel().getCurrentPlayer();
List<Move> availableMoves = controller.getPlayerMoves();
// if more than one move, ask selector
Move selectedMove = null;
if (availableMoves.size() > 1) {
selectedMove = moveSelectors[currentPlayer].selectMove(availableMoves);
} else {
// If only one move available, directly apply it.
selectedMove = availableMoves.getFirst();
}
System.out.println(visualizer.getChoseMoveAnnouncement(selectedMove, currentPlayer));
// Perform selected action:
controller.performMove(selectedMove);
System.out.println("\n\n");
}
Code
Your submission must provide implementations for all previously unimplemented interface methods of the model and controller package.
getPlayerMoves
: Determine where a player can move their figures. (See illustrations at end of document)isGameOver
: Tell whether a player has all their figures in their target zone.performMove
: Requests controller to modify model, by executing a provided move.
Don't reinvent the wheel
No need to implement the Move
class or Field
class. Those are provided to you, as part of the pre-configured dependencies.
Tests
Your test code
You are not required to=> You must add a new class, extending the existingextend
more abstract classes than before.AbstractStarControllerTest
class to gain access to the provided tests, and allow for your submission to be testable.- To do so, create a new java class in
src/test/java/ca/uqam/info/solanum/students/halma/controller/StarControllerTest.java
(Double check it is in your test dossier) - Your class must extend the existing abstract class and provide an implementation for the abstract
getController
method:
- However, you will need to write more unit tests for your new controller code, to reach the requested test coverage ratio.
Tests
As with the previous TP, all provided tests must pass, but I will test your submission beyound the provided tests.
Coverage
- Your controller and model code must reach a coverage of at least 90% lines of code.
- For the TP3, you are expected to integrate the
jacoco
plugin into yourpom.xml
configuration to reach the requested coverage. Your build must be configured to fail if the coverage is not reached. - If you are not familiar with
jacoco
, revise the CI lab session. A sample configuration is provided.
Build artifacts
- Your build configuration must be configured to provide a self-contained jar, i.e. a jar file that can be used as-is, without further dependencies.
- This jar has to be named
halma.jar
orHalma.jar
. - You can test the jar-file produced by the
package
phase, using the above run instructions. - To produce a self-contained jar, you must edit the
pom.xml
to make use of two maven plugins: (samples have been demonstrated in class and are available on GitLab)- The
maven-assembly
plugin - The
copy-rename
plugin
- The
Existing documentation
- For any question regarding the functionality requested by your implementation, consult
the
JavaDoc
of the existing interfaces, records and custom exceptions
Your JavaDoc Documentation
Your code must be 100% documented:
- All non-private classes must have a JavaDoc description
- All non-private methods must have a JavaDoc description
- If a method implements an existing interface, no additional documentation should be provided, as the interface is
already documented. Simple place an
@Override
annotation before the method signature.
Use maven
Use mvn clean package
to verify the state of your JavaDoc comments. It will tell you about missing or incorrect documentation.
Clutter
- Keep your repository clutter-free. (See TP0 + TP1 + TP2)
- Unless already done, can use a
.gitignore
file that excludes class files, OS hidden files and maven's target folder. - Do not commit binary or generated files (
.class
files,target
folder, or generatedjavadoc
) - Do not add any
System.out.println
statements to your code, this will break functionality in future TP milestones.
Plagiarism and ChatGPT
- Unless explicitly allowed, do not use ChatGPT or other generative tools to generate code or documentation.
- You can query the internet to find inspiration or help your understanding, but never copy-paste code without citing the source. Whenever you copy-paste, you MUST provide a reference / comment. Otherwise, it is legally considered plagiarism.
- Use a Java-comment
// ...
to provide the source, e.g. the exact stackoverflow post - Do not share code with other teams
- You can discuss, you can draw concepts on a piece of paper, but never pass-on code.
- I will run automated plagiarism checks on all submission.
- I am contractually obligated to report teams who copied to the university administration. This can have severe consequences for your studies, including lifetime expulsion form studies in Canada.
Plagiarism
Plagiarism, i.e. submitting work that is not your own (i.e. your team's), is a serious academic offense that can entail to expulsion from studies. Do not take any risks.
Contribution statement
You must include a contribution statement, which we may consider in case of sever imbalance between team member contributions. Use the template below, to include a file "contributions.md" at top level in your repo.
Other file names or file formats are not accepted (PDF, pages, html, ...). Make sure to name your
file contributions.md
As file content for your contributions.md
, use exactly the below template. Do not recreate the grid in another
software (excel, numbers, paint, ...). Use your code-permanent as ID
.
| ID | First | Last | Contribution (%) |
|-----|-------|------|--------------|
| ... | ... | ... | ... |
| ... | ... | ... | ... |
| ... | ... | ... | ... |
Cumulative contributions of all team members must add up to 100%.
Grading scheme
This TP is worth 15% of your total grade.
Criteria | Max percentage |
---|---|
Cyclomatic complexity limit respected, no checkstyle warnings | 25% |
Undisclosed additional tests pass | 20% |
Functional JAR produced by package phase |
15% |
Test coverage > 90%, tested with jacoco | 15% |
Provided tests pass | 10% |
Javadoc enforced in pom, no warnings | 5% |
Repo clutter free | 5% |
Contribution statement | 5% |
Recommendation
Simulate the grading process before submission: Re-clone your own project and run mvn clean package
and ensure your project can be built without warnings or errors, using the pom.xml
provided for this TP3
.
Desk rejection
Here is a checklist of what to avoid at all costs. If at least one item of the checklist applies, your submission will lose all code-related points. There may be other criteria for desk rejection.
- Solution not provided in dedicated GitLab directory, or not on
main
branch. - Solution is distributed across multiple unmerged branches.
- Repository contains zip file.
- Code does not compile with provided maven command.
pom.xml
has been semantically modified beyond integration ofjacoco plugin
/assembly plugin
/copy-rename plugin
. (Compared to TP3 specific, providedpom.xml
)- Provided classes do not implement provided interfaces.
- Implementation of abstract test classes is not provided in test package.
- Project structure not respected.
- Program attempts network communication at runtime.
- Program stalls at runtime.
- Copied code from ChatGPT / used Cursor / copied from other generative AI.
- Copied from other teams.
- Code copied from forums / online knowledge bases without giving credit (citation).
Additional illustrations
This section recapitulates games rules and illustrates the default UI representation of moves.
Game rule illustrations
Upon their turn, a player can only move one figure. However, if the figure has been moved, using a "jump", further repeated jumps with the same figure are allowed.
Certain rules must be respected when moving a figure:
- The target field must be vacant.
- The target field must be on the star shaped board (i.e. it is not allows to move figures off the terrain).
- Jumps are only allows to extended neighbours (the field behind a field, i.e. distance 2). Additionally, jumps are only allowed if the intermediate field is not vacant. However, if not vacant, it does not matter to which player the intermediate figure belongs.
Controller moves
This section provides additional illustration for how the getMoves
method is expected to operate.
If a player performed a jump, i.e. previously moved a figure by jumping across another figure, the game does not pass on to the next player. However...
- For the remainder of the player's turn, all follow-up moves (as proposed by the
getMoves
method) must only list the previously moved figure. - The player is not allows to perform anti-jumps, i.e. the
getMoves
result must not contain a move reverting a figure back to the field it just came from. Larger circles that lead back to the original Field are allowed though. - The player is not obligated to keep jumping their figure, even if possible. To cover this option,
getMoves
must include a special "Null-move", which has identical source and target field (current position of the figure). - To end their turn after having done at least one jump, the player must explicitly use a final "Null-move".
Do not over-optimize
It is possible that only a single move is possible (e.g. ending a series of jumps with a "Null move"). The controller must not automatically apply alternative-free options, but still return single possible options. The provided blackboard sample code does however include a check to automatically select moves if they are free of alternatives.
Example 1
Implicitly selecting the only option offered by controller's getMoves
method:
* After the initial jump, the controller's getMoves
method returned only a single option: the Null-move
.
* However, the controller did not implicitly apply the only available move.
* It is the UI's blackboard loop, which did not ask the player to confirm (their only) option, as it is free of alternatives.
$ java -jar halma.jar 2 Max Ryan Roman
y\x | 00 01 02 03 04 05 06 07 08
----+------------------------------------
00 | [ ] [1]
01 | [ ] [1]
02 | [ ] ( ) [1]
03 | ( ) ( )
04 | ( ) ( ) ( )
05 | [0] ( ) ( ) [ ]
06 | [0] ( ) ( ) ( ) [ ]
07 | [0] ( ) ( ) [ ]
08 | ( ) ( ) ( )
09 | ( ) ( )
10 | [ ] ( ) [2]
11 | [ ] [2]
12 | [ ] [2]
It's Max's turn. Max, your options are:
Available moves:
00: (01,05) -> (02,04) 01: (01,05) -> (02,06) 02: (00,06) => (02,04)
03: (00,06) => (02,08) 04: (01,07) -> (02,06) 05: (01,07) -> (02,08)
Enter move ID:
3
Chosen move: (00,06) => (02,08)
y\x | 00 01 02 03 04 05 06 07 08
----+------------------------------------
00 | [ ] [1]
01 | [ ] [1]
02 | [ ] ( ) [1]
03 | ( ) ( )
04 | ( ) ( ) ( )
05 | [0] ( ) ( ) [ ]
06 | [ ] ( ) ( ) ( ) [ ]
07 | [0] ( ) ( ) [ ]
08 | (0) ( ) ( )
09 | ( ) ( )
10 | [ ] ( ) [2]
11 | [ ] [2]
12 | [ ] [2]
It's Max's turn. Max, your options are:
Available moves:
00: (02,08) == (02,08)
Chosen move: (02,08) == (02,08)
y\x | 00 01 02 03 04 05 06 07 08
----+------------------------------------
00 | [ ] [1]
01 | [ ] [1]
02 | [ ] ( ) [1]
03 | ( ) ( )
04 | ( ) ( ) ( )
05 | [0] ( ) ( ) [ ]
06 | [ ] ( ) ( ) ( ) [ ]
07 | [0] ( ) ( ) [ ]
08 | (0) ( ) ( )
09 | ( ) ( )
10 | [ ] ( ) [2]
11 | [ ] [2]
12 | [ ] [2]
Example 2
Omitting an anti jump:
* After a jump move (00,06) => (02,08)
, the player has only two options:
1. continuing with another jump (upwards): (02,08) => (02,04)
2. ending their turn, by an explicit Null-move: (02,08) == (02,08)
3. The anti-jump, i.e. returning to (00,06)
, is not allowed.
4. Moving any other figure than the one who previously jumped is not allowed.
y\x | 00 01 02 03 04 05 06 07 08
----+------------------------------------
00 | [ ] [ ]
01 | [ ] [1]
02 | [ ] (1) [1]
03 | ( ) ( )
04 | ( ) ( ) ( )
05 | [ ] ( ) ( ) [ ]
06 | [0] (0) ( ) ( ) [ ]
07 | [0] ( ) ( ) [ ]
08 | ( ) ( ) (2)
09 | ( ) ( )
10 | [ ] ( ) [ ]
11 | [ ] [2]
12 | [ ] [2]
It's Max's turn. Max, your options are:
Available moves:
00: (02,06) -> (02,04) 01: (02,06) -> (03,05) 02: (02,06) -> (01,05)
03: (02,06) -> (03,07) 04: (02,06) -> (02,08) 05: (00,06) -> (01,05)
06: (00,06) => (02,08) 07: (01,07) -> (01,05) 08: (01,07) => (03,05)
09: (01,07) -> (02,08)
Enter move ID:
6
Chosen move: (00,06) => (02,08)
y\x | 00 01 02 03 04 05 06 07 08
----+------------------------------------
00 | [ ] [ ]
01 | [ ] [1]
02 | [ ] (1) [1]
03 | ( ) ( )
04 | ( ) ( ) ( )
05 | [ ] ( ) ( ) [ ]
06 | [ ] (0) ( ) ( ) [ ]
07 | [0] ( ) ( ) [ ]
08 | (0) ( ) (2)
09 | ( ) ( )
10 | [ ] ( ) [ ]
11 | [ ] [2]
12 | [ ] [2]
It's Max's turn. Max, your options are:
Available moves:
00: (02,08) == (02,08) 01: (02,08) => (02,04)
Enter move ID:
Move syntax samples
Here is a short reminder of how to read serialized Move
objects:
(00,00) -> (01,01)
: Standard move, moving figure on field to an adjacent neighbour.(00,00) => (02,02)
: Jump move, moving figure on extended field to an adjacent neighbour. Can only be performed if the field in-between is not vacant.(00,00) == (00,00)
: Null-move: Used to indicate end a series (or a single) jump. The figure stays where it is.
Note: Anti-jumps are not allowed in any case, i.e. a jump move must not be followed by a jump back to the original position. This is a design decision to limit "endless" player turns where a figure jumps back and forth.