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