Module:User:Cscott/Advent Of Code/2024/Day 2
Appearance
return (function()
local builders = {}
local function register(name, f)
builders[name] = f
end
register('advent.compat', function() return require [[Module:User:Cscott/compat]] end)
register('llpeg.lpegrex', function() return require [[Module:User:Cscott/lpegrex]] end)
register('util', function(myrequire)
local function read_wiki_input(func)
return function (frame, ...)
if type(frame)=='string' then
frame = { args = { frame, ... } }
end
local title = mw.title.new(frame.args[1])
local source = title:getContent()
if source == nil then
error("Can't find title " .. tostring(title))
end
source = source:gsub("^%s*<syntaxhighlight[^>]*>\n?", "", 1)
source = source:gsub("</syntaxhighlight[^>]*>%s*$", "", 1)
return func(source, frame.args[2], frame.args[3])
end
end
return {
read_wiki_input = read_wiki_input,
}
end)
register('day2', function(myrequire)
--[[
Day 2, first part; advent of code 2024
]]--
local compat = myrequire('advent.compat')
local lpegrex = myrequire('llpeg.lpegrex')
local read_wiki_input = myrequire('util').read_wiki_input
--[[ PARSING ]]--
local patt = lpegrex.compile([[
Lines <== nl* Report (nl+ Report)* nl*
Report <-- {| (Number SKIP)+ |}
Number <-- %d+ -> tonumber
nl <- %nl
SKIP <- [ ]*
NAME_SUFFIX <- [_%w]+
]])
function parse(source)
--print(inspect(source))
local ast, errlabel, pos = patt:match(source)
if not ast then
local lineno, colno, line = lpegrex.calcline(source, pos)
local colhelp = string.rep(' ', colno-1)..'^'
error('syntax error: '..lineno..':'..colno..': '..errlabel..
'\n'..line..'\n'..colhelp)
end
--print('Parsed with success!')
--print(inspect(ast))
return ast
end
--[[
Infrastructure
]]--
function part1(s)
return day2a(parse(s))
end
function part2(s)
return day2b(parse(s))
end
--[[
Part 1
]]--
function checkSafe(report, min, max)
for i,level in ipairs(report) do
if i > 1 then
local diff = level - report[i-1]
if diff < min or diff > max then
return false
end
end
end
return true
end
function day2a(reports)
local safeCount = 0
for _,report in ipairs(reports) do
local isSafe
if report[1] < report[2] then
isSafe = checkSafe(report, 1, 3)
else
isSafe = checkSafe(report, -3, -1)
end
if isSafe then
safeCount = safeCount + 1
end
end
return safeCount
end
--[[
Part 2!
]]--
function checkSafeDampener(report, min, max)
local sawBad = false
local skipOne = true -- handles skipping the first, too
for i,level in ipairs(report) do
if skipOne then
skipOne = false
else
local diff = level - report[i-1]
if diff < min or diff > max then
if sawBad then
return false
end
-- ok, we have to drop either (i) or (i-1) to make this safe
if i == #report then
-- at the end of the list, just drop the final element
return true
end
-- try dropping i, need to check (i-1) vs (i+1)
diff = report[i+1] - report[i-1]
if diff >= min and diff <= max then
sawBad = true
skipOne = true -- don't test i vs i+1 since i will be dropped
elseif i == 2 then
-- dropping i-1 is just dropping the first element. keep
-- going.
sawBad = true
else
-- try dropping (i-1), but need to check (i-2) vs (i)
diff = level - report[i-2]
if diff >= min and diff <= max then
sawBad = true
else
return false
end
end
end
end
end
return true
end
function day2b(reports)
local safeCount = 0
for _,report in ipairs(reports) do
if checkSafeDampener(report, 1, 3) or checkSafeDampener(report, -3, -1) then
safeCount = safeCount + 1
end
end
return safeCount
end
--[[
Testing
]]--
--[[
io.write("Safe: ", day2a(parse(io.input("day2.example"):read("a"))), "\n")
io.write("Safe: ", day2a(parse(io.input("day2.input"):read("a"))), "\n")
io.write("\nWith dampener:\n")
io.write("Safe: ", day2b(parse(io.input("day2.example"):read("a"))), "\n")
io.write("Safe: ", day2b(parse(io.input("day2.input"):read("a"))), "\n")
]]--
return {
part1 = read_wiki_input(part1),
part2 = read_wiki_input(part2),
}
end)
local modules = {}
modules['bit32'] = require('bit32')
modules['string'] = require('string')
modules['strict'] = {}
modules['table'] = require('table')
local function myrequire(name)
if modules[name] == nil then
modules[name] = true
modules[name] = (builders[name])(myrequire)
end
return modules[name]
end
return myrequire('day2')
end)()