Avatar of clechasseur

clechasseur's solution

to Tournament in the Java Track

Published at Jul 15 2019 · 0 comments
Instructions
Test suite
Solution

Tally the results of a small football competition.

Based on an input file containing which team played against which and what the outcome was, create a file with a table like this:

Team                           | MP |  W |  D |  L |  P
Devastating Donkeys            |  3 |  2 |  1 |  0 |  7
Allegoric Alaskans             |  3 |  2 |  0 |  1 |  6
Blithering Badgers             |  3 |  1 |  0 |  2 |  3
Courageous Californians        |  3 |  0 |  1 |  2 |  1

What do those abbreviations mean?

  • MP: Matches Played
  • W: Matches Won
  • D: Matches Drawn (Tied)
  • L: Matches Lost
  • P: Points

A win earns a team 3 points. A draw earns 1. A loss earns 0.

The outcome should be ordered by points, descending. In case of a tie, teams are ordered alphabetically.

Input

Your tallying program will receive input that looks like:

Allegoric Alaskans;Blithering Badgers;win
Devastating Donkeys;Courageous Californians;draw
Devastating Donkeys;Allegoric Alaskans;win
Courageous Californians;Blithering Badgers;loss
Blithering Badgers;Devastating Donkeys;loss
Allegoric Alaskans;Courageous Californians;win

The result of the match refers to the first team listed. So this line

Allegoric Alaskans;Blithering Badgers;win

Means that the Allegoric Alaskans beat the Blithering Badgers.

This line:

Courageous Californians;Blithering Badgers;loss

Means that the Blithering Badgers beat the Courageous Californians.

And this line:

Devastating Donkeys;Courageous Californians;draw

Means that the Devastating Donkeys and Courageous Californians tied.

Setup

Go through the setup instructions for Java to install the necessary dependencies:

https://exercism.io/tracks/java/installation

Running the tests

You can run all the tests for an exercise by entering the following in your terminal:

$ gradle test

Use gradlew.bat if you're on Windows

In the test suites all tests but the first have been skipped.

Once you get a test passing, you can enable the next one by removing the @Ignore("Remove to run test") annotation.

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

