1
0
Fork 0

2020 day 16

This commit is contained in:
Lucidiot 2020-12-16 07:57:32 +01:00
parent b283b337e9
commit 9746694a7b
Signed by: lucidiot
GPG Key ID: 3358C1CA6906FB8D
2 changed files with 145 additions and 1 deletions

144
2020/16/day16.lua Normal file
View File

@ -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())

View File

@ -26,7 +26,7 @@ is acceptable; anything goes as long as I solve it myself!
13 ██ █
14 ██ ██ ██
15 ██ ██
16 ██ ██
16 ██ ██ ██
17 ██
18 ██
19