christhekeele

christhekeele

Advent of Code 2022 - Day 2

Continuation of Advent of Code 2022​:christmas_tree:, Day 1:

Day 2!

Leaderboard:

First 10 of 27 Posts Switch mode

christhekeele

christhekeele OP

My solution lives here, with a test suite.


Part 1:
defmodule AoC.TwentyTwentyTwo.Day.Two.Part.One do
  def solve(input) do
    input
    |> Enum.map(&score_round/1)
    |> Enum.sum()
  end

  defp score_round({"A", "X"}), do: 1 + 3
  defp score_round({"A", "Y"}), do: 2 + 6
  defp score_round({"A", "Z"}), do: 3 + 0

  defp score_round({"B", "X"}), do: 1 + 0
  defp score_round({"B", "Y"}), do: 2 + 3
  defp score_round({"B", "Z"}), do: 3 + 6

  defp score_round({"C", "X"}), do: 1 + 6
  defp score_round({"C", "Y"}), do: 2 + 0
  defp score_round({"C", "Z"}), do: 3 + 3
end

Part 2:
defmodule AoC.TwentyTwentyTwo.Day.Two.Part.Two do
  def solve(input) do
    input
    |> Enum.map(&score_round/1)
    |> Enum.sum()
  end

  defp score_round({"A", "X"}), do: 0 + 3
  defp score_round({"A", "Y"}), do: 3 + 1
  defp score_round({"A", "Z"}), do: 6 + 2

  defp score_round({"B", "X"}), do: 0 + 1
  defp score_round({"B", "Y"}), do: 3 + 2
  defp score_round({"B", "Z"}), do: 6 + 3

  defp score_round({"C", "X"}), do: 0 + 2
  defp score_round({"C", "Y"}), do: 3 + 3
  defp score_round({"C", "Z"}), do: 6 + 1
end

A little hard-coded-y; I could have separated the scoring between win or lose, and the two different ways to interpret the second letter in each instruction.

stevensonmt

stevensonmt

Nothing too clever in this solution, I’m afraid. About as naive as it gets.

  def part1(input) do
    File.stream!(input)
    |> Enum.reduce(0, fn line, score -> score + score_line(String.trim(line)) end)
  end

  defp score_line("A X"), do: 4
  defp score_line("A Y"), do: 8
  defp score_line("A Z"), do: 3
  defp score_line("B X"), do: 1
  defp score_line("B Y"), do: 5
  defp score_line("B Z"), do: 9
  defp score_line("C X"), do: 7
  defp score_line("C Y"), do: 2
  defp score_line("C Z"), do: 6

  def part2(input) do
    File.stream!(input)
    |> Enum.reduce(0, fn line, score -> score + score_line_pt2(String.trim(line)) end)
  end

  defp score_line_pt2("A X"), do: score_line("A Z")
  defp score_line_pt2("A Y"), do: score_line("A X")
  defp score_line_pt2("A Z"), do: score_line("A Y")
  defp score_line_pt2("B X"), do: score_line("B X")
  defp score_line_pt2("B Y"), do: score_line("B Y")
  defp score_line_pt2("B Z"), do: score_line("B Z")
  defp score_line_pt2("C X"), do: score_line("C Y")
  defp score_line_pt2("C Y"), do: score_line("C Z")
  defp score_line_pt2("C Z"), do: score_line("C X")
weeksseth

weeksseth

Similar to @stevensonmt.

defmodule Day02 do
  use AOC

  def part1 do
    input(2)
    ~> String.split("\n")
    ~> Enum.map(fn round ->
      case round ~> String.split(" ") do
        ["A", "X"] -> 4
        ["B", "X"] -> 1
        ["C", "X"] -> 7
        ["A", "Y"] -> 8
        ["B", "Y"] -> 5
        ["C", "Y"] -> 2
        ["A", "Z"] -> 3
        ["B", "Z"] -> 9
        ["C", "Z"] -> 6
      end
    end)
    ~> Enum.sum()
  end

  def part2 do
    input(2)
    ~> String.split("\n")
    ~> Enum.map(fn round ->
      case round ~> String.split(" ") do
        ["A", "X"] -> 3
        ["B", "X"] -> 1
        ["C", "X"] -> 2
        ["A", "Y"] -> 4
        ["B", "Y"] -> 5
        ["C", "Y"] -> 6
        ["A", "Z"] -> 8
        ["B", "Z"] -> 9
        ["C", "Z"] -> 7
      end
    end)
    ~> Enum.sum()
  end

