Compare commits

..

No commits in common. "main" and "5e5192c9a3d7e2338ce9c6bd76801c709a505d71" have entirely different histories.

40 changed files with 133 additions and 1448 deletions

View File

@ -1,8 +0,0 @@
root = true
[*]
charset = utf-8
[*.nim]
indent_style = space
indent_size = 2

13
.gitignore vendored
View File

@ -1,13 +1,2 @@
# binaries
**/first
**/first.exe
**/second
**/second.exe
**/both
**/both.exe
# input files
**/input.txt
# session.key
session.key
input.txt

View File

@ -1,34 +0,0 @@
import
std/strformat,
std/strutils
import
../../adventofcode
let input = adventofcode.getInput(2021, 2)
let lines = input.splitLines()
var horizontalPosition = 0
var depth = 0
when defined(second):
var aim = 0
for line in lines:
var command = line.split(" ")
case command[0]:
of "forward":
horizontalPosition += command[1].parseInt()
when defined(second):
depth += aim * command[1].parseInt()
of "up":
when defined(second):
aim -= command[1].parseInt()
else:
depth -= command[1].parseInt()
of "down":
when defined(second):
aim += command[1].parseInt()
else:
depth += command[1].parseInt()
echo fmt"Answer: {horizontalPosition * depth}"

View File

@ -1,57 +0,0 @@
import
std/math,
std/sequtils,
std/strformat,
std/strutils,
std/tables
import
../../adventofcode
type
Cycle = int
let input = adventofcode.getInput(2021, 6)
var days = 0
var fishes: Table[Cycle, int]
for i in 0 .. 8:
fishes[i] = 0
proc parseInput() =
let cycles = input.split(",").mapIt(it.parseInt())
for cycle in cycles:
fishes[cycle] += 1
proc newDay() =
days += 1
var temporaryBox: Table[Cycle, int]
for i in 0 .. 8:
temporaryBox[i] = 0
for cycle in [8, 7, 6, 5, 4, 3, 2, 1]:
let count = fishes[cycle]
fishes[cycle] -= count
temporaryBox[cycle - 1] += count
block cycle0:
let count = fishes[0]
fishes[0] -= count
temporaryBox[6] += count
temporaryBox[8] += count
for cycle in temporaryBox.keys():
fishes[cycle] += temporaryBox[cycle]
when isMainModule:
const simulateDays = block:
when defined(second): 256
else: 80
parseInput()
for i in 1 .. simulateDays:
newDay()
echo fmt"{days} days passed."
echo fmt"Answer: {math.sum toSeq fishes.values()}"

View File

@ -1,29 +0,0 @@
import
std/options,
std/sequtils,
std/strformat,
std/strutils
import
../../adventofcode
let input = adventofcode.getInput(2021, 7)[0 .. ^2]
#let input = "16,1,2,0,4,2,7,1,2,14" # Example given
let crabs = input.split(",").mapIt(it.parseInt())
let minPos = min(crabs)
let maxPos = max(crabs)
var bestPosResult = none int
var bestFuelResult = none int
for pos in minPos .. maxPos:
var fuel = 0
for crab in crabs:
fuel += max(crab, pos) - min(crab, pos)
if bestFuelResult.isNone() or fuel < bestFuelResult.get():
bestPosResult = some pos
bestFuelResult = some fuel
echo fmt"Position : {bestPosResult.get()}"
echo fmt"Fuel | Answer: {bestFuelResult.get()}"

View File

@ -1,22 +0,0 @@
import
std/math,
std/sequtils,
std/strformat,
std/strutils
import
../../adventofcode
let input = adventofcode.getInput(2021, 7)
#let input = "16,1,2,0,4,2,7,1,2,14" # Example given
let crabs = input.split(",").mapIt(it.parseInt())
let avg = (math.sum crabs) div crabs.len()
var fuel = 0
for crab in crabs:
let distance = max(crab, avg) - min(crab, avg)
fuel += sum toSeq 1 .. distance
echo fmt"Position : {avg}"
echo fmt"Fuel | Answer: {fuel}"

View File

@ -1,26 +0,0 @@
import
std/math,
std/sequtils,
std/strformat,
std/strutils,
std/tables
import
../../adventofcode
let lines = adventofcode.getInput(2021, 8).splitLines()
var decodedDigits = newTable[int, int]()
for i in [2, 3, 4, 7]:
decodedDigits[i] = 0
for line in lines:
let lineParts = line.split(" | ")
# We only consider output digits for now
let outputDigits = lineParts[1].split(" ")
for outputDigit in outputDigits:
if decodedDigits.hasKey outputDigit.len():
decodedDigits[outputDigit.len()] += 1
echo fmt"Answer: {math.sum toSeq decodedDigits.values()}"

View File

