exercism/elixir/tournament/tournament.exs

59 lines
2.0 KiB
Elixir

defmodule Tournament do
defmodule Stat do
defstruct mp: 0, w: 0, d: 0, l: 0, p: 0
end
@doc """
Given `input` lines representing two teams and whether the first of them won,
lost, or reached a draw, separated by semicolons, calculate the statistics
for each team's number of games played, won, drawn, lost, and total points
for the season, and return a nicely-formatted string table.
A win earns a team 3 points, a draw earns 1 point, and a loss earns nothing.
Order the outcome by most total points for the season, and settle ties by
listing the teams in alphabetical order.
"""
@spec tally(input :: list(String.t())) :: String.t()
def tally(input) do
import String, only: [pad_trailing: 2, pad_leading: 2]
input
|> Enum.map(&String.split(&1, ";"))
|> Enum.filter(&(length(&1) == 3 && Enum.at(&1, 2) in ["win", "loss", "draw"]))
|> Enum.reduce(%{}, fn [team1, team2, outcome], acc ->
acc
|> Map.put(team1, stats(Map.get(acc, team1, %Stat{}), outcome))
|> Map.put(team2, stats(Map.get(acc, team2, %Stat{}), opposite_outcome(outcome)))
end)
|> Enum.sort_by(fn {name, hist} -> {-hist.p, name} end)
|> Enum.reduce("Team | MP | W | D | L | P", fn {name, hist}, acc ->
acc <>
"\n" <>
Enum.join(
[
pad_trailing(name, 30),
pad_leading(to_string(hist.mp), 2),
pad_leading(to_string(hist.w), 2),
pad_leading(to_string(hist.d), 2),
pad_leading(to_string(hist.l), 2),
pad_leading(to_string(hist.p), 2)
],
" | "
)
end)
end
defp stats(team, "win"), do: %{team | mp: team.mp + 1, w: team.w + 1, p: team.p + 3}
defp stats(team, "loss"), do: %{team | mp: team.mp + 1, l: team.l + 1}
defp stats(team, "draw"), do: %{team | mp: team.mp + 1, d: team.d + 1, p: team.p + 1}
defp opposite_outcome(outcome) do
case outcome do
"win" -> "loss"
"loss" -> "win"
"draw" -> "draw"
end
end
end