diff --git a/2023/day19.nim b/2023/day19.nim new file mode 100644 index 0000000..b639b60 --- /dev/null +++ b/2023/day19.nim @@ -0,0 +1,150 @@ +import std/algorithm +import std/bitops +import std/deques +import std/enumerate +import std/heapqueue +import std/math +import std/options +import std/sequtils +import std/sets +import std/strscans +import std/strutils +import std/sugar +import std/tables +import std/terminal +import std/times + +proc dbg[T](v: T): T = (echo(v); v) + +func empty[T](v: seq[T]): bool = v.len == 0 + +proc split[T](v: seq[T], pred: proc(elt: T): bool {.noSideEffect.}): seq[seq[T]] = + var current: seq[T] + for elt in v: + if pred(elt): + result.add current + current = @[] + else: + current.add elt + result.add current + +func join[T](parts: seq[seq[T]], sep: T): seq[T] = + if parts.len > 0: + result &= parts[0] + + for part in parts[1..^1]: + result.add sep + result &= part + +################### + +type Op = enum + None + Lt + Gt + +func parseOp(c: char): Op = + case c: + of '<': Lt + of '>': Gt + else: raise newException(ValueError, $c) + +type Step = tuple + attr: char + op: Op + val: int + dest: string + +func parseStep(text: string): Step = + var op: char + if scanf(text, "$c$c$i:$+$.", result.attr, op, result.val, result.dest): + result.op = op.parseOp + return + + if scanf(text, "$w$.", result.dest): + result.op = None + return + + raise newException(ValueError, text) + +type Workflow = tuple + name: string + steps: seq[Step] + +func parseWorkflow(text: string): Workflow = + var steps: string + if not scanf(text, "$+{$+}$.", result.name, steps): + raise newException(ValueError, text) + result.steps = steps.split(',').map(parseStep) + +type Part = tuple + attrs: Table[char, int] + +func parseAttr(text: string): (char, int) = + if not scanf(text, "$c=$i$.", result[0], result[1]): + raise newException(ValueError, text) + +func parseAttrs(text: string): Table[char, int] = + for (a, v) in text.split(',').map(parseAttr): + result[a] = v + +func parsePart(line: string): Part = + var attrs: string + if not scanf(line, "{$+}$.", attrs): + raise newException(ValueError, line) + result.attrs = attrs.parseAttrs + +type System = tuple + workflows: Table[string, Workflow] + parts: seq[Part] + +func parseSystem(text: string): System = + let sections = text.split("\n\n") + assert sections.len == 2 + + for w in sections[0].strip.splitLines.map(parseWorkflow): + result.workflows[w.name] = w + + result.parts = sections[1].strip.splitLines.map(parsePart) + +func apply(w: Workflow, p: Part): string = + for s in w.steps: + case s.op: + of None: + return s.dest + of Lt: + if p.attrs[s.attr] < s.val: + return s.dest + of Gt: + if p.attrs[s.attr] > s.val: + return s.dest + +func accepted(sys: System, part: Part): bool = + var w = "in" + while w != "R" and w != "A": + w = sys.workflows[w].apply(part) + w == "A" + +func rating(p: Part): int = + for v in p.attrs.values: + result.inc v + +proc part1(input: string): int = + let text = readFile(input) + let system = parseSystem(text) + + for part in system.parts: + if system.accepted(part): + result.inc part.rating + +################### + +proc timed[T](f: () -> T): T = + let start = cpuTime() + result = f() + echo "Time :", cpuTime() - start + +echo timed(proc(): int = part1("input/test.txt")) +echo timed(proc(): int = part1("input/day19.txt")) +# echo timed(proc(): int = part2("input/test.txt")) +# echo timed(proc(): int = part2("input/day19.txt"))