Module:User:Cscott/Advent Of Code 2023/Day 15
Appearance
return (function()
local builders = {}
local function register(name, f)
builders[name] = f
end
register('llpeg', function() return require [[Module:User:Cscott/llpeg]] end)
register('day15', function(myrequire)
--[[ DAY 15 ]]--
local lpeg = myrequire('llpeg')
--[[ PARSING ]]--
function split (s, sep)
sep = lpeg.P(sep)
local elem = lpeg.C((1 - sep)^0)
local p = lpeg.Ct(elem * (sep * elem)^0) -- make a table capture
return lpeg.match(p, s)
end
function parse1(s)
s = s:gsub("\n", "")
return split(s, ",")
end
function hash(s)
local currentValue = 0
for _,ascii in ipairs{string.byte(s, 1, #s)} do
currentValue = currentValue + ascii
currentValue = currentValue * 17
currentValue = currentValue % 256
end
return currentValue
end
function part1(s)
local sum = 0
for _,ss in ipairs(parse1(s)) do
sum = sum + hash(ss)
end
return sum
end
--[[ PART 2 ]]--
local l = lpeg
local nl = l.P"\n"
local patt = l.P{
l.Ct( l.V"Op" * (l.P"," * l.V"Op")^0 * nl^0 ) * -1,
Label = l.Cg( l.R"az"^1, "label" ) * l.Cg( l.Cb("label") / hash, "hash"),
Number = (l.R"09"^1) / tonumber,
OpDash = l.V"Label" * l.P"-" * l.Cg(l.Cc(true), "dash"),
OpEq = l.V"Label" * l.P"=" * l.Cg( l.V"Number", "focalLen" ),
Op = l.Ct( l.V"OpDash" + l.V"OpEq" ),
}
function parse2(source)
source = source:gsub("\n", "")
--print(inspect(source))
local ast, errlabel, pos = patt:match(source)
if not ast then
error(string.format("Error at pos %s label '%s'", pos, errlabel))
end
--print('Parsed with success!')
--print(inspect(ast))
return ast
end
local Lens = {}
Lens.__index = Lens
function Lens:new(props)
return setmetatable(props or {}, self)
end
local Box = {}
Box.__index = Box
function Box:new()
return setmetatable({ lensList={}, lensMap={} }, self)
end
function Box:get(label)
return self.lensMap[label]
end
function Box:addLens(label, lens)
table.insert(self.lensList, lens)
lens.pos = #(self.lensList)
self.lensMap[label] = lens
end
function Box:remove(label)
local old = self:get(label)
old.focalLen = nil -- tombstone
self.lensMap[label] = nil -- remove
end
function Box:sum(mult)
local i = 1
local sum = 0
for _,l in ipairs(self.lensList) do
if l.focalLen ~= nil then
sum = sum + (mult * i * l.focalLen)
i = i + 1
end
end
return sum
end
local Hashmap = {}
Hashmap.__index = Hashmap;
function Hashmap:new(s)
return setmetatable(s or {}, self)
end
function Hashmap:getBox(i)
local box = self[i+1]
if box == nil then
box = Box:new()
self[i+1] = box
end
return box
end
function Hashmap:execute(op)
if op.dash then
self:executeDash(op)
else
self:executeEq(op)
end
end
function Hashmap:executeDash(op)
local box = self:getBox(op.hash)
local old = box:get(op.label)
if old ~= nil then
box:remove(op.label)
end
end
function Hashmap:executeEq(op)
local box = self:getBox(op.hash)
local old = box:get(op.label)
if old ~= nil then
-- already a lens with this label, replace it
old.focalLen = op.focalLen
else
local lens = Lens:new{focalLen = op.focalLen}
box:addLens(op.label, lens)
end
end
function Hashmap:sum()
local sum = 0
for i=0,255 do
local box = self:getBox(i)
sum = sum + box:sum(i+1)
end
return sum
end
function part2(source)
local h = Hashmap:new()
local ops = parse2(source)
for i=1,#ops do
h:execute(ops[i])
end
return h:sum()
end
--[[ CLI ] ]--
local source = io.input("day15.input"):read("*a")
print('Sum:', part1(source))
print('Sum:', part2(source))
--[ [ END CLI ]]--
return {
part1 = function(frame)
local s = mw.title.new(frame.args[1]):getContent()
return part1(s)
end,
part2 = function(frame)
local s = mw.title.new(frame.args[1]):getContent()
return part2(s, tonumber(frame.args[2]))
end,
}
end)
local modules = {}
modules['table'] = require('table')
modules['string'] = require('string')
modules['strict'] = {}
local function myrequire(name)
if modules[name] == nil then
modules[name] = true
modules[name] = (builders[name])(myrequire)
end
return modules[name]
end
return myrequire('day15')
end)()