TournamentTest.java

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class TournamentTest {

    private Tournament tournament;

    @Before
    public void setUp() {
        tournament = new Tournament();
    }

    @Test
    public void justTheHeaderIfNoInput() {
        assertEquals("Team                           | MP |  W |  D |  L |  P\n", tournament.printTable());
    }

    @Ignore("Remove to run test")
    @Test
    public void aWinIsThreePointsALossIsZeroPoints() {
        tournament.applyResults("Allegoric Alaskans;Blithering Badgers;win");
        assertEquals(
                "Team                           | MP |  W |  D |  L |  P\n" +
                "Allegoric Alaskans             |  1 |  1 |  0 |  0 |  3\n" +
                "Blithering Badgers             |  1 |  0 |  0 |  1 |  0\n",
                tournament.printTable());
    }

    @Ignore("Remove to run test")
    @Test
    public void aWinCanAlsoBeExpressedAsALoss() {
        tournament.applyResults("Blithering Badgers;Allegoric Alaskans;loss");
        assertEquals(
                "Team                           | MP |  W |  D |  L |  P\n" +
                "Allegoric Alaskans             |  1 |  1 |  0 |  0 |  3\n" +
                "Blithering Badgers             |  1 |  0 |  0 |  1 |  0\n",
                tournament.printTable());
    }

    @Ignore("Remove to run test")
    @Test
    public void aDifferentTeamCanWin() {
        tournament.applyResults("Blithering Badgers;Allegoric Alaskans;win");
        assertEquals(
                "Team                           | MP |  W |  D |  L |  P\n" +
                "Blithering Badgers             |  1 |  1 |  0 |  0 |  3\n" +
                "Allegoric Alaskans             |  1 |  0 |  0 |  1 |  0\n",
                tournament.printTable());
    }

    @Ignore("Remove to run test")
    @Test
    public void aDrawIsOnePointEach() {
        tournament.applyResults("Allegoric Alaskans;Blithering Badgers;draw");
        assertEquals(
                "Team                           | MP |  W |  D |  L |  P\n" +
                "Allegoric Alaskans             |  1 |  0 |  1 |  0 |  1\n" +
                "Blithering Badgers             |  1 |  0 |  1 |  0 |  1\n",
                tournament.printTable());
    }

    @Ignore("Remove to run test")
    @Test
    public void thereCanBeMoreThanOneMatch() {
        tournament.applyResults(
                "Allegoric Alaskans;Blithering Badgers;win\n" +
                "Allegoric Alaskans;Blithering Badgers;win");
        assertEquals(
                "Team                           | MP |  W |  D |  L |  P\n" +
                "Allegoric Alaskans             |  2 |  2 |  0 |  0 |  6\n" +
                "Blithering Badgers             |  2 |  0 |  0 |  2 |  0\n",
                tournament.printTable());
    }

    @Ignore("Remove to run test")
    @Test
    public void thereCanBeMoreThanOneWinner() {
        tournament.applyResults(
                "Allegoric Alaskans;Blithering Badgers;loss\n" +
                "Allegoric Alaskans;Blithering Badgers;win");
        assertEquals(
                "Team                           | MP |  W |  D |  L |  P\n" +
                "Allegoric Alaskans             |  2 |  1 |  0 |  1 |  3\n" +
                "Blithering Badgers             |  2 |  1 |  0 |  1 |  3\n",
                tournament.printTable());
    }

    @Ignore("Remove to run test")
    @Test
    public void thereCanBeMoreThanTwoTeams() {
        tournament.applyResults(
                "Allegoric Alaskans;Blithering Badgers;win\n" +
                "Blithering Badgers;Courageous Californians;win\n" +
                "Courageous Californians;Allegoric Alaskans;loss");
        assertEquals(
                "Team                           | MP |  W |  D |  L |  P\n" +
                "Allegoric Alaskans             |  2 |  2 |  0 |  0 |  6\n" +
                "Blithering Badgers             |  2 |  1 |  0 |  1 |  3\n" +
                "Courageous Californians        |  2 |  0 |  0 |  2 |  0\n",
                tournament.printTable());
    }

    @Ignore("Remove to run test")
    @Test
    public void typicalInput() {
        tournament.applyResults(
                "Allegoric Alaskans;Blithering Badgers;win\n" +
                "Devastating Donkeys;Courageous Californians;draw\n" +
                "Devastating Donkeys;Allegoric Alaskans;win\n" +
                "Courageous Californians;Blithering Badgers;loss\n" +
                "Blithering Badgers;Devastating Donkeys;loss\n" +
                "Allegoric Alaskans;Courageous Californians;win");
        assertEquals(
                "Team                           | MP |  W |  D |  L |  P\n" +
                "Devastating Donkeys            |  3 |  2 |  1 |  0 |  7\n" +
                "Allegoric Alaskans             |  3 |  2 |  0 |  1 |  6\n" +
                "Blithering Badgers             |  3 |  1 |  0 |  2 |  3\n" +
                "Courageous Californians        |  3 |  0 |  1 |  2 |  1\n",
                tournament.printTable());
    }

    @Ignore("Remove to run test")
    @Test
    public void incompleteCompetition() {
        tournament.applyResults(
                "Allegoric Alaskans;Blithering Badgers;loss\n" +
                "Devastating Donkeys;Allegoric Alaskans;loss\n" +
                "Courageous Californians;Blithering Badgers;draw\n" +
                "Allegoric Alaskans;Courageous Californians;win");
        assertEquals(
                "Team                           | MP |  W |  D |  L |  P\n" +
                "Allegoric Alaskans             |  3 |  2 |  0 |  1 |  6\n" +
                "Blithering Badgers             |  2 |  1 |  1 |  0 |  4\n" +
                "Courageous Californians        |  2 |  0 |  1 |  1 |  1\n" +
                "Devastating Donkeys            |  1 |  0 |  0 |  1 |  0\n",
                tournament.printTable());
    }

    @Ignore("Remove to run test")
    @Test
    public void tiesBrokenAlphabetically() {
        tournament.applyResults(
                "Courageous Californians;Devastating Donkeys;win\n" +
                "Allegoric Alaskans;Blithering Badgers;win\n" +
                "Devastating Donkeys;Allegoric Alaskans;loss\n" +
                "Courageous Californians;Blithering Badgers;win\n" +
                "Blithering Badgers;Devastating Donkeys;draw\n" +
                "Allegoric Alaskans;Courageous Californians;draw");
        assertEquals(
                "Team                           | MP |  W |  D |  L |  P\n" +
                "Allegoric Alaskans             |  3 |  2 |  1 |  0 |  7\n" +
                "Courageous Californians        |  3 |  2 |  1 |  0 |  7\n" +
                "Blithering Badgers             |  3 |  0 |  1 |  2 |  1\n" +
                "Devastating Donkeys            |  3 |  0 |  1 |  2 |  1\n",
                tournament.printTable());
    }
}
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class Tournament {
    private static final Pattern RESULT_REGEX = Pattern.compile("^([^;]+);([^;]+);(win|loss|draw)$");

    private Map<String, TeamResults> results = new HashMap<>();

    void applyResults(String results) {
        String[] lines = results.split("\n");
        for (String line : lines) {
            Matcher matcher = RESULT_REGEX.matcher(line);
            if (matcher.find()) {
                String result = matcher.group(3);
                if (result.equals("win")) {
                    recordWin(matcher.group(1), matcher.group(2));
                } else if (result.equals("loss")) {
                    recordWin(matcher.group(2), matcher.group(1));
                } else {
                    recordDraw(matcher.group(1), matcher.group(2));
                }
            }
        }
    }

    String printTable() {
        List<String> lines = new ArrayList<>(
                Collections.singletonList("Team                           | MP |  W |  D |  L |  P"));
        this.results.entrySet().stream()
                               .map(entry -> new TeamWithResults(entry.getKey(), entry.getValue()))
                               .sorted((team1, team2) -> {
                                   int cmp = team2.results.points() - team1.results.points();
                                   if (cmp == 0) {
                                       cmp = team1.teamName.compareTo(team2.teamName);
                                   }
                                   return cmp;
                               }).forEach(team -> lines.add(String.format("%-31s|  %d |  %d |  %d |  %d |  %d",
                                                                            team.teamName, team.results.matchesPlayed(), team.results.wins(),
                                                                            team.results.draws(), team.results.losses(), team.results.points())));
        return String.join("\n", lines) + "\n";
    }

    private void recordWin(String winners, String losers) {
        this.results.computeIfAbsent(winners, n -> new TeamResults()).recordWin();
        this.results.computeIfAbsent(losers, n -> new TeamResults()).recordLoss();
    }

    private void recordDraw(String team1, String team2) {
        this.results.computeIfAbsent(team1, n -> new TeamResults()).recordDraw();
        this.results.computeIfAbsent(team2, n -> new TeamResults()).recordDraw();
    }

    private static final class TeamResults {
        private int wins;
        private int losses;
        private int draws;

        int matchesPlayed() {
            return this.wins + this.losses + this.draws;
        }

        int wins() {
            return this.wins;
        }

        int losses() {
            return this.losses;
        }

        int draws() {
            return this.draws;
        }

        int points() {
            return this.wins * 3 + this.draws;
        }

        void recordWin() {
            ++this.wins;
        }

        void recordLoss() {
            ++this.losses;
        }

        void recordDraw() {
            ++this.draws;
        }
    }

    private static final class TeamWithResults {
        final String teamName;
        final TeamResults results;

        TeamWithResults(String teamName, TeamResults results) {
            this.teamName = teamName;
            this.results = results;
        }
    }
}

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?