Compare commits

...

14 Commits

25 changed files with 730 additions and 19 deletions

View File

@@ -8,10 +8,10 @@ This project can also be run using the amazing [CraftOS-PC emulator](https://git
## Progress ## Progress
<!-- calendar-start --> <!-- calendar-start -->
#### Stars: 0/24 #### Stars: 14/24
|Mon|Tue|Wed|Thu|Fri|Sat|Sun| |Mon|Tue|Wed|Thu|Fri|Sat|Sun|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:| |:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|1<br>|2<br>|3<br>|4<br>|5<br>|6<br>|7<br>| |1<br>:star::star:|2<br>:star::star:|3<br>:star::star:|4<br>:star::star:|5<br>:star::star:|6<br>:star::star:|7<br>:star::star:|
|8<br>|9<br>|10<br>|11<br>|12<br>||| |8<br>|9<br>|10<br>|11<br>|12<br>|||
<!-- calendar-end --> <!-- calendar-end -->

10
res/examples/day01.txt Normal file
View File

@@ -0,0 +1,10 @@
L68
L30
R48
L5
R60
L55
L1
L99
R14
L82

3
res/examples/day02.txt Normal file
View File

@@ -0,0 +1,3 @@
11-22,95-115,998-1012,1188511880-1188511890,222220-222224,
1698522-1698528,446443-446449,38593856-38593862,565653-565659,
824824821-824824827,2121212118-2121212124

4
res/examples/day03.txt Normal file
View File

@@ -0,0 +1,4 @@
987654321111111
811111111111119
234234234234278
818181911112111

10
res/examples/day04.txt Normal file
View File

@@ -0,0 +1,10 @@
..@@.@@@@.
@@@.@.@.@@
@@@@@.@.@@
@.@@@@..@.
@@.@@@@.@@
.@@@@@@@.@
.@.@.@.@@@
@.@@@.@@@@
.@@@@@@@@.
@.@.@@@.@.

11
res/examples/day05.txt Normal file
View File

@@ -0,0 +1,11 @@
3-5
10-14
16-20
12-18
1
5
8
11
17
32

4
res/examples/day06.txt Normal file
View File

@@ -0,0 +1,4 @@
123 328 51 64
45 64 387 23
6 98 215 314
* + * +

16
res/examples/day07.txt Normal file
View File

@@ -0,0 +1,16 @@
.......S.......
...............
.......^.......
...............
......^.^......
...............
.....^.^.^.....
...............
....^.^...^....
...............
...^.^...^.^...
...............
..^...^.....^..
...............
.^.^.^.^.^...^.
...............

View File

@@ -1,31 +1,31 @@
{ {
"day01": { "day01": {
"puzzle1": false, "puzzle1": true,
"puzzle2": false "puzzle2": true
}, },
"day02": { "day02": {
"puzzle1": false, "puzzle1": true,
"puzzle2": false "puzzle2": true
}, },
"day03": { "day03": {
"puzzle1": false, "puzzle1": true,
"puzzle2": false "puzzle2": true
}, },
"day04": { "day04": {
"puzzle1": false, "puzzle1": true,
"puzzle2": false "puzzle2": true
}, },
"day05": { "day05": {
"puzzle1": false, "puzzle1": true,
"puzzle2": false "puzzle2": true
}, },
"day06": { "day06": {
"puzzle1": false, "puzzle1": true,
"puzzle2": false "puzzle2": true
}, },
"day07": { "day07": {
"puzzle1": false, "puzzle1": true,
"puzzle2": false "puzzle2": true
}, },
"day08": { "day08": {
"puzzle1": false, "puzzle1": false,

25
src/day01/puzzle1.lua Normal file
View File

@@ -0,0 +1,25 @@
local utils = require "utils"
local puzzle1 = {}
function puzzle1.solve(input)
local password = 0
local cursor = 50
local lines = utils.splitLines(input)
for _, line in ipairs(lines) do
local dir = line:sub(1, 1)
local dist = tonumber(line:sub(2))
if dir == "R" then
cursor = cursor + dist
else
cursor = cursor - dist
end
cursor = cursor % 100
if cursor == 0 then
password = password + 1
end
end
return password
end
return puzzle1

31
src/day01/puzzle2.lua Normal file
View File

@@ -0,0 +1,31 @@
local utils = require "utils"
local puzzle2 = {}
function puzzle2.solve(input)
local password = 0
local cursor = 50
local lines = utils.splitLines(input)
for _, line in ipairs(lines) do
local dir = line:sub(1, 1)
local dist = tonumber(line:sub(2))
if dir == "R" then
cursor = cursor + dist
if cursor > 99 then
password = password + math.floor(cursor / 100)
end
else
cursor = cursor - dist
if cursor <= 0 then
if cursor ~= -dist then
password = password + 1
end
password = password + math.floor(-cursor / 100)
end
end
cursor = cursor % 100
end
return password
end
return puzzle2

38
src/day02/puzzle1.lua Normal file
View File

@@ -0,0 +1,38 @@
local utils = require "utils"
local puzzle1 = {}
function puzzle1.splitRange(range)
local parts = utils.split(range, "-")
return tonumber(parts[1]), tonumber(parts[2])
end
function puzzle1.isValid(id)
local str = tostring(id)
if #str % 2 == 1 then
return true
end
local mid = #str / 2
return str:sub(1, mid) ~= str:sub(mid + 1)
end
function puzzle1.countInvalids(range)
local min, max = puzzle1.splitRange(range)
local total = 0
for i=min, max do
if not puzzle1.isValid(i) then
total = total + i
end
end
return total
end
function puzzle1.solve(input)
local ranges = utils.split(input, ",")
local total = 0
for _, range in ipairs(ranges) do
total = total + puzzle1.countInvalids(range)
end
return total
end
return puzzle1

46
src/day02/puzzle2.lua Normal file
View File

@@ -0,0 +1,46 @@
local utils = require "utils"
local puzzle1 = require(SRC_PATH .. "/day02/puzzle1")
local puzzle2 = {}
function puzzle2.isValid(id)
local str = tostring(id)
local totLen = #str
for len=1, totLen - 1 do
if totLen % len == 0 then
local ref = str:sub(1, len)
local allSame = true
for i=1, totLen / len - 1 do
if str:sub(len * i + 1, len * (i + 1)) ~= ref then
allSame = false
break
end
end
if allSame then
return false
end
end
end
return true
end
function puzzle2.countInvalids(range)
local min, max = puzzle1.splitRange(range)
local total = 0
for i=min, max do
if not puzzle2.isValid(i) then
total = total + i
end
end
return total
end
function puzzle2.solve(input)
local ranges = utils.split(input, ",")
local total = 0
for _, range in ipairs(ranges) do
total = total + puzzle2.countInvalids(range)
end
return total
end
return puzzle2

41
src/day03/puzzle1.lua Normal file
View File

@@ -0,0 +1,41 @@
local utils = require("utils")
local puzzle1 = {}
function puzzle1.bankJoltages(bank)
local joltages = {}
for i=1, #bank do
table.insert(joltages, tonumber(bank:sub(i, i)))
end
return joltages
end
function puzzle1.findMaxJoltage(bank)
local maxTens = 0
local maxJoltage = 0
local joltages = puzzle1.bankJoltages(bank)
for i, tens in ipairs(joltages) do
if tens > maxTens then
for j=i+1, #joltages do
local ones = joltages[j]
local joltage = tens * 10 + ones
if joltage > maxJoltage then
maxTens = tens
maxJoltage = joltage
end
end
end
end
return maxJoltage
end
function puzzle1.solve(input)
local banks = utils.splitLines(input)
local totalJoltage = 0
for _, bank in ipairs(banks) do
totalJoltage = totalJoltage + puzzle1.findMaxJoltage(bank)
end
return totalJoltage
end
return puzzle1

64
src/day03/puzzle2.lua Normal file
View File

@@ -0,0 +1,64 @@
local utils = require("utils")
local puzzle1 = require(SRC_PATH .. "/day03/puzzle1")
local puzzle2 = {}
local cache = {}
function puzzle2.findMaxJoltage(bank, joltages, n, startI)
startI = startI or 1
local key = bank:sub(startI) .. "-" .. tostring(n)
if cache[key] then
return cache[key]
end
local maxJoltage = 0
local maxDigit = 0
if startI > #joltages then
return nil
end
if n == 1 then
for i=startI, #joltages do
if joltages[i] > maxDigit then
maxDigit = joltages[i]
end
end
cache[key] = maxDigit
return maxDigit
end
local i = startI
while i <= #joltages do
local digit = joltages[i]
if digit > maxDigit then
local maxSuffix = puzzle2.findMaxJoltage(bank, joltages, n - 1, i + 1)
if maxSuffix ~= nil then
local joltage = tonumber(tostring(digit) .. tostring(maxSuffix))
if joltage > maxJoltage then
maxDigit = digit
maxJoltage = joltage
end
end
end
i = i + 1
end
if maxJoltage == 0 then
return nil
end
cache[key] = maxJoltage
return maxJoltage
end
function puzzle2.solve(input)
local banks = utils.splitLines(input)
local totalJoltage = 0
for _, bank in ipairs(banks) do
local joltages = puzzle1.bankJoltages(bank)
local max = puzzle2.findMaxJoltage(bank, joltages, 12)
totalJoltage = totalJoltage + max
end
return totalJoltage
end
return puzzle2

44
src/day04/puzzle1.lua Normal file
View File

@@ -0,0 +1,44 @@
local utils = require "utils"
local puzzle1 = {}
function puzzle1.inGrid(x, y, w, h)
return 1 <= x and x <= w and 1 <= y and y <= h
end
function puzzle1.isAccessible(lines, w, h, x, y)
local neighbors = 0
for dy=-1, 1 do
for dx=-1, 1 do
if dx ~=0 or dy ~= 0 then
local x2 = x + dx
local y2 = y + dy
if puzzle1.inGrid(x2, y2, w, h) then
if lines[y2]:sub(x2, x2) == "@" then
neighbors = neighbors + 1
end
end
end
end
end
return neighbors < 4
end
function puzzle1.solve(input)
local lines = utils.splitLines(input)
local accessible = 0
local h = #lines
local w = #lines[1]
for y, line in ipairs(lines) do
for x=1, #line do
if line:sub(x, x) == "@" and puzzle1.isAccessible(lines, w, h, x, y) then
accessible = accessible + 1
end
end
end
return accessible
end
return puzzle1

106
src/day04/puzzle2.lua Normal file
View File

@@ -0,0 +1,106 @@
local utils = require "utils"
local puzzle2 = {}
function puzzle2.inGrid(x, y, w, h)
return 1 <= x and x <= w and 1 <= y and y <= h
end
function puzzle2.countNeighbors(lines, w, h, x, y)
local neighbors = 0
for dy=-1, 1 do
for dx=-1, 1 do
if dx ~=0 or dy ~= 0 then
local x2 = x + dx
local y2 = y + dy
if puzzle2.inGrid(x2, y2, w, h) then
if lines[y2]:sub(x2, x2) == "@" then
neighbors = neighbors + 1
end
end
end
end
end
return neighbors
end
function puzzle2.copyGrid(grid)
local grid2 = {}
for y, row in ipairs(grid) do
local row2 = {}
for x, n in ipairs(row) do
row2[x] = n
end
grid2[y] = row2
end
return grid2
end
function puzzle2.removeRoll(grid, x, y, w, h)
for dy=-1, 1 do
for dx=-1, 1 do
if dx ~= 0 or dy ~= 0 then
local x2 = x + dx
local y2 = y + dy
if puzzle2.inGrid(x2, y2, w, h) then
if grid[y2][x2] > 0 then
grid[y2][x2] = grid[y2][x2] - 1
end
end
end
end
end
grid[y][x] = -1
end
function puzzle2.removeRolls(grid, w, h)
local removed = 0
local grid2 = puzzle2.copyGrid(grid)
for y, row in ipairs(grid) do
for x, n in ipairs(row) do
if 0 <= n and n < 4 then
puzzle2.removeRoll(grid2, x, y, w, h)
removed = removed + 1
end
end
end
return removed, grid2
end
function puzzle2.solve(input)
local lines = utils.splitLines(input)
local h = #lines
local w = #lines[1]
local grid = {}
for y, line in ipairs(lines) do
local row = {}
for x=1, #line do
local neighbors = -1
if line:sub(x, x) == "@" then
neighbors = puzzle2.countNeighbors(lines, w, h, x, y)
end
row[x] = neighbors
end
grid[y] = row
end
local totalRemoved = 0
local removed = 0
while true do
removed, grid = puzzle2.removeRolls(grid, w, h)
if removed == 0 then
break
end
totalRemoved = totalRemoved + removed
end
return totalRemoved
end
return puzzle2

39
src/day05/puzzle1.lua Normal file
View File

@@ -0,0 +1,39 @@
local strings = require "cc.strings"
local puzzle1 = {}
function puzzle1.isFresh(ranges, i)
for _, range in ipairs(ranges) do
if range[1] <= i and i <= range[2] then
return true
end
end
return false
end
function puzzle1.solve(input)
local lines = strings.split(input, "\n")
local ranges = {}
local bIngr = false
local totalFresh = 0
for _, line in ipairs(lines) do
if line == "" then
bIngr = true
else
if bIngr then
local ingr = tonumber(line)
if puzzle1.isFresh(ranges, ingr) then
totalFresh = totalFresh + 1
end
else
local min, max = line:match("(%d+)%-(%d+)")
table.insert(ranges, {tonumber(min), tonumber(max)})
end
end
end
return totalFresh
end
return puzzle1

47
src/day05/puzzle2.lua Normal file
View File

@@ -0,0 +1,47 @@
local strings = require "cc.strings"
local puzzle2 = {}
function puzzle2.solve(input)
local lines = strings.split(input, "\n")
local bounds = {}
for _, line in ipairs(lines) do
if line == "" then
break
end
local min, max = line:match("(%d+)%-(%d+)")
min = tonumber(min)
max = tonumber(max)
table.insert(bounds, {i=min, type="start"})
table.insert(bounds, {i=max, type="end"})
end
table.sort(bounds, function (a, b)
if a.i == b.i then
return a.type == "start" and b.type == "end"
end
return a.i < b.i
end)
local totalFresh = 0
local min = 0
local balance = 0
for _, bound in ipairs(bounds) do
if bound.type == "start" then
if balance == 0 then
min = bound.i
end
balance = balance + 1
else
balance = balance - 1
if balance == 0 then
totalFresh = totalFresh + bound.i - min + 1
end
end
end
return totalFresh
end
return puzzle2

39
src/day06/puzzle1.lua Normal file
View File

@@ -0,0 +1,39 @@
local strings = require "cc.strings"
local puzzle1 = {}
function puzzle1.solve(input)
local lines = strings.split(input, "\n")
local problems = {}
local n = #lines
local ops = strings.split(lines[n], "%s+")
for i, line in ipairs(lines) do
local j = 1
for num in line:gmatch("(%d+)") do
local op = ops[j]
if i == 1 then
if op == "+" then
table.insert(problems, 0)
else
table.insert(problems, 1)
end
end
local val = tonumber(num)
if op == "+" then
problems[j] = problems[j] + val
else
problems[j] = problems[j] * val
end
j = j + 1
end
end
local total = 0
for _, val in ipairs(problems) do
total = total + val
end
return total
end
return puzzle1

58
src/day06/puzzle2.lua Normal file
View File

@@ -0,0 +1,58 @@
local strings = require "cc.strings"
local puzzle2 = {}
function puzzle2.parseNumbers(lines)
local numbers = {}
local n = #lines
for i, line in ipairs(lines) do
if i ~= n then
for j=1, #line do
local c = line:sub(j, j)
if #numbers < j then
table.insert(numbers, {})
end
if c ~= " " then
table.insert(numbers[j], c)
end
end
end
end
local problems = {{}}
for _, num in ipairs(numbers) do
if #num == 0 then
table.insert(problems, {})
else
local value = tonumber(table.concat(num, ""))
table.insert(problems[#problems], value)
end
end
return problems
end
function puzzle2.solve(input)
local lines = strings.split(input, "\n")
local values = puzzle2.parseNumbers(lines)
local ops = strings.split(lines[#lines], "%s+")
local total = 0
for p, numbers in ipairs(values) do
local op = ops[p]
local value = 0
if op == "*" then
value = 1
end
for _, num in ipairs(numbers) do
if op == "+" then
value = value + num
else
value = value * num
end
end
total = total + value
end
return total
end
return puzzle2

34
src/day07/puzzle1.lua Normal file
View File

@@ -0,0 +1,34 @@
local utils = require "utils"
local puzzle1 = {}
function puzzle1.solve(input)
local lines = utils.splitLines(input)
local beams = {}
local totalSplits = 0
for i, line in ipairs(lines) do
local newBeams = {}
if i == 1 then
newBeams[line:find("S")] = true
else
for j=1, #line do
if beams[j] then
if line:sub(j, j) == "^" then
totalSplits = totalSplits + 1
newBeams[j - 1] = true
newBeams[j + 1] = true
else
newBeams[j] = true
end
end
end
end
beams = newBeams
end
return totalSplits
end
return puzzle1

37
src/day07/puzzle2.lua Normal file
View File

@@ -0,0 +1,37 @@
local utils = require "utils"
local puzzle2 = {}
function puzzle2.solve(input)
local lines = utils.splitLines(input)
local beams = {}
for i, line in ipairs(lines) do
local newBeams = {}
if i == 1 then
newBeams[line:find("S")] = 1
else
for j=1, #line do
if beams[j] then
if line:sub(j, j) == "^" then
newBeams[j - 1] = (newBeams[j - 1] or 0) + beams[j]
newBeams[j + 1] = (newBeams[j + 1] or 0) + beams[j]
else
newBeams[j] = (newBeams[j] or 0) + beams[j]
end
end
end
end
beams = newBeams
end
local finalBeams = 0
for _, v in pairs(beams) do
finalBeams = finalBeams + v
end
return finalBeams
end
return puzzle2

View File

@@ -147,7 +147,7 @@ function Day:execPuzzle(puzzleI, data, resultKey)
local result = puzzle.solve(data) local result = puzzle.solve(data)
local t1 = os.epoch("local") local t1 = os.epoch("local")
print(("(Executed in %.3fs)"):format((t1 - t0) / 1000)) print(("(Executed in %.3fs)"):format((t1 - t0) / 1000))
print("Result:", result) print(("Result: %.0f"):format(result))
self.results[resultKey] = result self.results[resultKey] = result
self:saveResults() self:saveResults()
end end

View File

@@ -78,14 +78,18 @@ function utils.waitForKey(targetKey)
end end
end end
function utils.splitLines(data) function utils.split(data, sep)
local t = {} local t = {}
for str in string.gmatch(data, "([^\n]+)") do for str in string.gmatch(data, "([^" .. sep .. "]+)") do
table.insert(t, str) table.insert(t, str)
end end
return t return t
end end
function utils.splitLines(data)
return utils.split(data, "\n")
end
function utils.round(x) function utils.round(x)
return x >= 0 and math.floor(x + 0.5) or math.ceil(x - 0.5) return x >= 0 and math.floor(x + 0.5) or math.ceil(x - 0.5)
end end