@ -1,40 +0,0 @@
import
std/math,
std/options,
std/sequtils,
std/strformat,
std/strutils
import
../../adventofcode
type
Grid = seq[seq[int]]
proc at(g: Grid; x, y: int): Option[int] =
try: result = some g[y][x]
except IndexDefect: discard
let input = adventofcode.getInput(2021, 9)
let grid = Grid input.splitLines().mapIt(toSeq(it).mapIt(($it).parseInt()))
var lowPoints = newSeq[int]()
for y in 0 .. grid.high():
for x in 0 .. grid[y].high():
let p = grid.at(x, y).get()
let adjA = grid.at(x - 1, y)
if adjA.isSome() and p >= adjA.get(): continue
let adjB = grid.at(x, y - 1)
if adjB.isSome() and p >= adjB.get(): continue
let adjC = grid.at(x + 1, y)
if adjC.isSome() and p >= adjC.get(): continue
let adjD = grid.at(x, y + 1)
if adjD.isSome() and p >= adjD.get(): continue
lowPoints.add p
var riskLevels = lowPoints.mapIt(it + 1)
echo fmt"Answer: {math.sum riskLevels}"

View File

@ -1,13 +1,11 @@
import
std/sequtils,
std/strformat,
std/strutils
import
../../adventofcode
../../adventofcode,
sequtils,
strformat,
strutils
let input = adventofcode.getInput(2021, 1)
let lines = input.splitLines().mapIt(it.parseInt())
let lines = input.splitLines()[0 .. ^2].mapIt(it.parseInt())
var depths = 0
var prevResult = lines[0]

View File

@ -1,13 +1,11 @@
import
std/sequtils,
std/strformat,
std/strutils
import
../../adventofcode
../../adventofcode,
sequtils,
strformat,
strutils
let input = adventofcode.getInput(2021, 1)
let lines = input.splitLines().mapIt(it.parseInt())
let lines = input.splitLines()[0 .. ^2].mapIt(it.parseInt())
var depths = 0
var prevSum = lines[0] + lines[1] + lines[2]

View File

@ -1,83 +0,0 @@
import
std/strformat,
std/strutils,
std/tables
when defined(second):
import std/algorithm
import
../../adventofcode
let input = adventofcode.getInput(2021, 10)
let lines = input.splitLines()
const chunkSymbols = (
open: {
'(': ')',
'[': ']',
'{': '}',
'<': '>'
}.toTable(),
close: {
')': '(',
']': '[',
'}': '{',
'>': '<'
}.toTable()
)
when not defined(second):
const chunkScores = {
')': 3,
']': 57,
'}': 1197,
'>': 25137
}.toTable()
else:
const chunkScores = {
')': 1,
']': 2,
'}': 3,
'>': 4
}.toTable()
var score = 0
when defined(second):
var lineScores: seq[int]
for line in lines:
var chunks: seq[char]
block abortLine:
for it in line:
if chunkSymbols.open.hasKey it:
chunks.add it
if chunkSymbols.close.hasKey it:
let expectedSymbol = chunkSymbols.open[chunks[^1]]
if it == expectedSymbol:
discard chunks.pop()
else:
# Line is corrupted.
when not defined(second):
score += chunkScores[it]
break abortLine
# Complete line by closing chunks
when defined(second):
var lineScore = 0
for idx in 0 .. chunks.high():
let it = chunks[chunks.high() - idx]
let closeSym = chunkSymbols.open[it]
lineScore *= 5
lineScore += chunkScores[closeSym]
lineScores.add lineScore
when defined(second):
lineScores.sort()
score = lineScores[lineScores.len() div 2]
echo fmt"Answer: {score}"

View File

@ -1,67 +0,0 @@
import
std/sequtils,
std/strformat,
std/strutils
import
../../adventofcode
let input = adventofcode.getInput(2021, 11)
var grid: seq[seq[int]] = block:
input.splitLines().mapIt(it.mapIt(parseInt $it))
var steps = 0
var flashes = 0
proc inc(row, col: int)
proc flash(row, col: int)
proc inc(row, col: int) =
try:
grid[row][col] += 1
if grid[row][col] == 10:
flash(row, col)
except IndexDefect: return
proc flash(row, col: int) =
flashes += 1
inc(row, col - 1)
inc(row, col + 1)
inc(row - 1, col)
inc(row + 1, col)
inc(row - 1, col - 1)
inc(row + 1, col + 1)
inc(row - 1, col + 1)
inc(row + 1, col - 1)
proc resetFlashes() =
for row in 0 .. grid.high():
for col in 0 .. grid[row].high():
if grid[row][col] > 9:
grid[row][col] = 0
when defined(second):
proc areTheyAllFlashing(): bool =
result = true
for row in 0 .. grid.high():
for col in 0 .. grid[row].high():
if grid[row][col] != 0:
return false
proc forwardStep() =
steps += 1
for row in 0 .. grid.high():
for col in 0 .. grid[row].high():
inc(row, col)
resetFlashes()
when not defined(second):
while steps < 100:
forwardStep()
echo fmt"Answer: {flashes}"
else:
while not areTheyAllFlashing():
forwardStep()
echo fmt"Answer: {steps}"

22
2021/2/first.nim Normal file
View File

