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.
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!