# Advent of Code 2022 - Day 2

Lots of good answers. Nothing special here pensandoemelixir/day2.exs at main · adolfont/pensandoemelixir · GitHub

If I understand what you’re getting at: I think substituting `Enum.map` with `Stream.map`, in this case, effectively does what you’re talking about. Can’t say if it’ll perform better/worse, but the input list will only be passed over once

Like most others, went with the direct approach once i figured there were only 9 cases. Although today I remembered to use File.stream

Although with all the magic numbers here I would probably use constants for the score calculations even in a first iteration.

3 Likes

I prefer the solution from @tfwright. It is faster and cleaner. Should have used `Enum.reduce/3`.

part 1

``````defmodule MyModule do
def outcome_score(65, 88), do: 3 # Rock     A - X Rock
def outcome_score(65, 89), do: 6 # Rock     A - Y Paper
def outcome_score(65, 90), do: 0 # Rock     A - Z Scissors
def outcome_score(66, 88), do: 0 # Paper    B - X Rock
def outcome_score(66, 89), do: 3 # Paper    B - Y Paper
def outcome_score(66, 90), do: 6 # Paper    B - Z Scissors
def outcome_score(67, 88), do: 6 # Scissors C - X Rock
def outcome_score(67, 89), do: 0 # Scissors C - Y Paper
def outcome_score(67, 90), do: 3 # Scissors C - Z Scissors

def shape_score(88), do: 1 # X
def shape_score(89), do: 2 # Y
def shape_score(90), do: 3 # Z
end

start = System.monotonic_time(:microsecond)

File.stream!("input.txt")
|> Stream.map(fn <<opponent, " ", me, "\n">> -> MyModule.shape_score(me) + MyModule.outcome_score(opponent, me) end)
|> Enum.sum()
|> tap(fn sum -> IO.puts "Total score: #{sum}" end)

elapsed = System.monotonic_time(:microsecond) - start
IO.puts "Job done in #{elapsed} µs"
``````

part 2

``````defmodule MyModule do
def score(65, 88), do: 0+3 # Rock     A - X Lose => Scissors
def score(65, 89), do: 3+1 # Rock     A - Y Draw => Rock
def score(65, 90), do: 6+2 # Rock     A - Z Win  => Paper
def score(66, 88), do: 0+1 # Paper    B - X Lose => Rock
def score(66, 89), do: 3+2 # Paper    B - Y Draw => Paper
def score(66, 90), do: 6+3 # Paper    B - Z Win  => Scissors
def score(67, 88), do: 0+2 # Scissors C - X Lose => Paper
def score(67, 89), do: 3+3 # Scissors C - Y Draw => Scissors
def score(67, 90), do: 6+1 # Scissors C - Z Win  => Rock
end

start = System.monotonic_time(:microsecond)

File.stream!("input.txt")
|> Stream.map(fn <<opponent, " ", goal, "\n">> -> MyModule.score(opponent, goal) end)
|> Enum.sum()
|> tap(fn sum -> IO.puts "Total score: #{sum}" end)

elapsed = System.monotonic_time(:microsecond) - start
IO.puts "Job done in #{elapsed} µs"
``````

My take.
part 1 and part 2 computed at the same time in the reduce. I wanted to avoid hard-coding, and keep it below 20 lines of code.

``````File.stream!("input")
|> Enum.map(& String.to_charlist(&1) |> List.to_tuple() )
|> Enum.reduce({0, 0}, fn {i, _, j, _}, {acc_part1, acc_part2} ->
{ acc_part1 + j - 87 +
case Integer.mod(j - i - (?X-?A), 3) do
2 -> 0       # loose
x -> (x+1)*3 # win or draw
end,
acc_part2 + case j do
?X -> Integer.mod(i - ?A - 1, 3) + 1 + 0 # loose
?Y -> Integer.mod(i - ?A + 0, 3) + 1 + 3 # draw
?Z -> Integer.mod(i - ?A + 1, 3) + 1 + 6 # win
end
}
end)
|> IO.inspect()

``````

Part 1

``````input
|> String.split("\n", trim: true)
|> Enum.reduce(0, fn
"A X", acc -> acc + 3 + 1
"A Y", acc -> acc + 6 + 2
"A Z", acc -> acc + 0 + 3

"B X", acc -> acc + 0 + 1
"B Y", acc -> acc + 3 + 2
"B Z", acc -> acc + 6 + 3

"C X", acc -> acc + 6 + 1
"C Y", acc -> acc + 0 + 2
"C Z", acc -> acc + 3 + 3
end)
``````

Part 2

``````input
|> String.split("\n", trim: true)
|> Enum.reduce(0, fn
"A X", acc -> acc + 0 + 3
"A Y", acc -> acc + 3 + 1
"A Z", acc -> acc + 6 + 2

"B X", acc -> acc + 0 + 1
"B Y", acc -> acc + 3 + 2
"B Z", acc -> acc + 6 + 3

"C X", acc -> acc + 0 + 2
"C Y", acc -> acc + 3 + 3
"C Z", acc -> acc + 6 + 1
end)
``````

You can find my solution here : Livebook.dev (comments are in french, but code is pretty expressive)

Here are some interesting part : the function playing the game :

``````defmodule Day2Helpers.Outcome do
# Rock defeats Scissors, Scissors defeats Paper, and Paper defeats Rock.
# If both players choose the same shape, the round instead ends in a draw.

def round_outcome({:scissors, :rock}), do: :win
def round_outcome({:paper, :scissors}), do: :win
def round_outcome({:rock, :paper}), do: :win

def round_outcome({opponent, me}) when opponent == me do
:draw
end

def round_outcome({_, _}), do: :loose
end
``````

Then for part two, I tried playing the game until I find the expected result :

``````  @shapes [:rock, :paper, :scissors]
#[...]
defp c2_shape("X", opponent) do
# Let's play the game and keep only loosing input shapes.
@shapes
|> Enum.filter(fn shape -> Day2Helpers.Outcome.round_outcome({opponent, shape}) == :loose end)
|> Enum.at(0)
end
``````

I’m pretty new to Elixir, so feedback is always appreciated!