This commit is contained in:
parent
3cb6474521
commit
7e06788d8d
4
.git-crypt/.gitattributes
vendored
Normal file
4
.git-crypt/.gitattributes
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Do not edit this file. To specify the files to encrypt, create your own
|
||||||
|
# .gitattributes file in the directory where your files are.
|
||||||
|
* !filter !diff
|
||||||
|
*.gpg binary
|
Binary file not shown.
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
session.key filter=git-crypt diff=git-crypt
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -1,2 +1,7 @@
|
||||||
|
# binaries
|
||||||
|
**/first
|
||||||
|
**/second
|
||||||
|
**/both
|
||||||
|
|
||||||
# input files
|
# input files
|
||||||
input.txt
|
**/input.txt
|
||||||
|
|
38
2022/01/both.nim
Normal file
38
2022/01/both.nim
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
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}"
|
43
2022/02/first.nim
Normal file
43
2022/02/first.nim
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
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}"
|
60
2022/02/second.nim
Normal file
60
2022/02/second.nim
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
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}"
|
50
2022/03/both.nim
Normal file
50
2022/03/both.nim
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
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}"
|
41
2022/04/both.nim
Normal file
41
2022/04/both.nim
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
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}"
|
||||||
|
|
84
2022/05/both.nim
Normal file
84
2022/05/both.nim
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
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}"
|
35
2022/06/both.nim
Normal file
35
2022/06/both.nim
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
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}"
|
|
@ -1,26 +1,63 @@
|
||||||
|
#[
|
||||||
|
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
|
import
|
||||||
std/httpclient,
|
std/httpclient,
|
||||||
std/os,
|
std/os,
|
||||||
std/strformat,
|
std/strformat,
|
||||||
std/strutils
|
std/strutils
|
||||||
|
|
||||||
proc getInput*(year, day: int): string =
|
when isMainModule:
|
||||||
try:
|
stderr.writeLine "adventofcode> This can not be ran directly."
|
||||||
let input = readFile(getCurrentDir() / "input.txt")
|
|
||||||
|
|
||||||
return input.strip(chars = {'\n'})
|
|
||||||
except IOError:
|
|
||||||
echo "adventofcode> Missing input.txt. Downloading..."
|
|
||||||
let session = os.getEnv("SESSION")
|
|
||||||
if session == "":
|
|
||||||
echo "adventofcode> Unable to download without the SESSION environment variable."
|
|
||||||
quit 1
|
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
|
||||||
|
|
||||||
|
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 client = newHttpClient()
|
let client = newHttpClient()
|
||||||
client.headers = newHttpHeaders({ "Cookie": fmt"session={session}" })
|
client.headers = newHttpHeaders({ "Cookie": fmt"session={sessionKey}" })
|
||||||
|
|
||||||
let input = client.getContent fmt"https://adventofcode.com/{year}/day/{day}/input"
|
let input = client.getContent fmt"https://adventofcode.com/{year}/day/{day}/input"
|
||||||
|
|
||||||
writeFile(getCurrentDir() / "input.txt", input)
|
writeFile(inputPath, input)
|
||||||
|
|
||||||
return input.strip(chars = {'\n'})
|
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)
|
||||||
|
|
BIN
session.key
Normal file
BIN
session.key
Normal file
Binary file not shown.
Loading…
Reference in a new issue