Project 6 - TournaKit

perspecTV is a (fictitious company) that designs, creates and markets products that provide a new perspective on television…

Introduction

perspecTV is a (fictitious company) that designs, creates and markets products that provide a new perspective on television. Their products make television both more interactive and more informative. They have found that viewers of televised competitions of various kinds (including traditional sporting events, eSports, dance competitions, singing competitions, cooking competitions, poker competitions, spelling bees, etc.) are very interested in historical statistics and they are developing various software products to help satisfy that demand.

“TournaKit” is a suite of products that can be used to analyze the results of completed tournaments.

Definitions

perspecTV uses the following definitions (listed in logical, not alphabetical, order) in relation to TournaKit.

Participant
A competitor in a competition of some kind.
Matchup
A competition involving exactly two participants.
Visitor/Home
Designations for the two participants in a matchup. The visiting participant is listed first and the home participant is listed second.
Score
The number of points/runs/goals/etc. awarded to a single participant.
Point Differential
The absolute value of the difference between the visiting participant’s points and the home participant’s points.
Win/Loss/Tie
The three possible outcomes of a matchup for a particular participant. Note that, in some matchups, the participant with the higher score wins (e.g., lacrosse) while in other matchups, the participant with the lower score wins (e.g., golf).
Round
A set of matchups in which none of the participants appears more than once.
Tournament
A set of rounds.
Round Robin Tournament
A tournament in which each competitor plays multiple other competitors, regardless of whether they win, lose, or tie. A round robin tournament is said to be regular if the number of competitors (n) is even, each competitor competes in each round, and each competitor competes against each other competitor exactly once (and, hence, there are n−1 rounds).
Single Elimination Tournament
A tournament in which ties are not possible and only the winning teams in a round advance to the next round (i.e., if a team loses it is eliminated). A single elimination tournament is said to be regular if the number of competitors (n) is a power of 2 (and, hence, the number of rounds is \(log_2(n)+1\) ). Many single elimination tournaments are regular but many, especially those that involve “play-in” competitions, are not. For example, the NCAA Division I Basketball Tournament used to be regular (involving 64 competitors and 7 rounds) but now includes a “play-in” round (involving 8 competitors reduced to 4 that are added to the 60 teams that skip the “play-in” round).

Assumptions

  1. You should assume that round robin tournaments are regular.
  2. You must not assume that single elimination tournaments are regular.
  3. You must not make any assumptions about other tournaments.

Data Files

perspecTV has provided three tournament files that you can use when running your code. They contain the results of the:

  1. 2017 NCAA Division III Women’s Lacrosse Championship (Single Elimination)
  2. 2016 Fantasy Division I Quidditch World Cup (Regular Round Robin)
  3. 2018 Disney Division Golf Championship (Regular Round Robin followed by Single Elimination)

Single elimination tournaments have a file type of .set, regular round robin tournaments have a file type of .rrt, and other tournaments have a file type of .tmt. None of these files are “human readable”.

To help you test your code, they have also provided the following files:

Design

The design of the initial components has already been completed, and is illustrated in the following UML class diagram. Existing classes (i.e., classes that are being provided to you) are shown in (perspecTV’s trademarked) green and new classes (i.e, classes that you must write) are shown in black.

Provided Code

Provided Code: Participant, Matchup, Tournament, RoundRobinTournament, and SingleElimination Tournament

Provided Code: Participant, Matchup, Tournament, RoundRobinTournament, and SingleElimination Tournament

New Code

You write Judge, HighWins, LowWins, Record, and Analyst

You write Judge, HighWins, LowWins, Record, and Analyst

Existing Components

  • Analyst.java
    • Stubs (including Javadoc comments) are provided for the Analyst class.
  • AnalystTest.java
    • This small set of tests are sufficient to cover 100 % of a reference solution.
  • tournakit.jar
    • The Participant enum, Matchup class, and Tournament, RoundRobinTournament, and SingleEliminationTournament classes have already been implemented and are provided in a .jar file:

The Matchup class

classDiagram
class Matchup
Matchup: +Matchup(visitor String, home String, visitorScore int, homeScore int)
Matchup: +getName(who Participant) String
Matchup: +getScore(who Participant) int
Matchup: +toString() String

In addition to the other specifications included in the UML diagram, this class has the following explicit values constructor that may help you write early unit tests (i.e. before you feel ready to read in a whole Tournament for testing).

  • Matchup(String team1Name, String team2Name, int team1Score, int team2Score)

