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

return (function local builders = {} local function register(name, f) builders[name] = f end register('llpeg', function return require Module:User:Cscott/llpeg end)

register('day16', function(myrequire) -- DAY 16 -- local l = myrequire('llpeg')

-- PARSING -- local Spot = {} Spot.__index = Spot function Spot:new(args) return setmetatable(args, self) end function Spot:is_mirror return self.char=='/' or self.char=='\\' end function Spot:is_splitter return self.char=='-' or self.char=='|' end function Spot:is_empty return self.char=='.' end function Spot:__tostring return self.char end

local nl = l.P"\n"

function make_spot(s) return Spot:new{char=s} end

local patt = l.P{  "Graph", Graph = l.Ct( l.V"Row" * (nl^1 * l.V"Row")^0 * nl^0) * -1, Row = l.Ct( l.V"Spot"^1 ), Spot = l.S".\\/-|" / make_spot, }

local Graph = {} Graph.__index = Graph

function parse(source) --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 Graph:new(ast) end

-- Part 1 --

function Graph:new(data) return setmetatable({ data=data }, self) end

function Graph:at(row,col,default) return (self.data[row] or {})[col] or default end

function Graph:rowN return #(self.data) end

function Graph:colN return #(self.data[1]) end

function Graph:print for r,row in ipairs(self.data) do     for c,val in ipairs(row) do         if val == nil then val = " " elseif val.energized then val = "#" end io.write(tostring(val)) end io.write("\n") end end

function Graph:link for r=1,self:rowN do     for c=1,self:colN do         local sp = self:at(r,c) sp.r, sp.c = r,c if r > 1 then sp.n = self:at(r-1,c) end if c > 1 then sp.w = self:at(r,c-1) end if r < self:rowN then sp.s = self:at(r+1,c) end if c < self:colN then sp.e = self:at(r,c+1) end end end end

function Graph:clearAndScore local sum = 0 for r=1,self:rowN do     for c=1,self:colN do         local sp = self:at(r,c) if sp.energized then sum = sum + 1 sp.energized = nil sp.seen_n = nil sp.seen_e = nil sp.seen_w = nil sp.seen_s = nil end end end return sum end

local mirror_effect = { -- east becomes north, north -> east, south-west, west-south ['/'] = { e='n', n='e', s='w', w='s' }, -- east becomes south, south->east, north->west, west->north ['\\'] = { e='s', s='e', n='w', w='n' }, ['|'] = { e='ns', w='ns', n='n', s='s' }, ['-'] = { e='e', w='w', n='ew', s='ew' }, ['.'] = { n='n', e='e', s='s', w='w' }, }

function ray_cast(sp, dir) if sp['seen_'..dir] ~= nil then return end sp.energized = true sp['seen_'..dir] = true local ndir = mirror_effect[sp.char][dir] if #ndir == 1 then local nsp = sp[ndir] if nsp ~= nil then return ray_cast(nsp, ndir) -- tail call end else for i=1,#ndir do        local nsp = sp[ndir:sub(i,i)] if nsp ~= nil then ray_cast(nsp, ndir:sub(i,i)) end end end end

function part1(source) local graph = parse(source) graph:link --graph:print --print ray_cast(graph:at(1,1),"e") --graph:print return graph:clearAndScore end

function part2(source) local graph = parse(source) graph:link local max = 0 local function check(r,c,dir) ray_cast(graph:at(r,c),dir) local score = graph:clearAndScore if score > max then max = score end end for r=1,graph:rowN do     check(r,1,"e") check(r,graph:colN,"w") end for c=1,graph:colN do     check(1,c,"s") check(graph:rowN,c,"n") end return max end

-- CLI ] ]-- local source = io.input("day16.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('day16') end)