Skip to content

Commit

Permalink
Day 10
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeladler committed Dec 10, 2024
1 parent ac52e3b commit 4c765d6
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
| 7 | Lua | 230ms |
| 8 | Lua | 3.31ms |
| 9 | Lua | 458ms |
| 10 | Lua | 8.94ms |
133 changes: 133 additions & 0 deletions src/day10/day10.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
-- Author: Michael Adler
local M = {}

---@class Point2D
---@field x number
---@field y number
local Point2D = {}
Point2D.__index = Point2D

--- @return Point2D
function Point2D.new(_, x, y)
local instance = setmetatable({}, Point2D)
instance.x = x or 0
instance.y = y or 0
return instance
end

--- @return Point2D
function Point2D.__add(a, b)
return Point2D:new(a.x + b.x, a.y + b.y)
end

--- @return Point2D
function Point2D.__sub(a, b)
return Point2D:new(a.x - b.x, a.y - b.y)
end

--- @return boolean
function Point2D.__eq(a, b)
return a.x == b.x and a.y == b.y
end

--- @return string
function Point2D:hash()
return string.format("%d,%d", self.x, self.y)
end

--- @return string
function Point2D:__tostring()
return string.format("Point2D(%d, %d)", self.x, self.y)
end

local function contains(t, value)
for _, v in pairs(t) do
if v == value then
return true
end
end
return false
end

--- @param input string
M.solve = function(input)
local part1, part2 = 0, 0
local map = {}
local trailheads = {}

local y = 0
for line in input:gmatch("[^\r\n]+") do
y = y + 1

local row = {}
local x = 0
for i = 1, string.len(line) do
x = x + 1
local c = tonumber(string.sub(line, i, i))
if c == 0 then
table.insert(trailheads, Point2D:new(x, y))
end
table.insert(row, c)
end
table.insert(map, row)
end

local row_count = y
local col_count = #map[1]

local all_directions = { Point2D:new(1, 0), Point2D:new(-1, 0), Point2D:new(0, 1), Point2D:new(0, -1) }
--- @param p Point2D
local neighbors = function(p)
local height = map[p.y][p.x]
local result = {}
for _, d in ipairs(all_directions) do
local candidate = p + d
-- check out-of-bounds
if candidate.y >= 1 and candidate.y <= row_count and candidate.x >= 1 and candidate.x <= col_count then
if map[candidate.y][candidate.x] - height == 1 then
table.insert(result, candidate)
end
end
end
return result
end

local nines_reached = {}

local function dfs(start, path)
path = path or {}
table.insert(path, start)
if map[start.y][start.x] == 9 then
nines_reached[start:hash()] = true
return { path }
end
local all_paths = {}
for _, neighbor in ipairs(neighbors(start)) do
if not contains(path, neighbor) then
local path_copy = {}
for _, v in ipairs(path) do
table.insert(path_copy, v)
end
local new_paths = dfs(neighbor, path_copy)
for _, new_path in pairs(new_paths) do
table.insert(all_paths, new_path)
end
end
end
return all_paths
end

for _, p in ipairs(trailheads) do
local all_paths = dfs(p)
for _, _ in pairs(nines_reached) do
part1 = part1 + 1
end
nines_reached = {}

part2 = part2 + #all_paths
end

return part1, part2
end

return M
28 changes: 28 additions & 0 deletions src/day10/day10_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
local day10 = require("day10")

describe("day10", function()
it("solves example 1", function()
local input = [[0123
1234
8765
9876
]]
local part1, _ = day10.solve(input)
assert.are.equal(1, part1)
end)

it("solves example 2", function()
local input = [[89010123
78121874
87430965
96549874
45678903
32019012
01329801
10456732
]]
local part1, part2 = day10.solve(input)
assert.are.equal(36, part1)
assert.are.equal(81, part2)
end)
end)
8 changes: 8 additions & 0 deletions src/day10/main.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env luajit
local day10 = require("day10")
local fname = arg[1] or "input.txt"
local f = assert(io.open(fname, "r"), fname .. " missing")
local input = f:read("*a")
f:close()
local part1, part2 = day10.solve(input)
print(string.format("Part 1: %d\nPart 2: %d", part1, part2))

0 comments on commit 4c765d6

Please sign in to comment.