The Tournament Class

The getFinalMatchups() method returns an array of all of the “final” Matchup objects, regardless of the round in which they occur. This means that the the array that is returned will include every competitor in at least one Matchup (and, in some cases, in more than one Matchup). For example, consider a Tournament that has six competitors; all six play a round robin portion and then the top four competitors in the round robin portion play a single elimination portion. The getFinalMatchups() method will return an array containing the one Matchup from the final round of the single elimination portion and the Matchup objects from the last round of the round robin portion for the two teams that did not participate in the single elimination portion.

The iterator() method returns an Iterator containing all of the Matchup objects involving the given team, ending with the given Matchup. The Iterator is in “round order” (i.e., the first call to next() will return the earliest Matchup).

The previous() method returns the Matchup from the previous round (relative to the round of the given Matchup) for the given team. If there is no such Matchup then this method returns null.

The RoundRobinTournament Class

The getFinalMatchups() method of a RoundRobinTournament object returns all of the Matchup objects from the final round.

The SingleEliminationTournament Class

The getFinalMatchups() method of a SingleEliminationTournament object return an array containing one Matchup, the Matchup for the final round of the tournament.

The getFinalMatchup() method (note the lack of an “s”) is a convenience method that returns the final round of the tournament. In other words, it returns getFinalMatchups()[0].

The Judge Interface

The Judge interface describes the capabilities of classes that can be used to determine the result of a Matchup.

Detailed Specifications

In addition to the other specifications included in the UML diagram, concrete implementations of this method must comply with the following specifications.

The result(Matchup, Participant) Method

The result(Matchup, Participant) method must return 1 if the given Participant won the given Matchup, must return -1 if the given Participant lost the given Matchup, and must return 0 if the given Matchup was a tie.

The HighWins and LowWins Classes

The HighWins class is a realization of the Judge interface that awards the win to the participant with the highest score. Both lacrosse and quidditch are examples of competitions that use this kind of Judge.

The LowWins class is a realization of the Judge interface that awards the win to the participant with the lowest score. Golf is an example of a competition that uses this kind of Judge.

The Record Class

The Record class is an encapsulation of a competitor’s performance in a tournament.

Detailed Specifications

In addition to the other specifications included in the UML diagram, this class must comply with the following specifications.

Default Constructor

The default constructor must initialize all attributes to 0.

Methods

The increaseLosses(), increaseWins(), and increaseTies() methods must increase the associated attribute by 1.

The toString() method must return a String representation of the record including the wins, losses, and ties (in that order), delimited by " - " (i.e., a dash with a space on each side). Each value must be right-justified in a field of width three.

The Analyst Class

The Analyst class is a utility class for performing calculations on tournaments.

Detailed Specifications

In addition to the other specifications included in the UML diagram, this class must comply with the following specifications. Note that when a specification says that a method must be recursive, it must be recursive in a meaningful/non-trivial way (i.e., it must use recursion to solve the problem).

The buildSchedule(RoundRobinTournament) Method

The buildSchedule(RoundRobinTournament) method is the publicly visible method that builds a schedule containing each competitor in a RoundRobinTournament (the key of the Map) and a List of all of its opponents (the value of the Map). The opponents must be included in the List in the order in which the competitions occurred (i.e., with the earliest round first).

It must call a private method that does most of the work. This private method need not be recursive, though it can be. In other words, because of the structure of RoundRobinTournament objects, the schedule can be built iteratively, without recursion. Keep in mind that a RoundRobinTournament has multiple matchups in each round, including the final round.

The buildSchedule(SingleEliminationTournament) Method

The buildSchedule(SingleEliminationTournament) method is the publicly visible method that builds a schedule containing each competitor in a SingleEliminationTournament (the key of the Map) and an ordered List of all of its opponents (the value of the Map). It must call a private method that does most of the work. This private method must be recursive.

The calculateRecords(Tournament, Judge) Method

The calculateRecords(Tournament, Judge) method is the publicly visible method for calculating the records of all competitors in a Tournament. It must call a private method that does most of the work. This private method must be recursive.

The totalPointDifferential(Tournament, Matchup, String) Method

The totalPointDifferential(Tournament, Matchup, String) method is the publicly visible method for calculating the total point differential in all games involving the given team, ending with the given Matchup. This method need not be recursive. In other words, it is possible (indeed relatively easy) to calculate the point differential for a particular team iteratively, without recursion.

