Tidy up Day 16 Part 2
Fewer maps, simpler variable names, break up complex function
This commit is contained in:
parent
343d2ef962
commit
cd95b1713c
1 changed files with 24 additions and 29 deletions
|
@ -5,9 +5,11 @@ defmodule Day16Part2 do
|
|||
tickets
|
||||
|> Enum.filter(&valid?(&1, rules))
|
||||
|> transpose_tickets()
|
||||
|> identify_columns(rules)
|
||||
|> Enum.filter(&match?({"departure" <> _, _idx}, &1))
|
||||
|> Enum.reduce(1, fn {_name, idx}, acc -> my_ticket[idx] * acc end)
|
||||
|> 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
|
||||
|
||||
|
@ -22,7 +24,6 @@ defmodule Day16Part2 do
|
|||
|
||||
def transpose_tickets(tickets, columns, field_index, stop) do
|
||||
{remaining_columns, column} = next_column(tickets)
|
||||
|
||||
transpose_tickets(remaining_columns, [{field_index, column} | columns], field_index + 1, stop)
|
||||
end
|
||||
|
||||
|
@ -30,33 +31,27 @@ defmodule Day16Part2 do
|
|||
Enum.map_reduce(tickets, [], fn [field | rest], column -> {rest, [field | column]} end)
|
||||
end
|
||||
|
||||
# Probably overcomplicated this.
|
||||
# First find which columns satisfy which rules.
|
||||
# The number of valid fields for each column is unique, which doesn't seem like a coincidence.
|
||||
# Then allocate the columns to fields starting from the column with the smallest number
|
||||
# of valid columns - need to filter out the known values for the rest of the columns.
|
||||
# It's the final filtering part that makes me think I'm missing a trick.
|
||||
def identify_columns(transposed_tickets, rules) do
|
||||
{field_map, by_valid_count} =
|
||||
Enum.reduce(transposed_tickets, {%{}, %{}}, fn {id, column}, {field_map, by_valid_count} ->
|
||||
names = names_for(column, rules)
|
||||
valid_count = length(names)
|
||||
|
||||
{Map.put(field_map, id, names), Map.put(by_valid_count, valid_count, id)}
|
||||
end)
|
||||
|
||||
{identified, _seen} =
|
||||
by_valid_count
|
||||
|> Enum.sort(fn {k1, _}, {k2, _} -> k1 <= k2 end)
|
||||
|> Enum.reduce({%{}, MapSet.new()}, fn {_, col}, {identified, seen} ->
|
||||
field = field_map[col] |> MapSet.new() |> MapSet.difference(seen) |> Enum.at(0)
|
||||
{Map.put(identified, field, col), MapSet.put(seen, field)}
|
||||
end)
|
||||
|
||||
identified
|
||||
# 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 {idx, column} <- transposed_tickets, do: {idx, valid_fields_for(column, rules)}
|
||||
end
|
||||
|
||||
def names_for(column, rules) do
|
||||
# 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.reduce({[], []}, fn {col, fields}, {identified, seen} ->
|
||||
[field] = fields -- seen
|
||||
{[{field, col} | identified], [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)
|
||||
|
|
Loading…
Reference in a new issue