Module:User:Cscott/Advent Of Code 2023/Day 15

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)