Avatar of paulfioravanti

paulfioravanti's solution

to Tournament in the Ruby Track

Published at May 06 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.


For installation and learning resources, refer to the Ruby resources page.

For running the tests provided, you will need the Minitest gem. Open a terminal window and run the following command to install minitest:

gem install minitest

If you would like color output, you can require 'minitest/pride' in the test file, or note the alternative instruction, below, for running the test file.

Run the tests from the exercise directory using the following command:

ruby tournament_test.rb

To include color from the command line:

ruby -r minitest/pride tournament_test.rb

Submitting Incomplete Solutions

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

tournament_test.rb

require 'minitest/autorun'
require_relative 'tournament'

# Common test data version: 1.4.0 ee01fe0
class TournamentTest < Minitest::Test
  def test_just_the_header_if_no_input
    # skip
    input = <<~INPUT

    INPUT

    expected = <<~TALLY
    Team                           | MP |  W |  D |  L |  P
    TALLY

    assert_equal expected, Tournament.tally(input)
  end

  def test_a_win_is_three_points_a_loss_is_zero_points
    skip
    input = <<~INPUT
    Allegoric Alaskans;Blithering Badgers;win
    INPUT

    expected = <<~TALLY
    Team                           | MP |  W |  D |  L |  P
    Allegoric Alaskans             |  1 |  1 |  0 |  0 |  3
    Blithering Badgers             |  1 |  0 |  0 |  1 |  0
    TALLY

    assert_equal expected, Tournament.tally(input)
  end

  def test_a_win_can_also_be_expressed_as_a_loss
    skip
    input = <<~INPUT
    Blithering Badgers;Allegoric Alaskans;loss
    INPUT

    expected = <<~TALLY
    Team                           | MP |  W |  D |  L |  P
    Allegoric Alaskans             |  1 |  1 |  0 |  0 |  3
    Blithering Badgers             |  1 |  0 |  0 |  1 |  0
    TALLY

    assert_equal expected, Tournament.tally(input)
  end

  def test_a_different_team_can_win
    skip
    input = <<~INPUT
    Blithering Badgers;Allegoric Alaskans;win
    INPUT

    expected = <<~TALLY
    Team                           | MP |  W |  D |  L |  P
    Blithering Badgers             |  1 |  1 |  0 |  0 |  3
    Allegoric Alaskans             |  1 |  0 |  0 |  1 |  0
    TALLY

    assert_equal expected, Tournament.tally(input)
  end

  def test_a_draw_is_one_point_each
    skip
    input = <<~INPUT
    Allegoric Alaskans;Blithering Badgers;draw
    INPUT

    expected = <<~TALLY
    Team                           | MP |  W |  D |  L |  P
    Allegoric Alaskans             |  1 |  0 |  1 |  0 |  1
    Blithering Badgers             |  1 |  0 |  1 |  0 |  1
    TALLY

    assert_equal expected, Tournament.tally(input)
  end

  def test_there_can_be_more_than_one_match
    skip
    input = <<~INPUT
    Allegoric Alaskans;Blithering Badgers;win
    Allegoric Alaskans;Blithering Badgers;win
    INPUT

    expected = <<~TALLY
    Team                           | MP |  W |  D |  L |  P
    Allegoric Alaskans             |  2 |  2 |  0 |  0 |  6
    Blithering Badgers             |  2 |  0 |  0 |  2 |  0
    TALLY

    assert_equal expected, Tournament.tally(input)
  end

  def test_there_can_be_more_than_one_winner
    skip
    input = <<~INPUT
    Allegoric Alaskans;Blithering Badgers;loss
    Allegoric Alaskans;Blithering Badgers;win
    INPUT

    expected = <<~TALLY
    Team                           | MP |  W |  D |  L |  P
    Allegoric Alaskans             |  2 |  1 |  0 |  1 |  3
    Blithering Badgers             |  2 |  1 |  0 |  1 |  3
    TALLY

    assert_equal expected, Tournament.tally(input)
  end

  def test_there_can_be_more_than_two_teams
    skip
    input = <<~INPUT
    Allegoric Alaskans;Blithering Badgers;win
    Blithering Badgers;Courageous Californians;win
    Courageous Californians;Allegoric Alaskans;loss
    INPUT

    expected = <<~TALLY
    Team                           | MP |  W |  D |  L |  P
    Allegoric Alaskans             |  2 |  2 |  0 |  0 |  6
    Blithering Badgers             |  2 |  1 |  0 |  1 |  3
    Courageous Californians        |  2 |  0 |  0 |  2 |  0
    TALLY

    assert_equal expected, Tournament.tally(input)
  end

  def test_typical_input
    skip
    input = <<~INPUT
    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
    INPUT

    expected = <<~TALLY
    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
    TALLY

    assert_equal expected, Tournament.tally(input)
  end

  def test_incomplete_competition_not_all_pairs_have_played
    skip
    input = <<~INPUT
    Allegoric Alaskans;Blithering Badgers;loss
    Devastating Donkeys;Allegoric Alaskans;loss
    Courageous Californians;Blithering Badgers;draw
    Allegoric Alaskans;Courageous Californians;win
    INPUT

    expected = <<~TALLY
    Team                           | MP |  W |  D |  L |  P
    Allegoric Alaskans             |  3 |  2 |  0 |  1 |  6
    Blithering Badgers             |  2 |  1 |  1 |  0 |  4
    Courageous Californians        |  2 |  0 |  1 |  1 |  1
    Devastating Donkeys            |  1 |  0 |  0 |  1 |  0
    TALLY

    assert_equal expected, Tournament.tally(input)
  end

  def test_ties_broken_alphabetically
    skip
    input = <<~INPUT
    Courageous Californians;Devastating Donkeys;win
    Allegoric Alaskans;Blithering Badgers;win
    Devastating Donkeys;Allegoric Alaskans;loss
    Courageous Californians;Blithering Badgers;win
    Blithering Badgers;Devastating Donkeys;draw
    Allegoric Alaskans;Courageous Californians;draw
    INPUT

    expected = <<~TALLY
    Team                           | MP |  W |  D |  L |  P
    Allegoric Alaskans             |  3 |  2 |  1 |  0 |  7
    Courageous Californians        |  3 |  2 |  1 |  0 |  7
    Blithering Badgers             |  3 |  0 |  1 |  2 |  1
    Devastating Donkeys            |  3 |  0 |  1 |  2 |  1
    TALLY

    assert_equal expected, Tournament.tally(input)
  end