end
weeksseth

weeksseth

Not sure why I did that extra split just to pattern match on the array of both of them. Matching on the string makes way more sense.

christhekeele

christhekeele OP

I do like my pattern of parsing input separately from solving each part; keeps the actual interpretation of inputs up to the part of the problem in question (useful here, as we were asked to re-interpret our input).

mudasobwa

mudasobwa

Creator of Cure

I am trying to fit tweet size so far :slight_smile:

score = fn 
  "A X" -> {4, 3}
  "A Y" -> {8, 4}
  "A Z" -> {3, 8}
  "B X" -> {1, 1}
  "B Y" -> {5, 5}
  "B Z" -> {9, 9}
  "C X" -> {7, 2}
  "C Y" -> {2, 6}
  "C Z" -> {6, 7}
end

input
|> String.split("\n")
|> Enum.map(score)
|> Enum.reduce({0, 0}, fn 
  {r1, r2}, {acc1, acc2} -> {acc1 + r1, acc2 + r2}
end)
code-shoily

code-shoily

I was annoyed a little while making this, don’t know why, maybe for all those reading.

Here it is: advent_of_code/lib/2022/day_02.ex at main · code-shoily/advent_of_code · GitHub

Interesting to see so many of our solutions look similar.

kwando

kwando

I ended up writing a bit more code :slight_smile: I realized pretty quickly I could just precompute the score for every combination like most of you did, but what’s the fun in that? :laughing:

I’m using Livebook this year, so thats why the code ends up in fn -> end instead of wrapped in a module :slight_smile:

input =
  File.stream!("/Users/kwando/projects/AoC2022/02/input.txt")
  |> Stream.map(fn line ->
    line
    |> String.trim()
    |> String.split(" ", parts: 2)
    |> List.to_tuple()
  end)

mapping = %{
  "A" => :rock,
  "B" => :paper,
  "C" => :scissors
}

shape_score = fn
  :rock -> 1
  :paper -> 2
  :scissors -> 3
end

game_score = fn
  any, any -> 3
  :rock, :paper -> 6
  :paper, :scissors -> 6
  :scissors, :rock -> 6
  _, _ -> 0
end

tally_games = fn games, your_pick ->
  for {elf, you} <- games, reduce: 0 do
    score ->
      elf = mapping[elf]
      you = your_pick.(elf, you)
      score + game_score.(elf, you) + shape_score.(you)
  end
end

your_pick = fn
  _, "X" -> :rock
  _, "Y" -> :paper
  _, "Z" -> :scissors
end

# part 1
tally_games.(input, your_pick)

# X = lose
# Y = draw
# Z = win
pick_shape = fn
  # losing
  :rock, "X" -> :scissors
  :paper, "X" -> :rock
  :scissors, "X" -> :paper
  # draw
  any, "Y" -> any
  # winning
  :rock, "Z" -> :paper
  :paper, "Z" -> :scissors
  :scissors, "Z" -> :rock
end

# part 2
tally_games.(input, pick_shape)
marcelfahle

marcelfahle

