Avatar of -1

-1's solution

to Tournament in the C# Track

Published at Sep 13 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.

Running the tests

To run the tests, run the command dotnet test from within the exercise directory.

Initially, only the first test will be enabled. This is to encourage you to solve the exercise one step at a time. Once you get the first test passing, remove the Skip property from the next test and work on getting that test passing. Once none of the tests are skipped and they are all passing, you can submit your solution using exercism submit Tournament.cs

Further information

For more detailed information about the C# track, including how to get help if you're having trouble, please visit the exercism.io C# language page.

TournamentTest.cs

// This file was auto-generated based on version 1.4.0 of the canonical data.

using System;
using System.IO;
using System.Text;
using Xunit;

public class TournamentTest
{
    [Fact]
    public void Just_the_header_if_no_input()
    {
        var rows = "";
        var expected = "Team                           | MP |  W |  D |  L |  P";
        Assert.Equal(expected, RunTally(rows));
    }

    [Fact(Skip = "Remove to run test")]
    public void A_win_is_three_points_a_loss_is_zero_points()
    {
        var rows = "Allegoric Alaskans;Blithering Badgers;win";
        var expected = 
            "Team                           | MP |  W |  D |  L |  P\n" +
            "Allegoric Alaskans             |  1 |  1 |  0 |  0 |  3\n" +
            "Blithering Badgers             |  1 |  0 |  0 |  1 |  0";
        Assert.Equal(expected, RunTally(rows));
    }

    [Fact(Skip = "Remove to run test")]
    public void A_win_can_also_be_expressed_as_a_loss()
    {
        var rows = "Blithering Badgers;Allegoric Alaskans;loss";
        var expected = 
            "Team                           | MP |  W |  D |  L |  P\n" +
            "Allegoric Alaskans             |  1 |  1 |  0 |  0 |  3\n" +
            "Blithering Badgers             |  1 |  0 |  0 |  1 |  0";
        Assert.Equal(expected, RunTally(rows));
    }

    [Fact(Skip = "Remove to run test")]
    public void A_different_team_can_win()
    {
        var rows = "Blithering Badgers;Allegoric Alaskans;win";
        var expected = 
            "Team                           | MP |  W |  D |  L |  P\n" +
            "Blithering Badgers             |  1 |  1 |  0 |  0 |  3\n" +
            "Allegoric Alaskans             |  1 |  0 |  0 |  1 |  0";
        Assert.Equal(expected, RunTally(rows));
    }

    [Fact(Skip = "Remove to run test")]
    public void A_draw_is_one_point_each()
    {
        var rows = "Allegoric Alaskans;Blithering Badgers;draw";
        var expected = 
            "Team                           | MP |  W |  D |  L |  P\n" +
            "Allegoric Alaskans             |  1 |  0 |  1 |  0 |  1\n" +
            "Blithering Badgers             |  1 |  0 |  1 |  0 |  1";
        Assert.Equal(expected, RunTally(rows));
    }

    [Fact(Skip = "Remove to run test")]
    public void There_can_be_more_than_one_match()
    {
        var rows = 
            "Allegoric Alaskans;Blithering Badgers;win\n" +
            "Allegoric Alaskans;Blithering Badgers;win";
        var expected = 
            "Team                           | MP |  W |  D |  L |  P\n" +
            "Allegoric Alaskans             |  2 |  2 |  0 |  0 |  6\n" +
            "Blithering Badgers             |  2 |  0 |  0 |  2 |  0";
        Assert.Equal(expected, RunTally(rows));
    }

    [Fact(Skip = "Remove to run test")]
    public void There_can_be_more_than_one_winner()
    {
        var rows = 
            "Allegoric Alaskans;Blithering Badgers;loss\n" +
            "Allegoric Alaskans;Blithering Badgers;win";
        var expected = 
            "Team                           | MP |  W |  D |  L |  P\n" +
            "Allegoric Alaskans             |  2 |  1 |  0 |  1 |  3\n" +
            "Blithering Badgers             |  2 |  1 |  0 |  1 |  3";
        Assert.Equal(expected, RunTally(rows));
    }

    [Fact(Skip = "Remove to run test")]
    public void There_can_be_more_than_two_teams()
    {
        var rows = 
            "Allegoric Alaskans;Blithering Badgers;win\n" +
            "Blithering Badgers;Courageous Californians;win\n" +
            "Courageous Californians;Allegoric Alaskans;loss";
        var expected = 
            "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";
        Assert.Equal(expected, RunTally(rows));
    }

