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

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

register('day2', function(myrequire) -- DAY 2 --

local lpegrex = myrequire('llpeg.lpegrex')

-- PARSING --

local patt = lpegrex.compile( Lines <== nl* Game (nl+ Game)* nl* Game <== `Game` {:id: Number :} `:` {:turns: Turns :} Turns <== Turn (`;` Turn)* Turn <== Color (`,` Color)* Color <== {:count: Number :} SKIP {:color: ColorName :} Number <-- %d+ -> tonumber ColorName <-- `red` / `green` / `blue` nl <- %nl SKIP <- [ ]* NAME_SUFFIX <- [_%w]+ )

function parse(source) --print(inspect(source)) local ast, errlabel, pos = patt:match(source) if not ast then local lineno, colno, line = lpegrex.calcline(source, pos) local colhelp = string.rep(' ', colno-1)..'^' error('syntax error: '..lineno..':'..colno..': '..errlabel..           '\n'..line..'\n'..colhelp) end --print('Parsed with success!') --print(inspect(ast)) return ast end

-- PART 1 --

function find(limit, color) for j = 1, #limit do     local draw = limit[j] if draw.color == color then return draw end end return nil end

function possible(game, limit) for i = 1, #game.turns do     local turn = game.turns[i] for j = 1, #turn do        local draw = turn[j] local match = find(limit, draw.color) if match == nil then return false -- impossible, no matching color elseif match.count < draw.count then return false -- impossible, not enough of this color end end end return true -- this game is possible end

function sum_possible(source, limit) local games = parse(source) -- print(#games, "games\n")

local limit = parse(limit)[1].turns[1] --print(inspect(limit)) local sum = 0 for i = 1, #games do     local game = games[i] if possible(game, limit) then -- print("Game " .. game.id .. " is possible!") sum = sum + game.id     end end -- print("Sum", sum) return sum end

-- PART 2 --

function min_cubes(game) local red, green, blue = 0,0,0 for i = 1, #game.turns do     local turn = game.turns[i] for j = 1, #turn do        local draw = turn[j] if draw.color == 'red' then red = math.max(red, draw.count) elseif draw.color == 'green' then green = math.max(green, draw.count) elseif draw.color == 'blue' then blue = math.max(blue, draw.count) else error("what color is this?") end end end return red, green, blue end

function power(game) local red, green, blue = min_cubes(game) return red * green * blue end

function sum_powers(source) local games = parse(source) -- print(#games, "games\n")

local sum = 0 for i = 1, #games do     local game = games[i] sum = sum + power(game) end -- print("Sum", sum) return sum end

function part1(source, limit) return sum_possible(source, limit) end

function part2(source) return sum_powers(source) end

--[[ CLI start ] ]-- -- local source = "Game 1: 1 blue\nGame 2: 2 red" --local source = io.input("day2.example"):read("a") --local limit = "Game 0: 12 red, 13 green, 14 blue"

local source = io.input("day2.input"):read("*a") local limit = "Game 0: 12 red, 13 green, 14 blue"

print("Sum", sum_possible(source, limit)) print("Power", sum_powers(source)) --[ [ CLI end ]]--

return { part1 = function(frame) local s = mw.title.new(frame.args[1]):getContent local limit = frame.args[2] return part1(s, limit) end, part2 = function(frame) local s = mw.title.new(frame.args[1]):getContent return part2(s) 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('day2') end)