And here’s mine, nothing too fancy and I even though I find multiple functions usually pretty readable, the second part of the puzzle made my head spin a little and is not readable at all :grin:

  @input "./lib/day2_input.txt"

  @x 1
  @y 2
  @z 3

  @win 6
  @loss 0
  @draw 3

  def puzzle1() do
      process(&score/1)
  end

  def puzzle2() do
      process(&cheat/1)
  end

  defp process(fun) do
    @input
    |> File.read!()
    |> String.split(~r/\R/, trim: true)
    |> Enum.map(fn row ->
      row
      |> String.split()
      |> (&(fun.(&1))).()
      end)
    |> Enum.sum()
  end

  defp score(["A", "X"]), do: @x + @draw
  defp score(["A", "Y"]), do: @y + @win
  defp score(["A", "Z"]), do: @z + @loss

  defp score(["B", "X"]), do: @x + @loss
  defp score(["B", "Y"]), do: @y + @draw
  defp score(["B", "Z"]), do: @z + @win

  defp score(["C", "X"]), do: @x + @win
  defp score(["C", "Y"]), do: @y + @loss
  defp score(["C", "Z"]), do: @z + @draw

  defp cheat(["A", "X"]), do: score(["A", "Z"])
  defp cheat(["A", "Y"]), do: score(["A", "X"])
  defp cheat(["A", "Z"]), do: score(["A", "Y"])

  defp cheat(["B", arg]), do: score(["B", arg])

  defp cheat(["C", "X"]), do: score(["C", "Y"])
  defp cheat(["C", "Y"]), do: score(["C", "Z"])
  defp cheat(["C", "Z"]), do: score(["C", "X"])
LostKobrakai

LostKobrakai

I did write a bit more code, but I used metaprogramming to generate all the score cases instead of hardcoding them. Would’ve been more useful it this hadn’t been just a 3x3 grid of possible inputs per line.

Solution
defmodule Day2 do
  def find_score(text) do
    text
    |> String.split("\n")
    |> Enum.reject(&(&1 == ""))
    |> Enum.map(&score/1)
    |> Enum.sum()
  end

  def find_score_alternate(text) do
    text
    |> String.split("\n")
    |> Enum.reject(&(&1 == ""))
    |> Enum.map(&score_alternate/1)
    |> Enum.sum()
  end

  @score_per_type %{rock: 1, paper: 2, scissors: 3}
  @score_per_result %{loss: 0, draw: 3, win: 6}

  scores_per_round =
    for opponent <- Map.keys(@score_per_type),
        myself <- Map.keys(@score_per_type),
        into: %{} do
      result =
        case {opponent, myself} do
          {x, x} -> :draw
          {:scissors, :rock} -> :win
          {:paper, :scissors} -> :win
          {:rock, :paper} -> :win
          _ -> :loss
        end

      opponent_key =
        case opponent do
          :rock -> "A"
          :paper -> "B"
          :scissors -> "C"
        end

      myself_key =
        case myself do
          :rock -> "X"
          :paper -> "Y"
          :scissors -> "Z"
        end

      {"#{opponent_key} #{myself_key}",
       Map.fetch!(@score_per_result, result) + Map.fetch!(@score_per_type, myself)}
    end

  for {round, score} <- scores_per_round do
    defp score(unquote(round)), do: unquote(score)
  end

  scores_per_round_alternate =
    for opponent <- Map.keys(@score_per_type),
        result <- Map.keys(@score_per_result),
        into: %{} do
      myself =
        case {opponent, result} do
          {x, :draw} -> x
          {:scissors, :win} -> :rock
          {:paper, :win} -> :scissors
          {:rock, :win} -> :paper
          {:scissors, :loss} -> :paper
          {:paper, :loss} -> :rock
          {:rock, :loss} -> :scissors
        end

      opponent_key =
        case opponent do
          :rock -> "A"
          :paper -> "B"
          :scissors -> "C"
        end

      result_key =
        case result do
          :loss -> "X"
          :draw -> "Y"
          :win -> "Z"
        end

      {"#{opponent_key} #{result_key}",
       Map.fetch!(@score_per_result, result) + Map.fetch!(@score_per_type, myself)}
    end

  for {round, score} <- scores_per_round_alternate do
    defp score_alternate(unquote(round)), do: unquote(score)
  end

  @doc false
  def debug, do: unquote(Macro.escape(scores_per_round))

  @doc false
  def debug_alternate, do: unquote(Macro.escape(scores_per_round_alternate))
end

Where Next?

Trending in Challenges Top

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
type1fool
I just stumbled on a newly redesigned elixir-lang.org. :tada: It looks like @Software_Mansion did the work, and I think it is generally a...
New
akoutmos
@hugobarauna and I (Alex Koutmos) have been hard at work on writing a book on Nerves that takes you from simply blinking LEDs to building...
New

We're in Beta

About us Mission Statement