Module:User:Cscott/Advent Of Code 2023/Day 6
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('day6', function(myrequire)
--[[ DAY 6 ]]--
local lpegrex = myrequire('llpeg.lpegrex')
--[[ PARSING ]]--
local patt = lpegrex.compile([[
Start <== `Time:` {:times: NumberList :} nl
`Distance:` {:distances: NumberList :} nl* !.
NumberList <-- {| (Number SKIP)* |}
Number <-- %d+ -> tonumber
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))
-- post process
local games = {}
for i,time in ipairs(ast.times) do
local dist = ast.distances[i]
table.insert(games, { time = time, dist = dist })
end
return games
end
function one_game(race)
local t,d = race.time,race.dist
-- solve quadratic formula
local discrim = (t*t) - 4*d
if d < 0 then return 0 end -- no way to win the race
discrim = math.sqrt(discrim)
local min = math.ceil((t - discrim) / 2)
local max = math.floor((t + discrim) / 2)
-- tricky corner case: we can't tie the record we must win it
if ((race.time - min) * min) <= race.dist then
min = min + 1 -- bump it up
end
if ((race.time - max) * max) <= race.dist then
max = max - 1 -- bump it down
end
-- print("min", min, "max", max)
assert(min > 0 and max < race.time)
if min <= max then return (max - min) + 1 end
return 0
end
function all_ways(source)
local races = parse(source)
local prod = 1
for i,r in ipairs(races) do
local ways = one_game(r)
--print("Race", i, "Time", r.time, "Dist", r.dist, "Ways", ways)
prod = prod * ways
end
return prod
end
function part1(source)
return all_ways(source)
end
function part2(source)
-- remove all spaces!
source = string.gsub(source, ' ', '')
source = string.gsub(source, ':', ': ')
return all_ways(source)
end
--[[ CLI start ] ]--
local source = io.input("day6.input"):read("a")
print(part1(source))
print(part2(source))
--[ [ CLI end ]]--
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)
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('day6')
end)()