Skip to content

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...

  1. complete your Skyjo Controller implementation, and integrate advanced concepts to ensure code quality into the build process. All code quality requirements must by enforced via pom.xml.

  2. Your code must be tested with classic and mutation tests. You must ensure a requested coverage and mutation coverate is met.

  3. Your code must comply to industry grade code formatting rules, and respect a requested complexity threshold.
  4. Your code must be fully documented with JavaDoc comments for all public methods and classes.

  5. use provided integration tests to guide the completion of your controller implementation, i.e. implementation of game rules.

  6. Your game implementation must support all game actions correctly, in any game situation.

  7. 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

  1. 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.
  2. Code quality:
    • Enable cyclomatic complexity via pom.xml.
    • Enable strict javadoc verification via pom.xml.
    • Raise code line coverage to 85% via pom.xml.
    • Enable mutation testing, enable a minimum mutation test coverage via pom.xml.
  3. Controller code: Complete your controller implementation, so the game is fully playable.
  4. Integration tests:
    • Separate integration tests from regular tests using the *IT suffix via pom.xml.
    • Extend the provided abstract integration test class to enable integration tests, and make sure all integration tests pass.

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 @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.

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 jacoco plugin into your pom.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.

Mutation coverage

Controller code

Description of blackboard / selectors here. Description of expected command order here. Reference to sample games (see integr. tests) here.

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 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:
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 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 of jacoco plugin / assembly plugin / copy-rename plugin. (Compared to TP3 specific, provided pom.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:

  1. import of your ControllerImpl class.
  2. Using your ControllerImpl constructor 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

Interfaces: Sources Docs

<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 file specify required changes.
  • link to execution traces