feat: add progress calendar

This commit is contained in:
2025-11-29 14:40:58 +01:00
parent b4445da204
commit 513d27c6c9
4 changed files with 192 additions and 13 deletions

17
README.md Normal file
View File

@@ -0,0 +1,17 @@
# Advent of Code
This repo contains my attempt at this year's Advent of Code (2025)
I will solve this AoC using Lua in the context of the ComputerCraft Minecraft mod.\
This project can also be run using the amazing [CraftOS-PC emulator](https://github.com/MCJack123/craftos2)
## Progress
<!-- calendar-start -->
#### Stars: 0/24
|Mon|Tue|Wed|Thu|Fri|Sat|Sun|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|1<br>|2<br>|3<br>|4<br>|5<br>|6<br>|7<br>|
|8<br>|9<br>|10<br>|11<br>|12<br>|||
<!-- calendar-end -->

125
src/calendar.lua Normal file
View File

@@ -0,0 +1,125 @@
package.path = "/aoc/src/lib/?.lua;" .. package.path
require("aoc")
local json = require("json")
local utils = require("utils")
local buffer = require("buffer")
local stats = json.loads(utils.readFile(RES_PATH .. "/stats.json")) or {}
local readmePath = ROOT_PATH .. "/README.md"
local outBuf = buffer.Buffer.new()
local function insertInReadme()
local body = utils.readFile(readmePath) or ""
local tagStart = "<!-- calendar-start -->"
local tagEnd = "<!-- calendar-end -->"
body = body:gsub(
tagStart:gsub("%-", "%%-") .. ".-" .. tagEnd:gsub("%-", "%%-"),
tagStart .. "\n" .. outBuf:tostring() .. tagEnd
)
utils.writeFile(readmePath, body, true)
end
local totalStars = 0
for _, s in pairs(stats) do
if s.puzzle1 then totalStars = totalStars + 1 end
if s.puzzle2 then totalStars = totalStars + 1 end
end
local startTS = 1764543600
local startDate = os.date("*t", startTS)
local firstWeekday = (startDate.wday + 5) % 7
local padding = {x=3, y=1}
local dayNames = {
"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
}
local day = -firstWeekday + 1
local rowSep = "+"
local padRow = "|"
for _ = 1, 7 do
rowSep = rowSep .. string.rep("-", padding.x * 2 + 3) .. "+"
padRow = padRow .. string.rep(" ", padding.x * 2 + 3) .. "|"
end
local function printRow(cells, cellWidth, padding)
local xPad = string.rep(" ", padding.x)
local height = 0
for _, cell in ipairs(cells) do
height = math.max(height, type(cell) == "string" and 1 or #cell)
end
for i = -padding.y + 1, height + padding.y do
write("|")
for _, cell in ipairs(cells) do
local text = cell[i] or ""
if type(cell) == "string" and i == 1 then
text = cell
end
local pad = cellWidth - #text
local lPad = math.ceil(pad / 2)
local rPad = pad - lPad
write(xPad .. string.rep(" ", lPad))
if i > 1 and i <= height then
term.setTextColor(colors.orange)
end
write(text)
term.setTextColor(colors.white)
write(string.rep(" ", rPad) .. xPad .. "|")
end
print()
end
print(rowSep)
end
term.clear()
term.setCursorPos(1, 1)
term.setTextColor(colors.white)
print(("Stars: %d/24"):format(totalStars, 24))
outBuf:print(("#### Stars: %d/24\n"):format(totalStars, 24))
print(rowSep)
printRow(dayNames, 3, padding)
outBuf:print("|" .. table.concat(dayNames, "|") .. "|")
for _ = 1, 7 do
outBuf:write("|:-:")
end
outBuf:print("|")
while day < 12 do
local row = {}
local outRow = {}
for _ = 1, 7 do
if day < 1 or day > 12 then
table.insert(row, "")
table.insert(outRow, "")
else
local dayStats = stats[("day%02d"):format(day)]
local stars = 0
if dayStats.puzzle1 then stars = stars + 1 end
if dayStats.puzzle2 then stars = stars + 1 end
local cell = {
tostring(day),
string.rep("\x04", stars, " ")
}
table.insert(row, cell)
cell = {
tostring(day),
string.rep(":star:", stars, "")
}
table.insert(outRow, table.concat(cell, "<br>"))
end
day = day + 1
end
printRow(row, 3, padding)
outBuf:print("|" .. table.concat(outRow, "|") .. "|")
end
insertInReadme()
---@diagnostic disable-next-line: undefined-field
term.screenshot()
os.sleep(2)

View File

@@ -1,5 +1,6 @@
SRC_PATH = "/aoc/src" ROOT_PATH = "/aoc"
RES_PATH = "/aoc/res" SRC_PATH = ROOT_PATH .. "/src"
RES_PATH = ROOT_PATH .. "/res"
CACHE_PATH = "/.cache/aoc" CACHE_PATH = "/.cache/aoc"
package.path = SRC_PATH .. "/lib/?.lua;" .. package.path package.path = SRC_PATH .. "/lib/?.lua;" .. package.path
@@ -12,8 +13,9 @@ local dates = require("dates")
local days = require("days") local days = require("days")
local progress = require "progress" local progress = require "progress"
local today = os.date("*t") local today = os.date("*t")
local aoc = {}
local function loadStats(path) function aoc.loadStats(path)
path = shell.resolve(path) path = shell.resolve(path)
if not fs.exists(path) then if not fs.exists(path) then
printError("Stats file not found (" .. path .. ")") printError("Stats file not found (" .. path .. ")")
@@ -29,7 +31,7 @@ local function loadStats(path)
return data return data
end end
local function printDateInfo() function aoc.printDateInfo()
if dates.isBefore(START_DATE, today) then if dates.isBefore(START_DATE, today) then
print("AoC 2025 has not started yet") print("AoC 2025 has not started yet")
return return
@@ -42,7 +44,7 @@ local function printDateInfo()
print("Day " .. day .. "/" .. END_DATE.day) print("Day " .. day .. "/" .. END_DATE.day)
end end
local function printStats(stats, selected) function aoc.printStats(stats, selected)
local keys = {} local keys = {}
for k in pairs(stats) do for k in pairs(stats) do
table.insert(keys, k) table.insert(keys, k)
@@ -100,7 +102,7 @@ local function printStats(stats, selected)
print("Press END to quit") print("Press END to quit")
end end
local function printBanner() function aoc.printBanner()
term.setTextColor(colors.green) term.setTextColor(colors.green)
print("+--------------------------------------+") print("+--------------------------------------+")
print("| Welcome to the Advent of Code 2025 |") print("| Welcome to the Advent of Code 2025 |")
@@ -108,8 +110,8 @@ local function printBanner()
term.setTextColor(colors.white) term.setTextColor(colors.white)
end end
local function main() function aoc.main()
local stats = loadStats("aoc/res/stats.json") or {} local stats = aoc.loadStats("aoc/res/stats.json") or {}
local selectedDay = math.max(1, math.min(END_DATE.day, today.day)) local selectedDay = math.max(1, math.min(END_DATE.day, today.day))
if not dates.isInDateRange(START_DATE, today, END_DATE) then if not dates.isInDateRange(START_DATE, today, END_DATE) then
selectedDay = 1 selectedDay = 1
@@ -118,9 +120,9 @@ local function main()
while true do while true do
term.clear() term.clear()
term.setCursorPos(1, 1) term.setCursorPos(1, 1)
printBanner() aoc.printBanner()
printDateInfo() aoc.printDateInfo()
printStats(stats, selectedDay) aoc.printStats(stats, selectedDay)
local event, key, is_held = os.pullEvent("key") local event, key, is_held = os.pullEvent("key")
if key == keys.up then if key == keys.up then
@@ -137,9 +139,9 @@ local function main()
dayStats.puzzle2 dayStats.puzzle2
) )
day:show() day:show()
stats = loadStats("aoc/res/stats.json") or {} stats = aoc.loadStats("aoc/res/stats.json") or {}
end end
end end
end end
main() return aoc

35
src/lib/buffer.lua Normal file
View File

@@ -0,0 +1,35 @@
local buffer = {}
---@class Buffer
local Buffer = {_buf=""}
Buffer.__index = Buffer
---Creates a new buffer
---@param str string?
---@return Buffer
function Buffer.new(str)
local buf = {}
buf._buf = str or ""
setmetatable(buf, Buffer)
return buf
end
function Buffer:tostring()
return self._buf
end
function Buffer:clear()
self._buf = ""
end
function Buffer:write(text)
self._buf = self._buf .. (text or "")
end
function Buffer:print(text)
self:write((text or "") .. "\n")
end
buffer.Buffer = Buffer
return buffer