@ -0,0 +1,22 @@
import
../../adventofcode,
strutils,
strformat
let input = adventofcode.getInput(2021, 2)
let lines = input.splitLines()[0 .. ^2]
var horizontalPosition = 0
var depth = 0
for line in lines:
var command = line.split(" ")
case command[0]:
of "forward":
horizontalPosition += command[1].parseInt()
of "up":
depth -= command[1].parseInt()
of "down":
depth += command[1].parseInt()
echo fmt"Answer: {horizontalPosition * depth}"

24
2021/2/second.nim Normal file
View File

@ -0,0 +1,24 @@
import
../../adventofcode,
strutils,
strformat
let input = adventofcode.getInput(2021, 2)
let lines = input.splitLines()[0 .. ^2]
var horizontalPosition = 0
var depth = 0
var aim = 0
for line in lines:
var command = line.split(" ")
case command[0]:
of "forward":
horizontalPosition += command[1].parseInt()
depth += aim * command[1].parseInt()
of "up":
aim -= command[1].parseInt()
of "down":
aim += command[1].parseInt()
echo fmt"Answer: {horizontalPosition * depth}"

View File

@ -1,13 +1,11 @@
import
std/sequtils,
std/strformat,
std/strutils
import
../../adventofcode
../../adventofcode,
sequtils,
strutils,
strformat
let input = adventofcode.getInput(2021, 3)
let lines = input.splitLines()
let lines = input.splitLines()[0 .. ^2]
var gammaRateBinary = ""
var epsilonRateBinary = ""

View File

@ -1,13 +1,11 @@
import
std/sequtils,
std/strformat,
std/strutils
import
../../adventofcode
../../adventofcode,
sequtils,
strutils,
strformat
let input = adventofcode.getInput(2021, 3)
let lines = input.splitLines()
let lines = input.splitLines()[0 .. ^2]
var oxygenGenRateValues = lines
var co2ScrubberRateValues = lines

View File

@ -1,15 +1,15 @@
import
std/math,
std/sequtils,
std/strformat,
std/strutils
import
../../adventofcode,
./x/board
math,
sequtils,
strutils,
strformat
import
x/board
let input = adventofcode.getInput(2021, 4)
let lines = input.splitLines()
let lines = input.splitLines()[0 .. ^2]
let drawn = lines[0].split(",").mapIt(it.parseInt())
var currentlyDrawn: seq[int] = @[]

View File

@ -1,15 +1,15 @@
import
std/math,
std/sequtils,
std/strformat,
std/strutils
import
../../adventofcode,
./x/board
math,
sequtils,
strutils,
strformat
import
x/board
let input = adventofcode.getInput(2021, 4)
let lines = input.splitLines()
let lines = input.splitLines()[0 .. ^2]
let drawn = lines[0].split(",").mapIt(it.parseInt())
var currentlyDrawn: seq[int] = @[]

View File

@ -1,18 +1,16 @@
import
std/hashes,
std/sequtils,
std/strformat,
std/strutils,
std/tables
import
../../adventofcode
../../adventofcode,
hashes,
sequtils,
strutils,
strformat,
tables
type
Grid = TableRef[Point, int]
HydrothermalVent = object
x1, y1, x2, y2: int
x1, x2, y1, y2: int
Point = object
x, y: int
@ -24,10 +22,10 @@ B = b >= 0 ? 2 * b : -2 * b - 1;
A >= B ? A * A + A + B : A + B * B;
]#
proc hash(p: Point): Hash =
let A = block:
let A =
if p.x >= 0: 2 * p.x
else: -2 * p.x - 1
let B = block:
let B =
if p.y >= 0: 2 * p.y
else: -2 * p.y - 1
return
@ -35,10 +33,11 @@ proc hash(p: Point): Hash =
else: A + B * B
proc getPoints(v: HydrothermalVent): seq[Point] =
let xOffset = v.x2 - v.x1
let yOffset = v.y2 - v.y1
let xd = xOffset.abs()
let yd = yOffset.abs()
let xd = max(v.x1, v.x2) - min(v.x1, v.x2)
let yd = max(v.y1, v.y2) - min(v.y1, v.y2)
when defined(second):
let xoffset = v.x2 - v.x1
let yoffset = v.y2 - v.y1
if xd != 0 and v.y1 == v.y2:
for i in 0 .. xd:
@ -50,25 +49,33 @@ proc getPoints(v: HydrothermalVent): seq[Point] =
else:
when defined(second):
assert xd == yd
for i in 0 .. xd:
result.add Point(
x:
if xOffset > 0: min(v.x1, v.x2) + i
else: max(v.x1, v.x2) - i,
y:
if yOffset > 0: min(v.y1, v.y2) + i
else: max(v.y1, v.y2) - i
)
# TODO:
if xoffset < 0:
for i in 0 .. xd:
result.add Point(
x: min(v.x1, v.x2) + i,
y: max(v.y1, v.y2) - i
)
else:
for i in 0 .. yd:
result.add Point(
x: max(v.x1, v.x2) + i,
y: min(v.y1, v.y2) + i
)
proc parseInput(input: string): seq[HydrothermalVent] =
for line in input.splitLines():
for line in input.splitLines()[0 .. ^2]:
let coords = line.split(" -> ").mapIt(it.split(",").mapIt(it.parseInt()))
result.add HydrothermalVent(
x1: coords[0][0], y1: coords[0][1], x2: coords[1][0], y2: coords[1][1]
)
static:
let dummyVent = HydrothermalVent(x1: 9, y1: 7, x2: 7, y2: 9)
for point in dummyVent.getPoints():
echo $point
let input = adventofcode.getInput(2021, 5)
let vents = input.parseInput()
let grid = Grid()

