Created
December 11, 2022 20:24
-
-
Save zoldar/43f5f2c6f0fb9c62857cc903f22ea4cf to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import std/sugar, | |
std/deques, | |
std/algorithm, | |
std/sequtils, | |
std/strformat, | |
std/strscans, | |
std/strutils, | |
std/tables, | |
std/enumerate | |
type | |
Monkey = object | |
inspections: int | |
items: Deque[int] | |
worryOp: (old: int) -> int | |
divider: int | |
onTrue: int | |
onFalse: int | |
proc ParseError(index: int, line: string): ref Exception = | |
newException(Exception, fmt"Failed to parse rule {index} on line: {line}") | |
let opTable = { | |
'+': proc(a: int, b: int): int = a + b, | |
'-': proc(a: int, b: int): int = a - b, | |
'*': proc(a: int, b: int): int = a * b | |
}.toTable | |
let monkeyParser = [ | |
proc(monkeys: var seq[Monkey], line: string): void = | |
let (ok, _) = line.scanTuple("Monkey $i:") | |
if ok: monkeys.add(Monkey()) | |
else: raise ParseError(0, line), | |
proc(monkeys: var seq[Monkey], line: string): void = | |
let (ok, numbers) = line.scanTuple("$sStarting items: $+") | |
if ok: monkeys[^1].items = numbers.split(", ").mapIt(parseInt(it)).toDeque | |
else: raise ParseError(1, line), | |
proc(monkeys: var seq[Monkey], line: string): void = | |
let (ok, p1, op, p2) = line.scanTuple("$sOperation: new = $+ $c $+") | |
if ok: | |
let operator = opTable[op] | |
let opFun = proc(old: int): int = | |
if p1 == "old" and p2 == "old": | |
operator(old, old) | |
elif p1 == "old": | |
operator(old, parseInt(p2)) | |
elif p2 == "old": | |
operator(old, parseInt(p1)) | |
else: | |
raise ParseError(2, line) | |
monkeys[^1].worryOp = opFun | |
else: | |
raise ParseError(2, line), | |
proc(monkeys: var seq[Monkey], line: string): void = | |
let (ok, divisibleBy) = line.scanTuple("$sTest: divisible by $i") | |
if ok: | |
monkeys[^1].divider = divisibleBy | |
else: | |
raise ParseError(3, line), | |
proc(monkeys: var seq[Monkey], line: string): void = | |
let (ok, toMonkey) = line.scanTuple("$sIf true: throw to monkey $i") | |
if ok: monkeys[^1].onTrue = toMonkey | |
else: raise ParseError(4, line), | |
proc(monkeys: var seq[Monkey], line: string): void = | |
let (ok, toMonkey) = line.scanTuple("$sIf false: throw to monkey $i") | |
if ok: monkeys[^1].onFalse = toMonkey | |
else: raise ParseError(5, line), | |
proc(monkeys: var seq[Monkey], line: string): void = | |
if line == "": discard | |
else: raise ParseError(6, line) | |
] | |
# var monkeys = @[ | |
# Monkey( | |
# items: [79, 98].toDeque, | |
# worryOp: proc(old: int): int = old * 19, | |
# divider: 23, | |
# onTrue: 2, | |
# onFalse: 3 | |
# ), | |
# Monkey( | |
# items: [54, 65, 75, 74].toDeque, | |
# worryOp: proc(old: int): int = old + 6, | |
# divider: 19, | |
# onTrue: 2, | |
# onFalse: 0 | |
# ), | |
# Monkey( | |
# items: [79, 60, 97].toDeque, | |
# worryOp: proc(old: int): int = old * old, | |
# divider: 13, | |
# onTrue: 1, | |
# onFalse: 3 | |
# ), | |
# Monkey( | |
# items: [74].toDeque, | |
# worryOp: proc(old: int): int = old + 3, | |
# divider: 17, | |
# onTrue: 0, | |
# onFalse: 1 | |
# ) | |
# ] | |
proc loadMonkeys(file: string): seq[Monkey] = | |
result = @[] | |
for idx, line in enumerate(file.lines): | |
monkeyParser[idx mod monkeyParser.len](result, line) | |
proc runInspections(monkeys: seq[Monkey], rounds: int, inspectFun: (monkey: Monkey, item: int) -> int): seq[Monkey] = | |
result = monkeys | |
for round in 0..<rounds: | |
for monkey in result.mitems: | |
while monkey.items.len > 0: | |
let item = monkey.items.popFirst() | |
let worryLevel = monkey.inspectFun(item) | |
if worryLevel mod monkey.divider == 0: | |
result[monkey.onTrue].items.addLast(worryLevel) | |
else: | |
result[monkey.onFalse].items.addLast(worryLevel) | |
monkey.inspections.inc | |
proc monkeyBusiness(monkeys: seq[Monkey]): int = | |
var topInspectors = monkeys.sortedByIt(-1 * it.inspections)[0..1] | |
topInspectors[0].inspections * topInspectors[1].inspections | |
# loading and parsing | |
let monkeys = loadMonkeys("day-11/input.txt") | |
# part 1 | |
let monkeys20 = runInspections(monkeys, 20, (monkey, i) => monkey.worryOp(i) div 3) | |
echo fmt"Monkeybusiness level after 20 rounds: {monkeyBusiness(monkeys20)}" | |
# part 2 | |
let worryCap = monkeys.mapIt(it.divider).foldl(a * b) | |
let monkeys10k = runInspections(monkeys, 10_000, (monkey, i) => monkey.worryOp(i mod worryCap)) | |
echo fmt"Monkeybusiness level after 10_000 rounds: {monkeyBusiness(monkeys10k)}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment