Advent of Code 2020 in Elixir - Day 22

Crab Combat

defmodule Aoc2020.Day22 do
  @moduledoc "Crab Combat"

  def run() do
    part_2()
  end

  def part_1() do
    [player1, player2] = parse_input()

    play_combat(player1, player2)
    |> calculate_score()
  end

  def part_2() do
    decks = parse_input()
    IO.puts("Starting game")

    play_recursive(decks, [])
    |> Enum.find(fn deck -> length(deck) > 0 end)
    |> calculate_score()
  end

  def parse_input() do
    File.read!("priv/inputs/2020/day22.txt")
    # test_input()
    |> String.trim()
    |> String.split("\n\n")
    |> Enum.map(&parse_deck/1)
  end

  def test_input() do
    "Player 1:\n9\n2\n6\n3\n1\n\nPlayer 2:\n5\n8\n4\n7\n10"
  end

  def loop_test() do
    "Player 1:\n43\n19\n\nPlayer 2:\n2\n29\n14"
  end

  def parse_deck(str) do
    str
    |> String.split("\n")
    |> tl()
    |> Enum.map(&String.to_integer/1)
  end

  def play_combat(p1_deck, []), do: p1_deck
  def play_combat([], p2_deck), do: p2_deck

  def play_combat([p1_card | p1_deck], [p2_card | p2_deck]) when p1_card > p2_card do
    play_combat(p1_deck ++ [p1_card, p2_card], p2_deck)
  end

  def play_combat([p1_card | p1_deck], [p2_card | p2_deck]) when p2_card > p1_card do
    play_combat(p1_deck, p2_deck ++ [p2_card, p1_card])
  end

  def play_recursive([player1, _] = decks, history, game \\ 1) do
    IO.inspect(game: game, turn: length(history) + 1)

    if Enum.member?(history, decks) do
      [player1, []]
    else
      case decks do
        [p1_deck, []] ->
          [p1_deck, []]

        [[], p2_deck] ->
          [[], p2_deck]

        [[p1_card | p1_deck], [p2_card | p2_deck]]
        when p1_card <= length(p1_deck) and p2_card <= length(p2_deck) ->
          case play_recursive([p1_deck, p2_deck], [decks | history], game + 1) do
            [_, []] ->
              play_recursive([p1_deck ++ [p1_card, p2_card], p2_deck], [decks | history], game)

            [[], _] ->
              play_recursive([p1_deck, p2_deck ++ [p2_card, p1_card]], [decks | history], game)
          end

        [[p1_card | p1_deck], [p2_card | p2_deck]] when p1_card > p2_card ->
          play_recursive([p1_deck ++ [p1_card, p2_card], p2_deck], [decks | history], game)

        [[p1_card | p1_deck], [p2_card | p2_deck]] when p2_card > p1_card ->
          play_recursive([p1_deck, p2_deck ++ [p2_card, p1_card]], [decks | history], game)
      end
    end
  end

  def calculate_score(deck) do
    Enum.reverse(deck)
    |> Enum.with_index()
    |> Enum.map(fn {v, index} -> v * (index + 1) end)
    |> Enum.sum()
  end
end