AOC 2022-02 (Rock Paper Scissors)
Mix.install([
{:kino, "~> 0.7.0"}
])
Shared code
defmodule RPS do
@moves [:rock, :paper, :scissors]
@size Enum.count(@moves)
def result(move, move) when move in @moves, do: :tie
def result(move1, move2) do
index1 = Enum.find_index(@moves, &(&1 == move1))
index2 = Enum.find_index(@moves, &(&1 == move2))
cond do
index1 == nil || index2 == nil -> raise "Invalid move"
Integer.mod(index1 - 1, @size) == index2 -> :win
Integer.mod(index1 + 1, @size) == index2 -> :lose
true -> raise "Invalid game"
end
end
def select_move_to(:tie, against), do: against
def select_move_to(intended_status, against) do
against_index = Enum.find_index(@moves, &(&1 == against))
case intended_status do
:win -> Enum.at(@moves, Integer.mod(against_index + 1, @size))
:lose -> Enum.at(@moves, Integer.mod(against_index - 1, @size))
_ -> raise "Invalid status"
end
end
end
Puzzle Input
input = Kino.Input.textarea("Input")
rounds =
input
|> Kino.Input.read()
|> String.split("\n")
|> Enum.map(&String.split/1)
Part 1
Score the strategy guide
defmodule Round1 do
def new([p1, p2]),
do: %{them: translate_move(p1), me: translate_move(p2), status: nil, score: nil}
def play(%{me: a, them: b} = game), do: %{game | status: RPS.result(a, b)}
def score(%{me: move, status: status} = game) do
%{game | score: points_for(move) + points_for(status)}
end
defp translate_move(char) do
case char do
"A" -> :rock
"B" -> :paper
"C" -> :scissors
"X" -> :rock
"Y" -> :paper
"Z" -> :scissors
_ -> raise "Invalid move"
end
end
defp points_for(move_or_status) do
case move_or_status do
# moves
:rock -> 1
:paper -> 2
:scissors -> 3
# statuses
:win -> 6
:tie -> 3
:lose -> 0
_ -> raise "Invalid move or status"
end
end
end
Enum.map(rounds, fn round ->
round
|> Round1.new()
|> Round1.play()
|> Round1.score()
|> Map.get(:score)
end)
|> Enum.sum()
Part 2
Fix the logic in the strategy guide and score it again.
defmodule Round2 do
def new([p1, p2]),
do: %{them: translate_move(p1), me: nil, status: translate_intended_status(p2), score: -1}
def play(%{them: a, status: b} = game), do: %{game | me: RPS.select_move_to(b, a)}
def score(%{me: move, status: status} = game) do
%{game | score: points_for(move) + points_for(status)}
end
defp translate_move(char) do
case char do
"A" -> :rock
"B" -> :paper
"C" -> :scissors
_ -> raise "Invalid move"
end
end
defp translate_intended_status(char) do
case char do
"X" -> :lose
"Y" -> :tie
"Z" -> :win
_ -> raise "Invalid intended status"
end
end
defp points_for(move_or_status) do
case move_or_status do
# moves
:rock -> 1
:paper -> 2
:scissors -> 3
# statuses
:win -> 6
:tie -> 3
:lose -> 0
_ -> raise "Invalid move or status"
end
end
end
Enum.map(rounds, fn round ->
round
|> Round2.new()
|> Round2.play()
|> Round2.score()
|> Map.get(:score)
end)
|> Enum.sum()