View File

@ -1,38 +0,0 @@
import
std/strformat,
std/strutils
when defined(second):
import
std/algorithm,
std/math
import
../../adventofcode
let input = adventofcode.getInput()
when not defined(second):
var mostCalories = 0
else:
var elveCalories: seq[int]
let inventories = input.split("\L\L")
for inventory in inventories:
var calories = 0
let items = inventory.split("\L")
for item in items:
calories += item.parseInt()
when not defined(second):
mostCalories = max(mostCalories, calories)
else:
elveCalories.add calories
when not defined(second):
echo fmt"Answer: {mostCalories}"
else:
elveCalories.sort(order=Descending)
let totalCalories = sum elveCalories[0 ..< 3]
echo fmt"Answer: {totalCalories}"

View File

@ -1,43 +0,0 @@
import
std/strformat,
std/strutils,
std/tables
import
../../adventofcode
let input = adventofcode.getInput()
var score = 0
type
Choices = enum
rock, paper, scissors
let choices = toTable({
'A': rock,
'B': paper,
'C': scissors,
'X': rock,
'Y': paper,
'Z': scissors,
})
for line in input.splitLines:
let opponentChoice = choices[line[0]]
let ourChoice = choices[line[2]]
# The choice we make is added to the score
score += 1 + ord ourChoice
let outcome = ord(ourChoice) - ord(opponentChoice)
case outcome
of -2: score += 6
of -1: score += 0
of 0: score += 3
of 1: score += 6
of 2: score += 0
else:
raise newException(ValueError, fmt"Outcome: {outcome}")
echo fmt"Answer: {score}"

View File

@ -1,60 +0,0 @@
import
std/strformat,
std/strutils,
std/tables
import
../../adventofcode
let input = adventofcode.getInput()
var score = 0
type
Choice = enum
cRock = 'A',
cPaper = 'B',
cScissors = 'C'
RequiredOutcome = enum
roDefeat = 'X',
roDraw = 'Y',
roVictory = 'Z'
let choiceScores = toTable({
cRock: 1,
cPaper: 2,
cScissors: 3,
})
for line in input.splitLines:
let opponentChoice = Choice line[0]
let requiredOutcome = RequiredOutcome line[2]
var ourChoice: Choice
case requiredOutcome
of roDefeat:
score += 0
case opponentChoice
of cRock:
ourChoice = cScissors
of cPaper:
ourChoice = cRock
of cScissors:
ourChoice = cPaper
of roDraw:
score += 3
ourChoice = opponentChoice
of roVictory:
score += 6
case opponentChoice
of cRock:
ourChoice = cPaper
of cPaper:
ourChoice = cScissors
of cScissors:
ourChoice = cRock
# The choice we make is added to the score
score += choiceScores[ourChoice]
echo fmt"Answer: {score}"

View File

@ -1,50 +0,0 @@
import
std/sequtils,
std/strformat,
std/strutils
import
../../adventofcode
let input = adventofcode.getInput()
var answer = 0
let itemTypePriorities = block:
toSeq "-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
when not defined(second):
for rucksack in input.splitLines:
let items = toSeq rucksack
let cmp1 = items[0 ..< items.len() div 2]
let cmp2 = items[items.len() div 2 .. ^1]
for item in cmp1:
if item in cmp2:
answer += itemTypePriorities.find item
break
else:
let inputLines = input.splitLines()
iterator groups*(): tuple[
itemList1: seq[char],
itemList2: seq[char],
itemList3: seq[char]
] =
var i = 0
while i < inputLines.len():
yield (
toSeq inputLines[i],
toSeq inputLines[i+1],
toSeq inputLines[i+2]
)
i += 3
for rucksack1, rucksack2, rucksack3 in groups():
for item in rucksack1:
if item notin rucksack2: continue
if item notin rucksack3: continue
answer += itemTypePriorities.find item
break
echo fmt"Answer: {answer}"

View File

