Module:User:Cscott/Advent Of Code 2023/Day 16
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('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)()