TP 3
Controller finalization, integration tests and advanced code quality requirements.
Meta
- Deadline: Sunday Apr 12th, 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.
Learning goals
In this third TP you will learn how to apply advanced code-quality requirements on existing and new code. In addition, you learn how to use robotic players as integration tests, to guide a correct realization of game rules.
More concretely, for the third TP you will...
-
complete your Skyjo
Controllerimplementation, and integrate advanced concepts to ensure code quality into the build process. All code quality requirements must by enforced viapom.xml. -
Your code must be tested with classic and mutation tests. You must ensure a requested coverage and mutation coverate is met.
- Your code must comply to industry grade code formatting rules, and respect a requested complexity threshold.
-
Your code must be fully documented with JavaDoc comments for all public methods and classes.
-
use provided integration tests to guide the completion of your controller implementation, i.e. implementation of game rules.
-
Your game implementation must support all game actions correctly, in any game situation.
- Provided decision-makers (deterministic robot players) will serve as grounds to verify if your model and controller implementation correctly implement the rules. These robotic players are used within provided integration tests. If an integration test fails, you must compare the expected and factual game execution paths to identify and fix potential implementation errors.
Step-by-step overview
- Corrections: Check your previous submission.
- Use the feedback returned to you on GitLab (may still take a few days, you can already re-run your tests).
- Fix any issues, especially failing tests. Use the debugger.
- Code quality:
- Enable cyclomatic complexity via
pom.xml. - Enable strict javadoc verification via
pom.xml. - Raise code line coverage to
85%viapom.xml. - Enable mutation testing, enable a minimum mutation test coverage via
pom.xml.
- Enable cyclomatic complexity via
- Controller code: Complete your controller implementation, so the game is fully playable.
- Integration tests:
- Separate integration tests from regular tests using the
*ITsuffix viapom.xml. - Extend the provided abstract integration test class to enable integration tests, and make sure all integration tests pass.
- Separate integration tests from regular tests using the
You can interleave 3. & 4.
Integration tests are also an efficient means to guide implementation (see TDD), i.e. you can first enable integration tests, and then gradually finalize your codebase, until all integration tests run through. The provided integration tests are also gradual, i.e. you can start with a short integration test that only plays a few turns, and later proceed to the more complex versions.
Detailed instructions
Several changes are necessary to pass this third milestone. In the following comes a detailed breakdown of what is expected.
Corrections
- Fix / see tp2 instructions...
Code quality
Cyclomatic complexity
Strict javadoc verification
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
@Overrideannotation 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.
Line 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
jacocoplugin into yourpom.xmlconfiguration 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.
Mutation coverage
Controller code
Description of blackboard / selectors here. Description of expected command order here. Reference to sample games (see integr. tests) here.
- For any question regarding the functionality requested by your implementation, consult
the
JavaDocof the existing interfaces, records and custom exceptions
Blackboard architecture
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.
Skyjo rules as flowchart
- Translating (or reverse engineering) game rules into a program execution path would be beyond the scope if this undergrad course.
- To help you with your implementation, here's a flowchart illustrating which actions are possible at any given moment:
flowchart TD
K(Start) --> |**Reveal deck** card| L(Card in buffer)
L --> |"**Reveal** player card<br>(reject buffer)"| M(Player card replaced or revealed)
L --> |"**Replace** player card<br>(using buffer)"| M
M --> |Eliminate<br>col / row| M
M --> |Cede|O(End)
K --> |"**Replace** player card<br>(using deck)"| M
- The above graph directly guides your implementation: At any state (box), there the current player has one or multiple
options (arrows).
- One arrow can translate to multiple option instances, e.g. revealing a card can be applied to any player card.
- Order is important: Options must always be sorted, so the integration tests (e.g. always picking the first available option) reliably take the same execution path.
- Concerning order: If there are multiple arrows, go from left to right. When there are multiple option instances, sort them based on the card they refer to (top left to bottom right).
Use the provided samples
The above definition is still somewhat formal, so you might seek for examples. Luckily there are plenty! You can use the provided sample game logs to inspect which exact options must be offered to a player at exemplary game states. See link in next section.
Integration tests
Locate and separate
How to separate from classic tests / where + suffix
Activate
How to enable / what must be extended
- You must add a new class, extending the
existing
AbstractStarControllerTestclass 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
getControllermethod:
- To do so, create a new java class in
public class StarControllerTest extends AbstractStarControllerTest {
@Override
public Controller getController(int baseSize, String[] playerNames) {
return new ControllerImpl(...); // The parameters of this constructor call vary, depending on your implementation.
}
}
Verify phase
How to activate via maven after separation, and how to exclude from mutation testing.
Grading scheme
TODO: UPDATE
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
mainbranch. - Solution is distributed across multiple unmerged branches.
- Repository contains zip file.
- Code does not compile with provided maven command.
pom.xmlhas 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).
MISC
Launcher Code
Testing does not require a launcher code, however it may be convenient to have a functioning launcher, that allows
manually playing your implementation.
Use the below launcher code for your TP3 implementation. Only two changes to the code below are needed:
importof yourControllerImplclass.- Using your
ControllerImplconstructor to create new game instances.
package ca.uqam.info.max.skyjo.view;
import ca.uqam.info.max.skyjo.controller.Command;
import ca.uqam.info.max.skyjo.controller.Controller;
import ca.uqam.info.max.skyjo.controller.ModelPreset;
// TODO: Import YOUR ControllerImpl here.
/**
* Launcher for a textual / TTY session with all physical players sharing one keyboard / screen.
*
* @author Maximilian Schiedermeier
*/
public class LauncherTp3 {
/**
* Default constructor, as imposed by javadoc.
*/
public LauncherTp3() {
}
/**
* Replace command selector by robot players to obtain an automated game (also used for
* integration testing).
*/
private static CommandSelector commandSelector;
/**
* Starts game by creating a new controller (which in turn creates a new model). Then keeps
* prompting players for choices until game end is reached.
*
* @param args not used.
*/
public static void main(String[] args) {
// Register UI to automatically refresh on model updates
boolean useTtyColours = true;
// Create a model, using your model constructor.
// Make sure your model implements the provided model readonly interface
String[] playerNames = new String[] {"Max", "Ryan", "Maram", "Quentin"};
// TODO: Initialize a new game, using YOUR controller implementation.
Controller controller = ... // Something like "new ControllerImpl(...)";
// Register UI to automatically refresh on model updates
controller.initializeModel(ModelPreset.DEFAULT, playerNames, null);
controller.addModelObserver(new TextualVisualizer(controller.getModel(), useTtyColours));
// Register UI to automatically refresh on model updates
controller.addModelObserver(new TextualVisualizer(controller.getModel(), useTtyColours));
// Initialize commandSelector for interactive / TTY mode
commandSelector = new TextualCommandSelector(useTtyColours, false);
// Play the game :)
playUntilGameEnd(controller);
}
/**
* Note: This method is not concerned with updating model state representations, for the model
* adheres to the observer pattern for this purpose.
* This loop is only about retrieving user inputs until game end. The model is automatically
* notified and re-rendered after each executed command.
*
* @param controller as the MVC controller allowing to progress the game command by command.
* Note that the view has no direct access to the model, and can only
* manipulate model state by executing commands.
*/
private static void playUntilGameEnd(Controller controller) {
// Initialize options for game start
Command[] options = controller.getCurrentPlayerCommands();
// Keep playing until controller offers no more options (game end)
while (options.length > 0) {
// Request a choice from human player - "undo"s have no relevance for INF2050, leave at
// "false".
int selectedCommand = commandSelector.selectCommand(options, false);
// Execute choice (this implicitly re-renders the model)
controller.doCommand(selectedCommand);
// Update options
options = controller.getCurrentPlayerCommands();
}
}
}
BUFFER
<dependency>
<groupId>ca.uqam.info.max</groupId>
<artifactId>skyjo-interfaces</artifactId>
<version>tp3-01</version>
</dependency>
Tests: Sources
<dependency>
<groupId>ca.uqam.info.max</groupId>
<artifactId>skyjo-tests</artifactId>
<version>tp3-01</version>
</dependency>
Illustration: Your controller must realize the above control flow graph. TODO: mention left comes before right, illustrate with sample prints.
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.
TODO
link to new checkstyle filespecify required changes.- link to execution traces