@ -1,41 +0,0 @@
import
std/strformat,
std/strutils
import
../../adventofcode
let input = adventofcode.getInput()
var score = 0
for pair in input.splitLines:
let sepIdx = pair.find(',')
assert sepIdx != -1
let range1Str = pair[0 ..< sepIdx]
let range2Str = pair[sepIdx + 1 .. ^1]
var range1: set[0 .. 65535]
var range2: set[0 .. 65535]
for i in 0 .. 1:
let rangeStr = block:
if i == 0: range1Str
else: range2Str
let rangeStrSepIdx = rangeStr.find('-')
assert rangeStrSepIdx != -1
let rangeStart = parseInt rangeStr[0 ..< rangeStrSepIdx]
let rangeEnd = parseInt rangeStr[rangeStrSepIdx + 1 .. ^1]
if i == 0: range1 = {rangeStart .. rangeEnd}
else: range2 = {rangeStart .. rangeEnd}
var total = range1 + range2
when not defined(second):
if range1.len == total.len or range2.len == total.len:
score += 1
else:
if total.len != (range1.len + range2.len):
score += 1
echo fmt"Answer: {score}"

View File

@ -1,84 +0,0 @@
import
std/algorithm,
std/sequtils,
std/strformat,
std/strutils
import
../../adventofcode
let input = adventofcode.getInput()
let lines = input.splitLines()
var stacks = newSeq[seq[char]]()
let sepIdx = lines.find("")
assert sepIdx != -1
let drawInstructions = lines[0 ..< sepIdx]
let moveInstructions = lines[sepIdx + 1 .. ^1]
when not defined(release):
proc echoRepr(stacks: seq[seq[char]]) =
let m = max stacks.mapIt(high it)
for i in countdown(m, 0):
echo join(stacks.map(proc(it: seq[char]): string =
if it.high < i: return " "
return fmt"[{it[i]}]"
), sep=" ")
echo " " & join(toSeq {1 .. stacks.high() + 1}, " ") & " "
for line in reversed drawInstructions:
if line.startsWith(" 1 "):
for n in line.splitWhitespace().mapIt(parseInt it):
assert n - 1 == len stacks
stacks.add(@[])
else:
var containers = newSeq[string]()
for i in countup(0, line.len - 2, 4):
containers.add line[i ..< i + 3]
for i in 0 ..< containers.len():
let cnt = containers[i]
if cnt[1] == ' ': continue
stacks[i].add cnt[1]
when not defined(release) and defined(stepByStep):
echoRepr stacks
echo ""
for line in moveInstructions.mapIt(it.splitWhitespace()):
when not defined(release) and defined(stepByStep):
echo line
echo ""
let howMany = parseInt line[1]
var srcStack = parseInt line[3]
srcStack.dec()
var destStack = parseInt line[5]
destStack.dec()
assert howMany <= len stacks[srcStack]
let sliceStart = stacks[srcStack].len() - howMany
let sliceEnd = stacks[srcStack].len() - 1
let movedItems = block:
when not defined(second):
reversed stacks[srcStack][sliceStart .. sliceEnd]
else:
stacks[srcStack][sliceStart .. sliceEnd]
stacks[srcStack].delete(sliceStart .. sliceEnd)
stacks[destStack] &= movedItems
when not defined(release) and defined(stepByStep):
echoRepr stacks
echo ""
echo "Press ENTER to continue."
discard stdin.readChar()
echo ""
when not defined(release):
echoRepr stacks
echo ""
let answer = join(stacks.mapIt it[high it])
echo fmt "Answer: {answer}"

View File

@ -1,35 +0,0 @@
import
std/strformat
import
../../adventofcode
let input = adventofcode.getInput()
const markerLen = block:
when not defined(second):
4
else:
14
var queue = newSeq[char](markerLen)
var answer: int
block process:
for i in 0 ..< len input:
let dup = input[i] in queue
discard queue.pop()
queue.insert(input[i], 0)
assert markerLen == len queue
if not dup and i >= markerLen:
block check:
for j in 0 ..< len queue:
for k in 0 ..< len queue:
if j == k: continue
if queue[k] == queue[j]:
break check
answer = i + 1
break process
echo fmt"Answer: {answer}"

View File

@ -1,105 +0,0 @@
import
std/strutils,
std/strformat,
std/tables
import
../../adventofcode
proc `/`(base, tail: string): string =
if tail == "..":
let sep = base.rfind('/')
if sep > 0: return base[0 ..< sep]
else: return "/"
else:
if base[^1] == '/':
return base & tail
else:
return base & "/" & tail
type
PseudoFileKind = enum
pfFile, pfDir
PseudoFile = ref object
case kind: PseudoFileKind
of pfFile:
size: Natural
of pfDir:
fileNames: seq[string]
proc newPseudoFile(size: Natural = 0): PseudoFile =
result = PseudoFile(kind: pfFile, size: size)
proc newPseudoDir(): PseudoFile =
result = PseudoFile(kind: pfDir)
let input = adventofcode.getInput()
var files = initTable[string, PseudoFile]()
var dirSizes = initTable[string, Natural]()
var cwd = "/"
var cwdObj = newPseudoDir()
files["/"] = cwdObj
proc computeSize(path: string): Natural =
if not files.hasKey path: return
let file = files[path]
case file.kind
of pfFile: return file.size
of pfDir:
if dirSizes.hasKey path:
return dirSizes[path]
for name in file.fileNames:
result += computeSize(path / name)
dirSizes[path] = result
for line in input.splitLines:
let args = line.splitWhitespace()
if args[0] == "$":
if args[1] == "cd":
if args[2][0] == '/':
cwd = args[2]
else:
cwd = cwd / args[2]
when not defined(release):
echo fmt"{cwd = }"
if files.hasKey cwd:
cwdObj = files[cwd]
else:
cwdObj = newPseudoDir()
files[cwd] = cwdObj
else:
if args[1] notin cwdObj.fileNames:
cwdObj.fileNames.add args[1]
if args[0] == "dir":
let dir = newPseudoDir()
files[cwd / args[1]] = dir
else:
let file = newPseudoFile(size=parseInt args[0])
files[cwd / args[1]] = file
when not defined(second):
var sum = 0
discard computeSize "/"
for path, dirSize in dirSizes:
if dirSize < 100_000:
sum += dirSize
echo fmt"Answer: {sum}"
else:
import
std/algorithm,
std/sequtils
const maxFsSize = 70_000_000
const updateSize = 30_000_000
let currFsSize = computeSize "/"
let requiredSize = currFsSize - (maxFsSize - updateSize)
when not defined(release):
echo fmt"{requiredSize = }"
let dirSizesSeq = sorted toSeq dirSizes.values()
for size in dirSizesSeq:
if size > requiredSize:
echo fmt"Answer: {size}"
break

