Skip to content

Instantly share code, notes, and snippets.

@zoldar
Created December 11, 2022 20:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zoldar/43f5f2c6f0fb9c62857cc903f22ea4cf to your computer and use it in GitHub Desktop.
Save zoldar/43f5f2c6f0fb9c62857cc903f22ea4cf to your computer and use it in GitHub Desktop.
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