2020 day 16
This commit is contained in:
parent
b283b337e9
commit
9746694a7b
|
@ -0,0 +1,144 @@
|
|||
local rules, my_ticket, tickets = {}, {}, {}
|
||||
|
||||
local parsing_mode = 0
|
||||
for line in io.lines() do
|
||||
if line == '' then
|
||||
-- who cares!
|
||||
elseif line == 'your ticket:' then
|
||||
parsing_mode = 1
|
||||
elseif line == 'nearby tickets:' then
|
||||
parsing_mode = 2
|
||||
elseif parsing_mode == 0 then
|
||||
-- Parse the rule into a validation function
|
||||
local name, low1, high1, low2, high2 = line:match('(.+): (%d+)-(%d+) or (%d+)-(%d+)')
|
||||
low1, high1, low2, high2 = tonumber(low1), tonumber(high1), tonumber(low2), tonumber(high2)
|
||||
local function validator(value)
|
||||
return (value >= low1 and value <= high1) or (value >= low2 and value <= high2)
|
||||
end
|
||||
rules[name] = validator
|
||||
elseif parsing_mode == 1 then
|
||||
-- Parse my ticket
|
||||
for n in line:gmatch('%d+') do
|
||||
table.insert(my_ticket, tonumber(n))
|
||||
end
|
||||
else
|
||||
-- Parse another ticket
|
||||
local ticket = {}
|
||||
for n in line:gmatch('%d+') do
|
||||
table.insert(ticket, tonumber(n))
|
||||
end
|
||||
table.insert(tickets, ticket)
|
||||
end
|
||||
end
|
||||
|
||||
local function is_valid(field)
|
||||
for _, validator in pairs(rules) do
|
||||
if validator(field) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local invalid = {}
|
||||
|
||||
local function part1()
|
||||
local total = 0
|
||||
for i, ticket in ipairs(tickets) do
|
||||
for _, field in ipairs(ticket) do
|
||||
if not is_valid(field) then
|
||||
total = total + field
|
||||
-- Help part 2 by storing the index of fully invalid tickets
|
||||
table.insert(invalid, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return total
|
||||
end
|
||||
|
||||
--[[
|
||||
Build a table that maps, for each rule, all the columns it can apply on.
|
||||
Then work by elimination to assemble the ticket order, and return the
|
||||
product of all departure fields.
|
||||
--]]
|
||||
local function part2()
|
||||
-- Remove invalid tickets found in part 1
|
||||
table.sort(invalid, function (a, b) return a > b end)
|
||||
for _, i in ipairs(invalid) do
|
||||
table.remove(tickets, i)
|
||||
end
|
||||
|
||||
-- For each rule, maps all the columns that fully match the rule.
|
||||
local columns = {}
|
||||
for name, _ in pairs(rules) do
|
||||
columns[name] = {}
|
||||
end
|
||||
|
||||
-- For each rule…
|
||||
for name, validator in pairs(rules) do
|
||||
-- For each column…
|
||||
for i = 1, #tickets[1] do
|
||||
local success = true
|
||||
-- Check all tickets. If any ticket does not match, abort mission
|
||||
for _, ticket in ipairs(tickets) do
|
||||
if not validator(ticket[i]) then
|
||||
success = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if success then
|
||||
-- This column matches!
|
||||
table.insert(columns[name], i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Field resolution
|
||||
local ordering = {}
|
||||
local function ordering_completed()
|
||||
for i = 1, #tickets[1] do
|
||||
if not ordering[i] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
while not ordering_completed() do
|
||||
--[[
|
||||
Loop over every rule until we find one where only a single column
|
||||
has not been matched yet; this is the column of that rule.
|
||||
--]]
|
||||
for name, applicable_columns in pairs(columns) do
|
||||
local matched, unique = nil, true
|
||||
for _, column in ipairs(applicable_columns) do
|
||||
if not ordering[column] then
|
||||
if matched then
|
||||
-- A column was already found before, so we cannot dedupe yet
|
||||
unique = false
|
||||
break
|
||||
else
|
||||
-- Keep this column and keep going to check for unicity
|
||||
matched = column
|
||||
end
|
||||
end
|
||||
end
|
||||
if matched and unique then
|
||||
ordering[matched] = name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Output the product
|
||||
local total = 1
|
||||
for i, name in ipairs(ordering) do
|
||||
if name:match('^departure') then
|
||||
total = total * my_ticket[i]
|
||||
end
|
||||
end
|
||||
return total
|
||||
end
|
||||
|
||||
print(part1())
|
||||
print(part2())
|
Loading…
Reference in New Issue