From 4ee88bb3580425e3bb0d03c82196bb4d0173bb88 Mon Sep 17 00:00:00 2001 From: Jeremy Kaplan Date: Wed, 13 Dec 2023 22:15:35 -0800 Subject: [PATCH] 2023 Day 14 Part 2 --- 2023/day14.nim | 117 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 100 insertions(+), 17 deletions(-) diff --git a/2023/day14.nim b/2023/day14.nim index 7200cbe..59098f1 100644 --- a/2023/day14.nim +++ b/2023/day14.nim @@ -15,7 +15,7 @@ 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): seq[seq[T]] = +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): @@ -41,8 +41,6 @@ type Platform = object height: int width: int - grid: Table[Point, char] - rows: seq[seq[char]] cols: seq[seq[char]] @@ -59,10 +57,12 @@ func parsePlatform(text: string): Platform = for (r, line) in enumerate(lines): for (c, sym) in enumerate(line): - result.grid[(r, c)] = sym result.rows[r] &= @[sym] result.cols[c] &= @[sym] +func render(p: Platform): string = + p.rows.mapIt(it.mapIt($it).join("")).join("\n") + proc fromCols(cols: seq[seq[char]]): Platform = result.cols = cols result.width = cols.len @@ -73,36 +73,119 @@ proc fromCols(cols: seq[seq[char]]): Platform = for (c, col) in enumerate(cols): for (r, sym) in enumerate(col): - result.grid[(r, c)] = sym result.rows[r] &= @[sym] -func render(p: Platform): string = - p.rows.mapIt(it.mapIt($it).join("")).join("\n") +proc fromRows(rows: seq[seq[char]]): Platform = + result.rows = rows + result.height = rows.len + result.width = rows[0].len -proc tiltNorth(col: seq[char]): seq[char] = - let parts = col.split(func (sym: char): bool = sym == '#') + for c in 0 ..< result.width: + result.cols.add @[] + + for (r, row) in enumerate(rows): + for (c, sym) in enumerate(row): + result.cols[c] &= @[sym] + +func tilt(rocks: seq[char], order: SortOrder): seq[char] = + let parts = rocks.split(func (sym: char): bool = sym == '#') var rolled: seq[seq[char]] for part in parts: - rolled.add part.sorted(Descending) + rolled.add part.sorted(order) result = rolled.join('#') -proc tiltNorth(p: Platform): Platform = +func tiltCols(p: Platform, order: SortOrder): Platform = var cols: seq[seq[char]] for col in p.cols: - cols.add col.tiltNorth + cols.add col.tilt(order) fromCols(cols) +func tiltRows(p: Platform, order: SortOrder): Platform = + var rows: seq[seq[char]] + for row in p.rows: + rows.add row.tilt(order) + fromRows(rows) + +func tiltNorth(p: Platform): Platform = + p.tiltCols(Descending) + +func tiltSouth(p: Platform): Platform = + p.tiltCols(Ascending) + +func tiltWest(p: Platform): Platform = + p.tiltRows(Descending) + +func tiltEast(p: Platform): Platform = + p.tiltRows(Ascending) + +type Direction = enum + N + S + E + W + +func tilt(p: Platform, d: Direction): Platform = + case d: + of N: p.tiltNorth + of S: p.tiltSouth + of E: p.tiltEast + of W: p.tiltWest + proc loadNorth(platform: Platform): int = - for (p, sym) in platform.grid.pairs: - if sym != 'O': continue - result.inc (platform.height - p.r) + for (r, row) in enumerate(platform.rows): + for sym in row: + if sym != 'O': continue + result.inc (platform.height - r) proc part1(input: string): int = let text = readFile(input) let platform = parsePlatform(text) platform.tiltNorth.loadNorth +func cycle(p: Platform): Platform = + p.tiltNorth.tiltWest.tiltSouth.tiltEast + +type Loop = tuple + start: int + stop: int + length: int + +proc findLoop(p: Platform): (Loop, Platform) = + let cycle = @[N, W, S, E] + + var seen: Table[Platform, int] + seen[p] = 0 + + var p = p + var i = 0 + while true: + inc i + + for dir in cycle: + p = p.tilt(dir) + + if p in seen: + let start = seen[p] + return ((start, i, i - start), p) + seen[p] = i + +proc part2(input: string): int = + let text = readFile(input) + let platform = parsePlatform(text) + let cycles = 1_000_000_000 + + let (loop, state) = platform.findLoop + + let repeats = (cycles - loop.stop).floorDiv(loop.length) + let timeSkip = loop.stop + repeats * loop.length + let remaining = cycles - timeSkip + + var p = state + for _ in 0 ..< remaining: + p = p.cycle + p.loadNorth + echo part1("input/test.txt") echo part1("input/day14.txt") -# echo part2("input/test.txt") -# echo part2("input/day14.txt") +echo part2("input/test.txt") +echo part2("input/day14.txt")