Module:User:Cscott/Advent Of Code 2023/Day 13
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('day13', function(myrequire)
--[[ DAY 13 ]]--
local l = myrequire('llpeg')
--[[ PARSING ]]--
local Node = {}
Node.__index = Node
function Node:new(args)
return setmetatable(args, self)
end
function Node:__tostring()
if self.rock then return "#" end
return "."
end
local nl = l.P"\n"
function make_node(s)
if s == "#" then return Node:new{rock=true} end
return Node:new{ash=true}
end
local Map = {}
Map.__index = Map
function Map:nRows()
return #(self.data)
end
function Map:nCols()
return #(self.data[1])
end
function Map:__tostring()
local result = {}
for r,row in ipairs(self.data) do
for c,node in ipairs(row) do
table.insert(result, tostring(node))
end
table.insert(result, "\n")
end
return table.concat(result)
end
function Map:try_vertical_mirror(col, smudges) -- # of columns to its left
local s = 0
for r,row in ipairs(self.data) do
local i = 0
repeat
local col1,col2 = col-i,col+1+i
if row[col1].ash ~= row[col2].ash then
s = s + 1
if s > smudges then return false end -- early return
end
i = i + 1
until col1 == 1 or col2 == #row
end
return (s == smudges)
end
function Map:try_horizontal_mirror(row, smudges) -- # of columns to its left
local s = 0
for c = 1,self:nCols() do
local i = 0
repeat
local row1,row2 = row-i,row+1+i
if self.data[row1][c].ash ~= self.data[row2][c].ash then
s = s + 1
if s > smudges then return false end -- early return
end
i = i + 1
until row1 == 1 or row2 == #(self.data)
end
return (s == smudges)
end
function Map:score(smudges)
for i=1,self:nRows()-1 do
if self:try_horizontal_mirror(i, smudges) then
return 100*i
end
end
for i=1,self:nCols()-1 do
if self:try_vertical_mirror(i, smudges) then
return i
end
end
error("no axis of reflection found")
end
function make_map(g)
return setmetatable({ data=g }, Map)
end
local patt = l.P{
"MapList",
MapList = l.Ct( l.V"Map" * (nl * nl * l.V"Map")^0 * nl^0) * -1,
Map = l.Ct( l.V"Row" * (nl * l.V"Row")^0 ) / make_map,
Row = l.Ct( l.V"Node"^1 ),
Node = l.S"#." / make_node,
}
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)
return ast
end
--[[ part 1 ]]--
function part1(source)
local maps = parse(source)
local sum = 0
for i=1,#maps do
local score = maps[i]:score(0)
--print(maps[i])
--print(score)
--print()
sum = sum + score
end
return sum
end
--[[ part 2 ]]--
function part2(source)
local maps = parse(source)
local sum = 0
for i=1,#maps do
local score = maps[i]:score(1)
--print(maps[i])
--print(score)
--print()
sum = sum + score
end
return sum
end
--[[ CLI
local source = io.input("day13.input"):read("a")
--print(inspect(parse(source)))
print('Part 1 Sum:', part1(source))
print('Part 2 Sum:', part2(source))
]]--
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('day13')
end)()