defmodule Solution do @spec get_total_winnings(String.t()) :: integer() def get_total_winnings(filename, with_jokers \\ false) do hands = filename |> File.stream!() |> Enum.map(&parse_line/1) |> Map.new() sorted_cards = hands |> Map.keys() |> Enum.sort_by(fn hand -> get_sorting_order(hand, with_jokers) end) sorted_cards |> Enum.with_index(1) |> Enum.reduce(0, fn {el, idx}, acc -> acc + idx * hands[el] end) end defp parse_line(line) do [hand, bid] = line |> String.trim() |> String.split(" ") {hand, String.to_integer(bid)} end # Map a hand to a list that may be used for sorting. defp get_sorting_order(hand, with_jokers) do # Map cards to corresponding power, # e.g. "57TA" -> [5, 7, 10, 14] powers = hand |> String.graphemes() |> Enum.map(fn card -> get_card_power(card, with_jokers) end) # Prepend hand rank. [get_rank(hand, with_jokers) | powers] end @doc """ Converts a card symbol to a number. ## Examples iex> Solution.get_card_power("J") 11 iex> Solution.get_card_power("J", true) 1 iex> Solution.get_card_power("5") 5 """ def get_card_power(card, with_jokers \\ false) do case card do "A" -> 14 "K" -> 13 "Q" -> 12 "J" -> if with_jokers, do: 1, else: 11 "T" -> 10 num -> String.to_integer(num) end end @doc """ Returns the rank of a hand based on the number of identical cards. ## Examples iex> Solution.get_rank("AAAAA") 7 iex> Solution.get_rank("AA8AA") 6 iex> Solution.get_rank("23332") 5 iex> Solution.get_rank("TTT98") 4 iex> Solution.get_rank("23432") 3 iex> Solution.get_rank("A23A4") 2 iex> Solution.get_rank("23456") 1 """ def get_rank(hand, with_jokers \\ false) do hand = if with_jokers, do: replace_joker(hand), else: hand freq = hand |> String.graphemes() |> Enum.frequencies() |> Map.values() |> Enum.sort() case freq do # five of a kind [5] -> 7 # four of a kind [1, 4] -> 6 # full house [2, 3] -> 5 # three of a kind [1, 1, 3] -> 4 # two pair [1, 2, 2] -> 3 # one pair [1, 1, 1, 2] -> 2 # high card _ -> 1 end end @doc """ Replaces all J cards in hand with the card that will result in the best rank. ## Examples iex> Solution.replace_joker("T55J5") "T5555" iex> Solution.replace_joker("KTJJT") "KTTTT" iex> Solution.replace_joker("JJJJJ") "AAAAA" """ def replace_joker(hand) do freq = hand |> String.replace("J", "") |> String.graphemes() |> Enum.frequencies() if Enum.empty?(Map.keys(freq)) do String.replace(hand, "J", "A") else max_count = freq |> Map.values() |> Enum.max() {most_used_symbol, _} = freq |> Map.to_list() |> Enum.find(fn {_, count} -> count == max_count end) String.replace(hand, "J", most_used_symbol) end end end