Add version with simpler transpose
This commit is contained in:
parent
ab7c6d5d33
commit
44092ab958
2 changed files with 86 additions and 0 deletions
|
@ -39,6 +39,13 @@ Anyway, solve it by:
|
||||||
I'm sure there is a more efficient way to handle this rather than the building up n lists
|
I'm sure there is a more efficient way to handle this rather than the building up n lists
|
||||||
and then for each entry removing that from the remaining lists.
|
and then for each entry removing that from the remaining lists.
|
||||||
|
|
||||||
|
** UPDATE 17 December **
|
||||||
|
|
||||||
|
I refactored part 2, and I'm much happier with it now.
|
||||||
|
Most of the simplifications I thought of myself, but there was one change I read on the ElixirForum,
|
||||||
|
using Enum.zip to transpose the ticket rows to columns, which I wanted to use, so I committed that
|
||||||
|
change only to day16paet2-simpler-transpose.exs.
|
||||||
|
|
||||||
|
|
||||||
+------------------+
|
+------------------+
|
||||||
| Overall Thoughts |
|
| Overall Thoughts |
|
||||||
|
|
79
day16/day16part2-simpler-transpose.exs
Normal file
79
day16/day16part2-simpler-transpose.exs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
defmodule Day16Part2 do
|
||||||
|
def run do
|
||||||
|
{rules, my_ticket, tickets} = File.read!("input") |> parse_input()
|
||||||
|
|
||||||
|
tickets
|
||||||
|
|> Enum.filter(&valid?(&1, rules))
|
||||||
|
|> transpose_tickets()
|
||||||
|
|> valid_fields_for_columns(rules)
|
||||||
|
|> reduce_to_column_by_field_name()
|
||||||
|
|> Stream.filter(&match?({"departure" <> _, _idx}, &1))
|
||||||
|
|> Stream.map(fn {_name, idx} -> my_ticket[idx] end)
|
||||||
|
|> Enum.reduce(&Kernel.*/2)
|
||||||
|
|> IO.puts()
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid?(ticket, rules) do
|
||||||
|
Enum.all?(ticket, fn field ->
|
||||||
|
Enum.any?(rules, fn {_name, {range1, range2}} -> field in range1 || field in range2 end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def transpose_tickets(tickets) do
|
||||||
|
tickets |> Enum.zip() |> Enum.map(&Tuple.to_list/1) |> Enum.with_index()
|
||||||
|
end
|
||||||
|
|
||||||
|
# Compare each column against the rules to determine the list of fields each column satisfies
|
||||||
|
def valid_fields_for_columns(transposed_tickets, rules) do
|
||||||
|
for {column, idx} <- transposed_tickets, do: {idx, valid_fields_for(column, rules)}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Reduce the list of valid fields by elimination.
|
||||||
|
# The number of valid fields for each column is unique, which doesn't seem like a coincidence.
|
||||||
|
# Start with the column that only has one possible valid field, mark the field as seen.
|
||||||
|
# Continue to the column with two valid fields, subtract the seen field, mark remaining field as seen.
|
||||||
|
# Repeat for all columns.
|
||||||
|
def reduce_to_column_by_field_name(valid_fields_by_column) do
|
||||||
|
valid_fields_by_column
|
||||||
|
|> Enum.sort_by(fn {_k, v} -> length(v) end)
|
||||||
|
|> Enum.map_reduce([], fn {col, fields}, seen ->
|
||||||
|
[field] = fields -- seen
|
||||||
|
{{field, col}, [field | seen]}
|
||||||
|
end)
|
||||||
|
|> elem(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_fields_for(column, rules) do
|
||||||
|
Enum.filter(rules, fn {_field, {range1, range2}} ->
|
||||||
|
Enum.all?(column, fn val -> val in range1 || val in range2 end)
|
||||||
|
end)
|
||||||
|
|> Enum.map(&elem(&1, 0))
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_input(input) do
|
||||||
|
[rules, [_, my_ticket | _], [_ | nearby_tickets]] =
|
||||||
|
input
|
||||||
|
|> String.split("\n\n", trim: true)
|
||||||
|
|> Enum.map(&String.split(&1, "\n", trim: true))
|
||||||
|
|
||||||
|
rules = Map.new(rules, &parse_rule/1)
|
||||||
|
|
||||||
|
[my_ticket | nearby_tickets] =
|
||||||
|
[my_ticket | nearby_tickets]
|
||||||
|
|> Enum.map(fn ticket -> String.split(ticket, ",") |> Enum.map(&String.to_integer/1) end)
|
||||||
|
|
||||||
|
my_ticket = my_ticket |> Enum.with_index() |> Map.new(fn {val, idx} -> {idx, val} end)
|
||||||
|
|
||||||
|
{rules, my_ticket, nearby_tickets}
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_rule(rule) do
|
||||||
|
[name | ranges] =
|
||||||
|
Regex.run(~r/\A(.*): (\d+)-(\d+) or (\d+)-(\d+)\z/, rule, capture: :all_but_first)
|
||||||
|
|
||||||
|
[a, b, c, d] = Enum.map(ranges, &String.to_integer/1)
|
||||||
|
{name, {a..b, c..d}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Day16Part2.run()
|
Loading…
Reference in a new issue