View File

@ -1,65 +0,0 @@
import
std/strformat,
std/strutils
import
../../adventofcode
type
Map = seq[seq[int]]
proc `[]`(m: Map; x, y: Natural): int =
result = m[y][x]
let input = adventofcode.getInput()
var map: seq[seq[int]] = @[]
let lines = input.splitLines()
for y in 0 ..< lines.len():
map.add(@[])
for x in 0 ..< lines[y].len():
map[y].add parseInt($lines[y][x])
var visibleTrees = 0
for y in 0 ..< map.len():
for x in 0 ..< map[y].len():
if (x == 0) or (y == 0) or (x == map[y].high()) or (y == map.high()):
visibleTrees.inc()
continue
let height = map[x, y]
block check:
block left:
for x2 in countdown(x - 1, 0):
let h = map[x2, y]
if h >= height:
break left
visibleTrees.inc()
break check
block right:
for x3 in countup(x + 1, high map[y]):
let h = map[x3, y]
if h >= height:
break right
visibleTrees.inc()
break check
block top:
for y2 in countdown(y - 1, 0):
let h = map[x, y2]
if h >= height:
break top
visibleTrees.inc()
break check
block bottom:
for y3 in countup(y + 1, high map):
let h = map[x, y3]
if h >= height:
break bottom
visibleTrees.inc()
break check
echo fmt"Answer: {visibleTrees}"

View File

@ -1,63 +0,0 @@
import
std/strformat,
std/strutils
import
../../adventofcode
type
Map = seq[seq[int]]
proc `[]`(m: Map; x, y: Natural): int =
result = m[y][x]
let input = adventofcode.getInput()
var map: seq[seq[int]] = @[]
let lines = input.splitLines()
for y in 0 ..< lines.len():
map.add(@[])
for x in 0 ..< lines[y].len():
map[y].add parseInt($lines[y][x])
var highestScore: int = 0
for y in 0 ..< map.len():
for x in 0 ..< map[y].len():
if (x == 0) or (y == 0) or (x == map[y].high()) or (y == map.high()):
continue
let height = map[x, y]
var leftScore = 0
for x2 in countdown(x - 1, 0):
let h = map[x2, y]
leftScore.inc()
if h >= height:
break
var rightScore = 0
for x3 in countup(x + 1, high map[y]):
let h = map[x3, y]
rightScore.inc()
if h >= height:
break
var topScore = 0
for y2 in countdown(y - 1, 0):
let h = map[x, y2]
topScore.inc()
if h >= height:
break
var bottomScore = 0
for y3 in countup(y + 1, high map):
let h = map[x, y3]
bottomScore.inc()
if h >= height:
break
var score = leftScore * rightScore * topScore * bottomScore
if score > highestScore:
highestScore = score
echo fmt"Answer: {highestScore}"

View File

@ -1,49 +0,0 @@
import
std/strformat,
std/strutils
import
../../adventofcode
type
Position = tuple[x, y: int]
let input = adventofcode.getInput()
var knots: seq[Position] = @[]
const knotCount =
when not defined(second): 2
else: 10
for i in 0 ..< knotCount:
knots.add (0, 0)
var visitedByLastKnot: seq[Position] = @[(0, 0)]
for line in input.splitLines():
let direction = line[0]
let moves = parseInt line[2 .. ^1]
for i in 0 ..< moves:
case direction
of 'L': knots[0].x -= 1
of 'R': knots[0].x += 1
of 'U': knots[0].y -= 1
of 'D': knots[0].y += 1
else: discard
for i in 1 ..< len knots:
let dx = knots[i - 1].x - knots[i].x
let dy = knots[i - 1].y - knots[i].y
let tension = abs(dx) > 1 or abs(dy) > 1
if tension:
if abs(dx) > 0:
let moveKnotX = if dx > 0: 1 else: -1
knots[i].x += moveKnotX
if abs(dy) > 0:
let moveKnotY = if dy > 0: 1 else: -1
knots[i].y += moveKnotY
if i == high knots:
if knots[i] notin visitedByLastKnot:
visitedByLastKnot.add knots[i]
echo fmt"Answer: {len visitedByLastKnot}"

