Jump to content

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

From Wikipedia, the free encyclopedia
return (function()
local builders = {}
local function register(name, f)
  builders[name] = f
end
register('llpeg', function() return require [[Module:User:Cscott/llpeg]] end)

register('day9', function(myrequire)
--[[ DAY 9 ]]--
local l = myrequire('llpeg')
-- local inspect = require 'inspect'

--[[ PARSING ]]--

local SKIP = l.P" " ^ 0
local nl = l.P"\n"
function tok(s) return l.P(s) * SKIP end

local patt = l.P{
   l.Ct( l.V"NumberLine" * (nl^1 * l.V"NumberLine")^0 * nl^0) * -1,
   NumberLine = l.Ct( (l.V"Number" * SKIP) ^ 1 ),
   Number =  ((l.P"-" ^ -1) * (l.R"09"^1)) / tonumber,
}

function parse(source)
   --print(inspect(source))
   local ast, errlabel, pos = patt:match(source)
   if not ast then
      error(string.format("Error at pos %s label '%s'", pos, errlabel))
   end
   --print('Parsed with success!')
   --print(inspect(ast))
   return ast
end

--[[ Part 1 ]]--

function predict_next(seq)
   local diffs = {}
   local saw_nonzero = false
   for i=2,#seq do
      local d = (seq[i] - seq[i-1])
      table.insert(diffs, d)
      if seq[i] ~= 0 then saw_nonzero = true end
   end
   if saw_nonzero then
      table.insert(diffs, predict_next(diffs))
      -- now compute the next value
      return seq[#seq] + diffs[#diffs]
   else
      -- easy: the sequence was all zeros so predict a zero!
      return 0
   end
end

function part1(source)
   local lines = parse(source)
   local sum = 0
   for i=1,#lines do
      local seq = lines[i]
      local prediction = predict_next(seq)
      -- print(inspect(seq), prediction)
      sum = sum + prediction
   end
   return sum
end

--[[ Part 2 ]]--

function predict_prev_and_next(seq)
   local diffs = {}
   local saw_nonzero = false
   for i=2,#seq do
      local d = (seq[i] - seq[i-1])
      table.insert(diffs, d)
      if seq[i] ~= 0 then saw_nonzero = true end
   end
   if saw_nonzero then
      local p, n = predict_prev_and_next(diffs)
      -- compute the previous value
      local prev = seq[1] - p
      -- now compute the next value
      local nxt = seq[#seq] + n
      return prev,nxt
   else
      -- easy: the sequence was all zeros so predict a zero (on both sides)!
      return 0,0
   end
end

function part2(source)
   local lines = parse(source)
   local sum = 0
   for i=1,#lines do
      local seq = lines[i]
      local p,n = predict_prev_and_next(seq)
      -- print(p, inspect(seq), n)
      sum = sum + p
   end
   return sum
end

--[[ CLI:
local source = io.input("day9.input"):read("a")
print("Sum", part1(source))

print("Sum", part2(source))
]]--

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('day9')
end)()