# 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 `` (a list containing `2`); the result is then found by instead comparing `[0,0,0]` and ``.

But if you compare lists according to the rules, `[0,0,0]` vs `` 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  and retry comparison
- Compare [2,3,4] vs 
- 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 `` 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 =
[[[], []] | parsed]
|> Enum.flat_map(&Function.identity/1)
|> Enum.sort(&Decoder.compare/2)

divider_packet_1 = Enum.find_index(sorted, &(&1 == [])) + 1
divider_packet_2 = Enum.find_index(sorted, &(&1 == [])) + 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
[[], [] | packets]
|> Enum.sort(__MODULE__)
|> Enum.with_index(1)
|> Enum.filter(fn {p, _} -> p == [] or p == [] 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