end
# frozen_string_literal: true

module Tournament
  INCREMENT = 1
  private_constant :INCREMENT
  INPUT_SEPARATOR = ";"
  private_constant :INPUT_SEPARATOR
  LOSS = "loss"
  private_constant :LOSS
  NEW_TALLY = Hash.new { |hash, key| hash[key] = Tally.new }.freeze
  private_constant :NEW_TALLY
  NEWLINE = "\n"
  private_constant :NEWLINE
  WIN = "win"
  private_constant :WIN

  class Tally
    DRAW_POINTS = 1
    private_constant :DRAW_POINTS
    INITIAL_POINTS = 0
    private_constant :INITIAL_POINTS
    WIN_POINTS = 3
    private_constant :WIN_POINTS

    attr_accessor :draws, :losses, :wins

    def initialize
      @draws = INITIAL_POINTS
      @losses = INITIAL_POINTS
      @wins = INITIAL_POINTS
    end

    def matches_played
      wins + draws + losses
    end

    def points
      wins * WIN_POINTS + draws * DRAW_POINTS
    end
  end
  private_constant :Tally

  module Table
    REPORT_RESULTS = %i[matches_played wins draws losses points].freeze
    private_constant :REPORT_RESULTS
    SCORE_HEADER = "| MP |  W |  D |  L |  P"
    private_constant :SCORE_HEADER
    SPACE = " "
    private_constant :SPACE
    TABLE_MAX_LENGTH = 55
    private_constant :TABLE_MAX_LENGTH
    TEAM_HEADER = "Team"
    private_constant :TEAM_HEADER

    module_function

    def header
      TEAM_HEADER + space_gap(TEAM_HEADER) + SCORE_HEADER + NEWLINE
    end

    def output(tally)
      tally
        .reduce("", &method(:append_team_results))
        .prepend(header)
    end

    def append_team_results(acc, (team_name, team_results))
      acc +
        team_name +
        space_gap(team_name) +
        team_scores_to_string(team_results) +
        NEWLINE
    end
    private_class_method :append_team_results

    def space_gap(string)
      SPACE * (TABLE_MAX_LENGTH - SCORE_HEADER.length - string.length)
    end
    private_class_method :space_gap

    def team_scores_to_string(team_results)
      REPORT_RESULTS
        .map { |result| "|  #{team_results.public_send(result)}" }
        .join(SPACE)
    end
    private_class_method :team_scores_to_string
  end
  private_constant :Table

  module_function

  def tally(input)
    results = input.strip
    return Table.header if results.empty?

    results
      .then(&method(:tally_results))
      .then(&method(:sort_tally))
      .then(&Table.method(:output))
  end

  def tally_results(results)
    results
      .split(NEWLINE)
      .each_with_object(NEW_TALLY.dup, &method(:tally_result))
  end
  private_class_method :tally_results

  def tally_result(result, results_tally)
    result.split(INPUT_SEPARATOR).tap do |team1, team2, outcome|
      team1_tally = results_tally[team1]
      team2_tally = results_tally[team2]
      tally_outcome(team1_tally, team2_tally, outcome)
    end
  end
  private_class_method :tally_result

  def tally_outcome(team1_tally, team2_tally, outcome)
    case outcome
    when WIN
      tally_win(team1_tally, team2_tally)
    when LOSS
      tally_loss(team1_tally, team2_tally)
    else
      tally_draw(team1_tally, team2_tally)
    end
  end
  private_class_method :tally_outcome

  def tally_win(team1_tally, team2_tally)
    team1_tally.wins += INCREMENT
    team2_tally.losses += INCREMENT
  end
  private_class_method :tally_win

  def tally_loss(team1_tally, team2_tally)
    team1_tally.losses += INCREMENT
    team2_tally.wins += INCREMENT
  end
  private_class_method :tally_loss

  def tally_draw(team1_tally, team2_tally)
    team1_tally.draws += INCREMENT
    team2_tally.draws += INCREMENT
  end
  private_class_method :tally_draw

  def sort_tally(tally)
    tally.sort_by { |team_name, results| [-results.points, team_name] }
  end
  private_class_method :sort_tally
end

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?