Tournament —
Initial Match-ups
PROJECT
By Alyce Brady
PROBLEM DESCRIPTION
In this project you will implement a program to read tournament
participants (for example, tennis players or basketball teams) in from a
file along with their rankings, print that information sorted by ranking,
and determine initial match-ups. In general, match-ups pair the
highest-ranked participant with the lowest-ranked participant, the second
highest-ranked participant with the second lowest-ranked participant, and
so on.
If the number of participants is not an even power of two, then there will
be an initial round involving only some participants in order to get the
number of match-ups to an even power of 2; the highest-ranked participants
will have a bye for the first round. The second round would then have a
full complement of participants. For example, if there are 68 teams in a
tournament, the first round (the bye round would consist of only
the 8 lowest-ranked teams, with the other 60 teams having a bye for the
first round. The four winners of the bye round would then join the other
60 teams for a full round of 64 teams participating in 32 matches. In a
less-realistic but shorter example of a tournament with 3 participating
teams, the top-ranked team would have a bye for the first round and would
then play the winner of the other two teams. The table below compares this
scenario against a single match-up between 2 teams with no bye.
| Example with Byes | Example with no Byes |
Initial Rankings:
1. S Carolina
2. UConn
3. UCLA
Number of Rounds: 2
Number of Byes: 1
First-Round Byes:
1. S Carolina
----------------
Round 1 Match-Ups:
2. UConn vs. 3. UCLA
Round 1 Scores:
2. UConn (78) vs. 3. UCLA (64)
----------------
Round 2 Match-Ups:
1. S Carolina vs. 2. UConn
Round 2 Scores:
2. UConn (82) vs. 1. S Carolina (59)
|
Initial Rankings:
1. S Carolina
2. UConn
Number of Rounds: 1
Number of Byes: 0
----------------
Round 1 Match-Ups:
1. S Carolina vs. 2. UConn
Round 1 Scores:
2. UConn (82) vs. 1. S Carolina (59)
|
Getting Started
Most of the functionality for the Tournament program is in the
Tournament class, although there are several other classes
that represent participant information as (name, value) pairs, match-up
assignments between participants, and classes that know how to read those
values from files.
Download the files for the Tournament — Initial Match-ups Project:
- Tournament.java
— Contains most of the functionality of the program,
including the
main method.
(Needs to be completed.)
- ParticipantInfo.java
— Represents (name, value) information about
participants, such as their name and ranking. (Fully implemented)
- ParticipantReader.java
— An object that knows how to read participant information
from a file, one participant per line. (Fully implemented)
- Matchup.java
— Represents a pairing of two participants and their scores.
(Fully implemented)
- Round.java
— Represents the match-ups in a single round of the
tournament, including scores after the match has been played.
(Fully implemented)
- ScoresReader.java
— An object that knows how to read participant scores
from a file, two participants per line.
(Needs to be completed.)
- ValidatedInputReader.java
— Class with static methods that can prompt users for numeric
or string input. (Fully implemented)
- There are two files containing sample rankings data:
18rankings.txt (18 participants, so
byes are needed)
and
16rankings.txt (16 participants, so no
byes necessary).
- There are two files containing sample score results:
byeRoundScores.txt and
fullRoundScores.txt.
- You will also need your
K_OrderedList class and any
other classes it requires.
First Look: Reading and Running Code for Understanding
Start by reading the code, but don't try to read every detail at once.
Start with the Tournament class. The Tournament
class contains the main method at the bottom of the class --
what does it do?
The runTournament method is the main driver of the program,
calling the following methods to perform subtasks:
getRankings
setRoundsAndByes
generateFirstRound (which in turn calls
determineMatchups)
simulateRoundOfPlay
getWinners
Where is the runTournament method? In
general, (leaving main aside), are the methods laid out in a
top-down or a bottom-up fashion?
Note that some of the methods in the Tournament class are
fully implemented, while others have code missing or commented out.
Does the Tournament class have many instance variables that
are shared among all of the methods? One of the first methods that the
runTournament method calls is getRankings. Are
the rankings only used within getRankings, or are they
communicated back to runTournament, and if so, how?
Before diving more deeply into all the details of the
Tournament class, glance at the 18rankings.txt
file and round1Scores.txt to see what the format is of the
data being used by the program. Run the program to see what it does with
the data. Look back at runTournament and
getRankings and notice which blocks of code are commented-out
or missing and which are not. Does the initial behavior of the code make
sense to you based on your initial look at the runTournament
and getRankings methods?
Next, look at the ParticipantInfo class, which is fully
implemented. This class keeps track of the ranking, name, and most recent
score for each participant. It has the usual getter methods, and one
setter to set the score. (A participant's ranking and name do not change
during the course of a tournament.) The class also redefines the
toString method to always return a participant's ranking and
name together, and provides a nameAndScore method that adds
the participant's most recent score to that. Finally, it redefines the
equals and compareTo methods.
ParticipantInfo instances are considered equal (they refer to
the same participant) if they have the same name. To order participants by
ranking, less-than and greater-than comparisons are based on that. (The
method also redefines the hashCode method because it should
always be redefined when redefining the equals method. Hash
codes will be a future topic in this course.)
Making Modifications
Following the precepts and practices of Agile Development, you should start
by making the smallest modifications you can, and test them before going on
to other modifications. As you work on the program, your output should
slowly look more and more like that in the
Sample Output below.
You may complete the implementation of the Tournament and
ScoresReader classes based only on the comments in the
existing code and the
Sample Output as a guide, or, if
you prefer more detailed guidance, you may follow the
guided directions provided here.
Guided Directions
(Display)
⇓
It makes sense to go through the methods called by
runTournament in the
order they are called, ignoring other code that isn't immediately relevant.
That means starting with getRankings, which is partly implemented.
Reading in Participant Rankings
getRankings:
getRankings first reads in the rankings from a file, then
reports them. You do not need to study the internals of the
ParticipantReader at this time, since it is fully implemented,
but you may want to read its class documentation just to be sure you
understand what it is supposed to do. In particular, what public method
would be useful to call from getRankings? What does it do,
and what does it return? In getRankings: What seems to be
missing here, given the method documentation and internal comments
describing what the method should do, and what you saw when you ran the
program? Make the modifications you determine are needed and then re-run
the program with both 16rankings.txt and
18rankings.txt to test your changes. Does your output contain
all of the teams ordered by ranking?
Tip: If your K_SimpleList
interface and its implementing classes support iterators, you can use the
for-each style loop to step through the list of participants
(and later to step through lists of match-ups).
setRoundsAndByes:
The setRoundsAndByes method is fully implemented, but you may
want to look it over to make sure you understand what it is supposed to be
doing. You may also wish to understand how it is doing it,
or you could use it withough studying it at that level of detail. Using
functioning code based on an understanding of what it
does, rather than how it does it, is common practice.
Generating Match-Ups for the First Round
Matchup:
At this point, it makes sense to read over the fully implemented
Matchup class, which pairs two participants, to make sure you
understand it and could use it.
Round:
The program will be creating a list of Matchup instances for
each round. It could store them in a K_SimpleAL list, for
example, but there will be a number of tasks to perform on the match-ups in
this list, including printing them, finding a particular match-up in the
list, updating scores, and getting all of the winners, so the program has
created a new class, Round that extends K_SimpleAL
and adds methods to do those tasks. Read over the Round class
to make sure you understand it and could use it.
generateFirstRound:
The next thing runTournament should do is call
generateFirstRound, although you will have to remove the
comments around the first block of commented-out code in
runTournament so that the call to this method is
no longer commented out. The first round being generated might be a full
round of all tournament participants, or it might be a partial round to
reduce the number of participants to a power of two. In the latter case,
the highest-ranked teams or players do not need to participate — they
will have "byes", meaning they can sit out this round.
This method is mostly implemented, but missing a small amount of code.
The implemented code starts by identifying highly ranked
participants who have byes (if any). It creates fake "match-ups"
between these highly ranked participants and a default participant
identified in the ParticipantInfo class as
BYE. It stores them in an instance of the
Round list class.
The method also includes some temporary code that prints
intermediate results for testing purposes. If you run the program
using 18rankings.txt you should see a list of
participants with byes.
If you run the program with
16rankings.txt you should see the same output you saw
before, since there are no byes in this case.
- The
generateFirstRound method then creates a list of
the participants who do need to compete in this
round and passes it to determineMatchups to create the
match-ups among those participants. The participants being matched
up might be a subset of the original set of participants, if there
were some byes, or might be the full set if the original number of
participants was a power of two. The
determineMatchups method, though, is missing most of
the code it needs.
determineMatchups:
This method should determine which participants to match and add them to
the matchups list. The method should match the first and last
participants in the list, then the second and second-to-last participants,
and so on. If, for example, a bye round consists of 8 participants, the
matchups would be:
The active participant at index 0 with the participant at index 7,
The active participant at index 1 with the participant at index 6,
The active participant at index 2 with the participant at index 5, and
The active participant at index 3 with the participant at index 4.
When you are done implementing determineMatchups,
go back to generateFirstRound.
The generateFirstRound method
is missing some code to add the match-ups returned by
determineMatchups to the fake match-ups created at the
beginning of the method.
Move the "paragraph" of temporary code that prints intermediate
results to come after the code you just added.
This time, if you run the program with
18rankings.txt, you should see 14 byes and 2 actual
match-ups of 4 teams, similar to the first set of match-ups in
Column 1 in the Sample Output
section. If you run the program with
16rankings.txt, you should see 0 byes and 8 actual
match-ups, similar to the first set of match-ups in Column 2 in the
Sample Output section.
Are the match-ups what you expected, given the list of participants
who should be participating in the bye round?
When you are satisfied that your code is behaving as expected,
remove the block of temporary code that prints the identified
match-ups.
Simulating a Bye Round
The next block of commented-out code in runTournament, which
is in an if statement so it is only run when there will be a
bye round, calls the simulateRoundOfPlay method. Uncomment
this block.
The simulateRoundOfPlay method is fully implemented, but it
uses the ScoresReader class, which is missing a significant
portion of its code.
ScoresReader: The structure of this class should be similar to
that of ParticipantReader, except that
ScoresReader creates and fills a list of Matchup
objects instead of a list of ParticipantInfo objects.
ScoresReader is also missing some key functionality.
readScores: The readScores method is very
similar to the readParticipantInfo method, with a few
small differences. The first difference is that, whereas
readParticipantInfo creates a list of
ParticipantInfo instances, readScores
adds new Matchup instances to an existing list. This
affects the number of parameters and the return types of the two
methods.
-
parseLine: This method reads the data on a single
line, but now each line contains (name,
score) information for two participants. The
parseLine method should read in the names and scores for
both participants, and then update their scores for the round.
The code to read in the data is missing; the code to update the
scores is there, but commented out.
The simulateRoundOfPlay method prints the results immediately
after asking the scores reader to read them in, so you should be able to
test your code without adding any extra debugging print statements.
Test your program with the 18rankings.txt input file,
comparing your results to the Bye Round matches and results in Column 1 of
the Sample Output section. (If you run
the program with 16rankings.txt you should just see the
initial list of ranked participants and the number of rounds and byes
unless you have not yet removed the temporary printed results in
generateFirstRound.)
getWinners:
There is one more commented-out piece of code in the bye round block in
runTournament, after the call to
simulateRoundOfPlay, which calls getWinners to
get the bye round winners and then
calls determineMatchups a second time to get the match-ups for
the first full round of play. (The first call to
determineMatchups was in generateFirstRound.)
Uncomment this code. If you try to compile your code, though, you will get
an error saying that there is no getWinners method. It exists
but is commented out because it has no functionality. Add code to create
an empty list, fill it with the winner from each match in the round, and
return it.
Handling the First Full Round
You have already written and tested code that simulates a bye round. The
same code can be used to simulate a full round of play. Add code to
runTournament that will do that and test it with both
18rankings.txt and 16rankings.txt.
Compare your output to the results in the
Sample Output section.
Sample Output
(Display)
⇓
Output using 18rankings.txt |
Output using 16rankings.txt |
Ranked Participants:
1. UConn
2. Maryland
3. Texas
4. UCLA
5. USC
6. TCU
7. Duke
8. LSU
9. NC State
10. UNC
11. Notre Dame
12. Kansas State
13. Tennessee
14. Ole Miss
15. S Carolina
16. Baylor
17. Oklahoma
18. Kentucky
Number of Rounds: 5
Number of Byes: 14
Bye Round (Round 0) Matches:
1. UConn (bye)
2. Maryland (bye)
3. Texas (bye)
4. UCLA (bye)
5. USC (bye)
6. TCU (bye)
7. Duke (bye)
8. LSU (bye)
9. NC State (bye)
10. UNC (bye)
11. Notre Dame (bye)
12. Kansas State (bye)
13. Tennessee (bye)
14. Ole Miss (bye)
15. S Carolina vs 18. Kentucky
16. Baylor vs 17. Oklahoma
Bye Round (Round 0) Results:
1. UConn (bye)
2. Maryland (bye)
3. Texas (bye)
4. UCLA (bye)
5. USC (bye)
6. TCU (bye)
7. Duke (bye)
8. LSU (bye)
9. NC State (bye)
10. UNC (bye)
11. Notre Dame (bye)
12. Kansas State (bye)
13. Tennessee (bye)
14. Ole Miss (bye)
15. S Carolina won in 15. S Carolina (75) vs 18. Kentucky (62)
17. Oklahoma won in 16. Baylor (58) vs 17. Oklahoma (68)
Round 1 Matches:
1. UConn vs 17. Oklahoma
2. Maryland vs 15. S Carolina
3. Texas vs 14. Ole Miss
4. UCLA vs 13. Tennessee
5. USC vs 12. Kansas State
6. TCU vs 11. Notre Dame
7. Duke vs 10. UNC
8. LSU vs 9. NC State
Round 1 Results:
1. UConn won in 1. UConn (82) vs 17. Oklahoma (59)
15. S Carolina won in 2. Maryland (67) vs 15. S Carolina (71)
3. Texas won in 3. Texas (76) vs 14. Ole Miss (62)
4. UCLA won in 4. UCLA (67) vs 13. Tennessee (59)
5. USC won in 5. USC (67) vs 12. Kansas State (61)
6. TCU won in 6. TCU (71) vs 11. Notre Dame (62)
7. Duke won in 7. Duke (47) vs 10. UNC (38)
8. LSU won in 8. LSU (80) vs 9. NC State (73)
|
Ranked Participants:
1. UConn
2. Maryland
3. Texas
4. UCLA
5. USC
6. TCU
7. Duke
8. LSU
9. NC State
10. UNC
11. Notre Dame
12. Kansas State
13. Tennessee
14. Ole Miss
15. S Carolina
16. Oklahoma
Number of Rounds: 4
Number of Byes: 0
Round 1 Matches:
1. UConn vs 16. Oklahoma
2. Maryland vs 15. S Carolina
3. Texas vs 14. Ole Miss
4. UCLA vs 13. Tennessee
5. USC vs 12. Kansas State
6. TCU vs 11. Notre Dame
7. Duke vs 10. UNC
8. LSU vs 9. NC State
Round 1 Results:
1. UConn won in 1. UConn (82) vs 16. Oklahoma (59)
15. S Carolina won in 2. Maryland (67) vs 15. S Carolina (71)
3. Texas won in 3. Texas (76) vs 14. Ole Miss (62)
4. UCLA won in 4. UCLA (67) vs 13. Tennessee (59)
5. USC won in 5. USC (67) vs 12. Kansas State (61)
6. TCU won in 6. TCU (71) vs 11. Notre Dame (62)
7. Duke won in 7. Duke (47) vs 10. UNC (38)
8. LSU won in 8. LSU (80) vs 9. NC State (73)
|
*Note: These results are somewhat doctored versions of the results of
the 2025 NCAA Women's Basketball championships. The changes (especially
the artifically low ranking for South Carolina) was to test correct behavior
when the lower-ranked team won.
Clean and Refactor
Follow this link to
clean and refactor your
comments and code.
Submission
Submit your completed program through Kit,
under Tournament — Initial Match-ups Project.
Have fun! And if you have any questions remember I am
available during office hours and the Collaboration Center
folks are here for you as well!