    [Fact(Skip = "Remove to run test")]
    public void Typical_input()
    {
        var rows = 
            "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";
        var expected = 
            "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";
        Assert.Equal(expected, RunTally(rows));
    }

    [Fact(Skip = "Remove to run test")]
    public void Incomplete_competition_not_all_pairs_have_played_()
    {
        var rows = 
            "Allegoric Alaskans;Blithering Badgers;loss\n" +
            "Devastating Donkeys;Allegoric Alaskans;loss\n" +
            "Courageous Californians;Blithering Badgers;draw\n" +
            "Allegoric Alaskans;Courageous Californians;win";
        var expected = 
            "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";
        Assert.Equal(expected, RunTally(rows));
    }

    [Fact(Skip = "Remove to run test")]
    public void Ties_broken_alphabetically()
    {
        var rows = 
            "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";
        var expected = 
            "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";
        Assert.Equal(expected, RunTally(rows));
    }

    private string RunTally(string input)
    {
        var encoding = new UTF8Encoding();
        using (var inStream = new MemoryStream(encoding.GetBytes(input)))
        using (var outStream = new MemoryStream())
        {
            Tournament.Tally(inStream, outStream);
            return encoding.GetString(outStream.ToArray());
        }
    }
}
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

public static class Tournament
{
    public static void Tally(Stream inStream, Stream outStream)
    {
        var inLines = ReadInput(inStream);
        var teamScores = ProcessLines(inLines);
        var scoresText = BuildScoresText(teamScores);
        WriteOutput(outStream, scoresText);
    }

    private static void WriteOutput(Stream outStream, string scoresText)
    {
        var encoding = new UTF8Encoding();
        outStream.Write(encoding.GetBytes(scoresText));
    }

    private static Dictionary<string, Scores> ProcessLines(string[] inLines)
    {
        var teamScores = new Dictionary<string, Scores>();
        foreach (var line in inLines)
        {
            var lineParts = line.Split(';');
            if (lineParts[0].Equals(string.Empty))
            {
                break;
            }
            UpdateTeamScores(lineParts[0], lineParts[2], teamScores);
            UpdateTeamScores(lineParts[1], InvertedOutcomes[lineParts[2]], teamScores);
        }

        var orderedTeamScores = teamScores
            .OrderByDescending(x => x.Value.Points())
            .ThenBy(x => x.Key)
            .ToDictionary(x => x.Key, x => x.Value);

        return orderedTeamScores;
    }

    private static string[] ReadInput(Stream inStream)
    {
        return new StreamReader(inStream).ReadToEnd().Split('\n');
    }

    static string BuildScoresText(Dictionary<string, Scores> teamScores)
    {
        const int NAME_ALIGNMENT = -31;
        const int SCORE_ALIGNMENT = 2;
        var scoresBuilder = new StringBuilder($"{"Team",NAME_ALIGNMENT}| MP |  W |  D |  L |  P");
        foreach (var teamScore in teamScores)
        {
            var score = teamScore.Value;
            scoresBuilder.Append($"\n{teamScore.Key,NAME_ALIGNMENT}| {score.MatchesPlayed(),SCORE_ALIGNMENT} | {score.win,SCORE_ALIGNMENT} | {score.draw,SCORE_ALIGNMENT} | {score.loss,SCORE_ALIGNMENT} | {score.Points(),SCORE_ALIGNMENT}");
        }
        return scoresBuilder.ToString();
    }

    static void UpdateTeamScores(string teamName, string outcome, Dictionary<string, Scores> teamScores)
    {
        teamScores.TryAdd(teamName, new Scores());
        var teamScore = teamScores[teamName];
        teamScore.Update(outcome);
    }

    static readonly Dictionary<string, string> InvertedOutcomes = new Dictionary<string, string>
    {
        [WIN] = LOSS,
        [LOSS] = WIN,
        [DRAW] = DRAW,
    };

    //nameof can be used as long as corresponding text in file is proper name of variable
    const string WIN = nameof(Scores.win);
    const string LOSS = nameof(Scores.loss);
    const string DRAW = nameof(Scores.draw);

    class Scores
    {
        public int win { get; set; }
        public int loss { get; set; }
        public int draw { get; set; }

        public int MatchesPlayed() => win + loss + draw;
        public int Points() => 3 * win + draw;

        public void Update(string outcome)
        {
            switch (outcome)
            {
                case WIN:
                    win++;
                    break;
                case LOSS:
                    loss++;
                    break;
                case DRAW:
                    draw++;
                    break;
            }
        }
    }
}

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?