# Advent Of Code 2022 - Day 13

Anyone else think the prompt for this challenge is contradictory?
The rules for comparing packets include

• If both values are lists, compare the first value of each list, then the second value, and so on. If the left list runs out of items first, the inputs are in the right order. If the right list runs out of items first, the inputs are not in the right order. If the lists are the same length and no comparison makes a decision about the order, continue checking the next part of the input.
• If exactly one value is an integer, convert the integer to a list which contains that integer as its only value, then retry the comparison. For example, if comparing [0,0,0] and 2, convert the right value to [2] (a list containing 2); the result is then found by instead comparing [0,0,0] and [2].

But if you compare lists according to the rules, [0,0,0] vs [2] should be incorrect order since the right side runs out of items first. But the sample data evaluation weâ€™re told

Compare [2,3,4] vs 4
- Mixed types; convert right to [4] and retry comparison
- Compare [2,3,4] vs [4]
- Compare 2 vs 4
- Left side is smaller, so inputs are in the right order

So the rule for â€ślist vs wrapped integerâ€ť is not the same as the rule for â€ślist vs listâ€ť. Am I crazy?

I didnâ€™t understand what they meant in Part 2 for longer than Iâ€™d like to. But part 1 was super fun to solve.

@stevensonmt
Yeah, it was a bit annoying to get rightâ€¦ but in your example
[0, 0, 0] vs [2] you would compare 0 vs 2 first and left is smaller

Turned out okay, I donâ€™t dare to clean this up more now that it works
Code.eval_string saved me from some parsing fun.

1 Like

Nice, looks very clean

1 Like

I was having a hard time understanding the rules, too. The thing is, when you traverse two lists, you stop as soon as the result is either right or wrong. Only if it stays undecided until one of the lists are out of items, then that rule decides.

Today it felt like an Elixir puzzle againâ€¦

Parsing:

parsed =
input_field
|> String.split("\n", trim: true)
|> Enum.map(&Code.eval_string/1)
|> Enum.map(&elem(&1, 0))
|> Enum.chunk_every(2)

Decoder:

defmodule Decoder do
def compare([], []), do: :none
def compare([_ | _], []), do: false
def compare([], [_ | _]), do: true

def compare([a | resta], [b | restb]) do
case compare(a, b) do
:none -> compare(resta, restb)
other -> other
end
end

def compare(a, b) when is_integer(a) and is_integer(b) do
cond do
a < b -> true
a > b -> false
true -> :none
end
end

def compare(a, b), do: compare(List.wrap(a), List.wrap(b))
end

Part 1:

parsed
|> Enum.map(fn [a, b] -> Decoder.compare(a, b) end)
|> Enum.with_index(1)
|> Enum.filter(&(elem(&1, 0) == true))
|> Enum.map(&elem(&1, 1))
|> Enum.sum()

Part 2:

sorted =
[[[[2]], [[6]]] | parsed]
|> Enum.flat_map(&Function.identity/1)
|> Enum.sort(&Decoder.compare/2)

divider_packet_1 = Enum.find_index(sorted, &(&1 == [[2]])) + 1
divider_packet_2 = Enum.find_index(sorted, &(&1 == [[6]])) + 1
divider_packet_1 * divider_packet_2
1 Like

I have the same kind of solution, but my compare/2 function returns :lt | :gt | :eq which means that I can call Enum.sort(packets, __MODULE__)

defmodule Aoe.Y22.Day13 do
alias Aoe.Input

end

def parse_input!(input, _part) do
input
|> String.trim()
|> String.split("\n")
|> Enum.flat_map(&parse_line/1)
end

defp parse_line("") do
[]
end

defp parse_line(text) do
[Jason.decode!(text)]
end

def part_one(packets) do
packets
|> Enum.chunk_every(2)
|> Enum.map(&compare/1)
|> Enum.with_index(1)
|> Enum.filter(fn {order, _} -> order == :lt end)
|> Enum.reduce(0, fn {_, index}, acc -> acc + index end)
end

def part_two(packets) do
[[[2]], [[6]] | packets]
|> Enum.sort(__MODULE__)
|> Enum.with_index(1)
|> Enum.filter(fn {p, _} -> p == [[6]] or p == [[2]] end)
|> case(do: ([{_, a}, {_, b}] -> a * b))
end

def compare([left, right]) do
compare(left, right)
end

def compare([a | as], [b | bs]) do
case compare(a, b) do
:eq -> compare(as, bs)
other -> other
end
end

def compare([], []) do
:eq
end

def compare([], [_ | _]) do
:lt
end

def compare([_ | _], []) do
:gt
end

def compare(a, b) when is_integer(a) and is_integer(b) do
cond do
a < b -> :lt
a > b -> :gt
a == b -> :eq
end
end

def compare(a, b) when is_list(a) and is_integer(b) do
compare(a, [b])
end

def compare(a, b) when is_integer(a) and is_list(b) do
compare([a], b)
end
end

Every year I start doing AoC in Rust now, but after a week I have no time for it since I have to work, so I fallback on Elixir and then it feels like cheating

3 Likes

Thatâ€™s neat!

Similar to others â€“ made a â€śsorterâ€ť function for Enum.sort/2. Although, doesnâ€™t seem like anyoneâ€™s sorter functions are strictly identical which is cool!

Using &Enum.sort/2 in part 2 with absolutely no modifications to the comparator was something else

2 Likes

We did almost the same thing

1 Like

Was a nice break after the last few days

Although I am not proud of my comparison logicâ€¦zip wasnâ€™t a good choice due to the need to pad the arrays, pattern matching on head/tail as others did is much better.

I used Code.string_to_quoted since lists and integers are both conveniently represented as themselves in AST, and it seems less likely to release nasal demons than Code.eval_string.

I really like how the definition of compare winds up pretty much matching the paragraphs in the problem statement; for instance, the last two clauses perfectly encapsulate the â€śinteger vs listâ€ť rule.

1 Like