View File

@ -1,36 +0,0 @@
import
std/strformat,
std/strutils
import
../../adventofcode
type
Position = tuple[x, y: int]
let input = adventofcode.getInput()
var lastHead: Position = (0, 0)
var head: Position = (0, 0)
var tail: Position = (0, 0)
var visited: seq[Position] = @[(0, 0)]
for line in input.splitLines():
let direction = line[0]
let moves = parseInt line[2 .. ^1]
for i in 0 ..< moves:
lastHead = head
case direction
of 'L': head.x -= 1
of 'R': head.x += 1
of 'U': head.y -= 1
of 'D': head.y += 1
else: discard
var tension = abs(head.x - tail.x) > 1 or abs(head.y - tail.y) > 1
if tension:
tail = lastHead
if tail notin visited:
visited.add tail
echo fmt"Answer: {len visited}"

View File

@ -1,77 +0,0 @@
import
std/strutils
import
../../adventofcode
when not defined(second):
import
std/strformat
let input = adventofcode.getInput()
let instructions = input.splitLines()
var registerX = 1
var currOpIdx = 0
var currOp = ""
var currOpArg = 0
var sameOpN = 0
proc endOp() =
reset currOp
reset currOpArg
reset sameOpN
currOpIdx.inc()
when not defined(second):
const rangeEnd = 220
var answer = 0
else:
const rangeEnd = high(int)
var crtOutput = ""
for n in 1 .. rangeEnd:
when not defined(second):
if (n + 20) mod 40 == 0:
when not defined(release):
echo fmt"{ n = }, { registerX = }"
answer += n * registerX
else:
# Because the cycle count `n` starts at 1
let a = n - 1
# Only generate a 40x6 image
if a < 40 * 6:
if a mod 40 == 0:
crtOutput.add '\n'
if a mod 40 in registerX - 1 .. registerX + 1:
crtOutput.add '#'
else:
crtOutput.add '.'
if currOp == "":
if currOpIdx > high instructions:
break
let opArgs = splitWhitespace instructions[currOpIdx]
currOp = opArgs[0]
if opArgs.len() == 2:
currOpArg = parseInt opArgs[1]
case currOp
of "addx":
if sameOpN == 1:
registerX += currOpArg
endOp()
of "noop":
endOp()
else:
discard
if currOp != "":
sameOpN.inc()
when not defined(second):
echo fmt"Answer: {answer}"
else:
echo crtOutput

View File

@ -1,39 +0,0 @@
import
std/strformat,
std/strutils
import
../../adventofcode
let input = adventofcode.getInput()
var sum = 0
for line in input.splitLines():
var digitsInLine: seq[char] = @[]
for i in 0 .. line.high:
if line[i].isDigit():
digitsInLine.add line[i]
when defined(second):
# We can't use multiReplace() as numbers can overlap
# e.g.: eighthree, twone, ...
proc matches(pattern: string): bool =
if i + pattern.high <= line.high:
return line[i .. i + pattern.high] == pattern
for (a, b) in [
("one", '1'),
("two", '2'),
("three", '3'),
("four", '4'),
("five", '5'),
("six", '6'),
("seven", '7'),
("eight", '8'),
("nine", '9'),
]:
if matches a:
digitsInLine.add b
# Take the first and the last digits of the current line
sum += parseInt(digitsInLine[0] & digitsInLine[^1])
echo fmt"Answer: {sum}"

View File

@ -1,39 +0,0 @@
import
std/strformat,
std/strutils
import
../../adventofcode
const maxRedCubes = 12
const maxGreenCubes = 13
const maxBlueCubes = 14
let input = adventofcode.getInput()
var sum = 0
for line in input.splitLines():
let colonIdx = line.find(':')
let gameID = parseInt line[len("Game ") ..< colonIdx]
let gameSets = line[colonIdx + 2 .. ^1].split("; ")
block verifyGameSet:
for gameSet in gameSets:
for cubeTypeAndCount in gameSet.split(", "):
let cubeCountAndTypeSplit = cubeTypeAndCount.split(' ')
let cubeCount = parseInt cubeCountAndTypeSplit[0]
let cubeType = cubeCountAndTypeSplit[1]
case cubeType:
of "red":
if cubeCount > maxRedCubes:
break verifyGameSet
of "green":
if cubeCount > maxGreenCubes:
break verifyGameSet
of "blue":
if cubeCount > maxBlueCubes:
break verifyGameSet
else:
discard
sum += gameID
echo fmt"Answer: {sum}"

View File

