Module:User:Cscott/Advent Of Code 2023/Day 3
Appearance
return (function()
local builders = {}
local function register(name, f)
builders[name] = f
end
register('advent.compat', function() return require [[Module:User:Cscott/compat]] end)
register('day3', function(myrequire)
--[[
Day 1, first part; advent of code 2023
]]--
local compat = myrequire('advent.compat')
--local inspect = require 'inspect'
--[[
Infrastructure
]]--
function split(str)
lines = {}
for s in string.gmatch(str, "[^\r\n]+") do
table.insert(lines, s)
end
return lines
end
function part1(frame)
local s = frame:expandTemplate{ title = frame.args[1] }
return day1a(split(s))
end
function part2(frame)
local s = frame:expandTemplate{ title = frame.args[1] }
return day1b(split(s))
end
--[[
Part 1
]]--
function make_graph(lines)
local graph = {}
for row = 1,#lines do
local l = lines[row]
graph[row] = {}
for col = 1,#l do
graph[row][col] = l:sub(col, col)
end
end
return graph, #graph, #(graph[1])
end
function is_symbol(c)
return c:match("[0-9.]") == nil
end
function is_digit(c)
return not (c:match("[0-9]") == nil)
end
function char_at(graph, row, col)
if row < 1 then
return "."
elseif row > #graph then
return "."
end
r = graph[row]
if col < 1 then
return "."
elseif col > #r then
return "."
end
return r[col]
end
function symbol_around(graph, row, col)
for r = row-1, row+1 do
for c = col-1, col+1 do
if is_symbol(char_at(graph, r, c)) then
return true
end
end
end
return false
end
function day1a(lines)
local graph, rows, cols = make_graph(lines)
-- look for numbers
local in_number = false
local seen_symbol = false
local seen_number = ""
local sum = 0
for row = 1, rows do
for col = 1, cols do
local c = graph[row][col]
if in_number then
if is_digit(c) then
-- continue our number
seen_number = seen_number .. c
seen_symbol = seen_symbol or symbol_around(graph, row, col)
else
-- finish our number
--print("Found", seen_number, seen_symbol)
in_number = false
if seen_symbol then
local n = 0 + seen_number -- coerce to int
sum = sum + n
end
end
else -- not in_number, see if we want to start one
if is_digit(c) then
in_number = true
seen_number = c
seen_symbol = symbol_around(graph, row, col)
end
end
end
end
return sum
end
--[[
Part 2!
]]--
function gears_around(graph, row, col)
gears = {}
for r = row-1, row+1 do
for c = col-1, col+1 do
if char_at(graph, r, c) == "*" then
if gears[r] == nil then
gears[r] = {}
end
gears[r][c] = true
end
end
end
return gears
end
function day1b(lines)
local graph, rows, cols = make_graph(lines)
-- look for numbers
local in_number = false
local seen_gears = {}
local seen_number = ""
local number_for_gear = {}
for row = 1, rows do
for col = 1, cols do
local c = graph[row][col]
if in_number then
if is_digit(c) then
-- continue our number
seen_number = seen_number .. c
for r,row in pairs(gears_around(graph, row, col)) do
for c,_ in pairs(row) do
if seen_gears[r] == nil then
seen_gears[r] = {}
end
seen_gears[r][c] = true
end
end
else
-- finish our number
-- print("Found", seen_number, inspect(seen_gears))
in_number = false
local n = 0 + seen_number -- coerce to int
for r,row in pairs(seen_gears) do
for c,_ in pairs(row) do
if number_for_gear[r] == nil then
number_for_gear[r] = {}
end
if number_for_gear[r][c] == nil then
number_for_gear[r][c] = {}
end
table.insert(number_for_gear[r][c], n)
end
end
end
else -- not in_number, see if we want to start one
if is_digit(c) then
in_number = true
seen_number = c
seen_gears = gears_around(graph, row, col)
end
end
end
end
-- okay, now find gears bordering exactly two numbers
local sum = 0
for r,row in pairs(number_for_gear) do
for c,nums in pairs(row) do
if #nums > 2 then
-- print("Found",#nums,":",inspect(nums),"???")
elseif #nums == 2 then
sum = sum + (nums[1] * nums[2])
end
end
end
return sum
end
--[[
Testing
]]--
--print(inspect(day1a(split(io.input("day3.example"):read("a")))))
--print(inspect(day1a(split(io.input("day3.input"):read("a")))))
--print(inspect(day1b(split(io.input("day3.example"):read("a")))))
--print(inspect(day1b(split(io.input("day3.input"):read("a")))))
return {
part1 = part1,
part2 = part2,
}
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('day3')
end)()