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

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)