@ -1,44 +0,0 @@
import
std/sequtils,
std/strformat,
std/strutils
import
../../adventofcode
let input = adventofcode.getInput()
var sum = 0
var schematic: seq[seq[char]] = input.splitLines().mapIt(toSeq it)
for y in 0 .. schematic.high():
var numStr = ""
for x in 0 .. schematic[y].high():
block wholeChara:
let chara = schematic[y][x]
if not chara.isDigit():
numStr.reset()
continue
numStr &= chara
if x == schematic[y].high() or not schematic[y][x + 1].isDigit():
# x = end index
var adjacentPositions: seq[(int, int)] = @[]
for i in 0 .. numStr.high():
# up, down
adjacentPositions.add (x - numStr.high() + i, y - 1)
adjacentPositions.add (x - numStr.high() + i, y + 1)
for dy in -1 .. 1:
# left, right
adjacentPositions.add (x - numStr.high() - 1, y + dy)
adjacentPositions.add (x + 1, y + dy)
for pos in adjacentPositions:
if pos[0] < 0 or pos[0] > schematic[y].high(): continue
if pos[1] < 0 or pos[1] > schematic.high(): continue
let charaToCheck = schematic[pos[1]][pos[0]]
if not charaToCheck.isDigit() and charaToCheck != '.':
sum += numStr.parseInt()
break wholeChara
echo fmt"Answer: {sum}"

View File

@ -1,54 +0,0 @@
import
std/math,
std/sequtils,
std/strformat,
std/strutils
import
../../adventofcode
let input = adventofcode.getInput()
when not defined(second):
var points = 0
else:
var additionalCards: seq[Natural] = @[]
let lines = input.splitLines()
for i in 0 .. lines.high():
let line = lines[i]
when defined(second):
if additionalCards.high() < i:
additionalCards.add 0
let instances = 1 + additionalCards[i]
let winningNumsStart = line.find(':') + 1
let winningNumsEnd = line.find('|')
let winningNums = line[winningNumsStart ..< winningNumsEnd]
.split(' ')
.filterIt(it != "")
.mapIt(parseInt it)
let elfNums = line[winningNumsEnd + 1 .. ^1]
.split(' ')
.filterIt(it != "")
.mapIt(parseInt it)
var duplicata = 0
for n in elfNums:
if n in winningNums:
duplicata.inc()
when not defined(second):
if duplicata > 0:
points += 2 ^ (duplicata - 1)
else:
for j in 1 .. duplicata:
if additionalCards.high() < i + j:
additionalCards.add 0
additionalCards[i + j] += instances
when not defined(second):
echo fmt"Answer: {points}"
else:
var originalCardCount = len(lines)
var additionalCardSum = sum(additionalCards)
echo fmt"Answer: {originalCardCount + additionalCardSum}"

View File

@ -1,63 +1,24 @@
#[
This file works by doing a lot of assumptions
about the project folder structure.
For instance, it assumes the executable
directory to be something in a "YYYY/DD"
format.
It also assumes the session key to be
available at the project root.
(in other words "../../session.key")
]#
import
std/httpclient,
std/os,
std/strformat,
std/strutils
when isMainModule:
stderr.writeLine "adventofcode> This can not be ran directly."
quit 1
let sessionKeyPath = os.getAppDir() / ".." / ".." / "session.key"
if not fileExists sessionKeyPath:
stderr.writeLine "adventofcode> secret.key could not be found."
let sessionKey = readFile sessionKeyPath
if sessionKey[0 ..< 10] == "\x00GITCRYPT\x00":
stderr.writeLine "adventofcode> secret.key is encrypted."
quit 1
httpclient,
os,
strformat
proc getInput*(year, day: int): string =
## Automatically downloads the input from
## AdventOfCode, unless there is a local copy
## of the input as 'input.txt' in the app dir.
let inputPath = os.getAppDir() / "input.txt"
if fileExists inputPath:
echo "adventofcode> Proceeding with current input.txt file."
let input = readFile inputPath
return input.strip(chars = {'\n'})
else:
echo "adventofcode> Missing input.txt. Downloading..."
let session = os.getEnv("SESSION")
if session == "":
echo "Missing SESSION environment variable."
quit 1
proc getInput*(year, day: int): string =
try:
let input = readFile(getCurrentDir() / "input.txt")
return input
except IOError:
let client = newHttpClient()
client.headers = newHttpHeaders({ "Cookie": fmt"session={sessionKey}" })
client.headers = newHttpHeaders({ "Cookie": fmt"session={session}" })
let input = client.getContent fmt"https://adventofcode.com/{year}/day/{day}/input"
writeFile(inputPath, input)
writeFile(getCurrentDir() / "input.txt", input)
return input.strip(chars = {'\n'})
proc getInput*(): string =
## Tries to determine year and date of
## day by looking at the app dir path.
let day = block:
let p = os.getAppDir().splitPath()
p.tail.parseInt()
let year = block:
let p = os.getAppDir().parentDir().splitPath()
p.tail.parseInt()
result = getInput(year, day)
return input

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2021 Danny Harpigny
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1 +0,0 @@
Rename this file to `session.key` and put your session key in it.