The totalPointDifferential(SingleEliminationTournament) Method

The totalPointDifferential(SingleEliminationTournament) method is the publicly visible method for calculating the total point differential of all matchups in a SingleEliminationTournament. It must call a private method that does most of the work. This private method must be recursive.

Submission and Grading

You must submit all of the classes/interfaces you implement. You must not submit JUnit test suites.

For Part A, submit only Judge.java, HighWins.java, LowWins.java, and Record.java. We recommend you complete this part at least one week before the deadline.

For Part B, submit only Analyst.java. Gradescope will compile your Analyst with the reference solutions for Judge, HighWins, LowWins, and Record.

At this point in the semester, you should be able to both implement and test your code. Hence, it is your responsibility to submit correct code. Hence, though you do not need to submit any tests, you will almost certainly need to write tests.

One way to think about this is that perspecTV does not have testers (either in-house alpha testers or external beta testers). The code you submit will be given to clients to use in a production environment. If the code contains defects, the clients will not provide details, they will just stop using the product (which will cost perspecTV revenue). If you find yourself in a situation in which your code passes all of your tests but the client (i.e., Gradescope) says it didn’t work properly, you will need to re-think your tests.

Your grade will be based on the “official” tests your code passes/fails (though, as in the past, your code must not contain any style defects). Specifically:

Item Points
Judge, HighWins, LowWins, Record 15
totalPointDifferential(Tournament, Matchup, String) 10
buildSchedule(RoundRobinTournament) 20
totalPointDifferential(SingleEliminationTournament) 20
buildSchedule(SingleEliminationTournament) 20
calculateRecords(Tournament, Judge) 15
Instructor Code Review 0*

You may submit up to 10 times without penalty. Additional submissions (if any) will receive a penalty of 1 point each. Be sure to use all the development tools offline: Checkstyle, JUnit, coverage analysis, and the debugger.

The code reviews are worth 0 additional points. However, points may be deducted if your code is difficult to understand. For example: confusing variable names, too few or too many comments, etc.

Hints and Suggestions

You should certainly start by implementing and testing the Judge interface, HighWins and LowWins classes, and Record. After that, you should probably work on the methods in the Analyst class in increasing level of difficulty.

The totalPointDifferential(Tournament, Matchup, String) method is the easiest in the Analyst class. It needn’t involve recursion.

The buildSchedule(RoundRobinTournament) method is the next-easiest (because you need only consider regular round robin tournaments). It also needn’t involve recursion, though you need to think carefully about how you can use iteration because the last round in such a tournament includes multiple Matchup objects.

The totalPointDifferential(SingleEliminationTournament) method does involve recursion (i.e., the private method it calls must use recursion), but the recursion is fairly straightforward (i.e., similar to many examples that you’ve seen).

The buildSchedule(SingleEliminationTournament) method must work backwards from the last Matchup to construct a complete schedule. The base case should be fairly obvious, and you should also be able to figure out how to refine the other cases fairly quickly. What may take some time is figuring out how to keep track of the results.

The calculateRecords(Tournament, Judge) method is pretty challenging because it must work on any Tournament. Hence, though the base case is pretty obvious, you will need to spend time thinking about how to refine the other cases. Also, you will need to think carefully about how to avoid “double counting”.

Using Your Code

You should test your code using JUnit. However, you may also want to write a small application or two just because it is sometimes more satisfying than just writing components. For example, the following application can be used to print the records of each team in the lacrosse championship.

import java.util.*;

public class Results
{
  public static void main(String[] args) throws Exception
  {
    HashMap<String, Record>     records;
    SingleEliminationTournament lacrosse;

    lacrosse = SingleEliminationTournament.read("lacrosse-women_d3_2017.set");

    records = Analyst.calculateRecords(lacrosse, new HighWins());

    System.out.println(lacrosse.getDescription());
    for (String team: records.keySet())
    {
      Record record = records.get(team);
      System.out.printf("%25s:\t%s\n", team, record);
    }
  }
}

If you’re actually interested in these kinds of analyses, you might also want to write an application that compares the point differential for all of the matchups in the lacrosse tournament with the point differential for the winner in order to evaluate their “dominance” in the tournament.

Last modified April 30, 2022: practice coding exam (a2ce8c8)