feat: add progress calendar
This commit is contained in:
17
README.md
Normal file
17
README.md
Normal 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
125
src/calendar.lua
Normal 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)
|
||||||
@@ -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
35
src/lib/buffer.lua
Normal 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
|
||||||
Reference in New Issue
Block a user