Watch
1
0
Fork
You've already forked advent-of-code
0

2023 Day 10 Part 2

This commit is contained in:
Jeremy Kaplan 2023-12-10 22:15:49 -08:00
commit dff1225024

View file

@ -2,12 +2,14 @@ import std/algorithm
import std/deques
import std/enumerate
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
proc dbg[T](v: T): T = (echo(v); v)
@ -33,8 +35,11 @@ func parseSketch(text: string): Sketch =
if sym == 'S':
result.start = (r, c)
func inBounds(s: Sketch, p: Point): bool =
p.r in 0 ..< s.rows and p.c in 0 ..< s.cols
func pipeAt(s: Sketch, p: Point): char =
if p.r in 0 ..< s.rows and p.c in 0 ..< s.cols:
if s.inBounds(p):
s.grid[p]
else:
'.'
@ -65,6 +70,8 @@ func neighbors(s: Sketch, p: Point): seq[Point] =
(0, +1, E, W), # Right E-W
]:
let n = (p.r + dr, p.c + dc)
if not s.inBounds(n):
continue
if s.hasConn(p, pDir) and s.hasConn(n, nDir):
result.add(n)
@ -93,7 +100,190 @@ proc part1(): int =
let text = readFile("input/day10.txt")
let sketch = parseSketch(text)
for d in values(sketch.reachable):
let reachable = sketch.reachable
for d in values(reachable):
result = max(result, d)
proc path(s: Sketch): seq[Point] =
var queue: Deque[seq[Point]]
queue.addLast @[s.start]
while queue.len > 0:
let path = queue.popFirst
let point = path[^1]
if path.len > 1 and s.pipeAt(point) == 'S':
return path
for n in s.neighbors(point):
if path.len > 1 and path[^2] == n:
continue
queue.addLast(path & @[n])
func inBoundsPadding(s: Sketch, p: Point): bool =
p.r in -1 .. s.rows and p.c in -1 .. s.cols
func neighborsFill(s: Sketch, p: Point): seq[Point] =
for (dr, dc) in [
(-1, 0), # Up
(+1, 0), # Down
(0, -1), # Left
(0, +1), # Right
]:
let n = (p.r + dr, p.c + dc)
if s.inBoundsPadding(n):
result.add n
proc floodFill(s: Sketch, start: Point, path: HashSet[Point]): HashSet[Point] =
var queue: Deque[Point]
queue.addLast start
while queue.len > 0:
let state = queue.popFirst
if state in result:
continue
result.incl state
for n in s.neighborsFill(state):
if n in path:
continue
queue.addLast n
func cells(s: Sketch): HashSet[Point] =
for r in 0 ..< s.rows:
for c in 0 ..< s.cols:
result.incl (r, c)
proc heading(a: Point, b: Point): Direction =
let dr = b.r - a.r
let dc = b.c - a.c
assert(abs(dr) + abs(dc) == 1)
if dr == -1: return N
if dr == +1: return S
if dc == -1: return W
if dc == +1: return E
assert(false)
func north(p: Point): Point = (p.r - 1, p.c)
func south(p: Point): Point = (p.r + 1, p.c)
func east(p: Point): Point = (p.r , p.c + 1)
func west(p: Point): Point = (p.r , p.c - 1)
func northwest(p: Point): Point = (p.r - 1, p.c - 1)
func northeast(p: Point): Point = (p.r - 1, p.c + 1)
func southwest(p: Point): Point = (p.r + 1, p.c - 1)
func southeast(p: Point): Point = (p.r + 1, p.c + 1)
func sides3(p: Point, h: Direction): (seq[Point], seq[Point]) =
case h:
of N: (
@[p.northwest, p.west, p.southwest],
@[p.northeast, p.east, p.southeast],
)
of S: (
@[p.southeast, p.east, p.northeast],
@[p.southwest, p.west, p.northwest],
)
of E: (
@[p.northeast, p.north, p.northwest],
@[p.southeast, p.south, p.southwest],
)
of W: (
@[p.southwest, p.south, p.southeast],
@[p.northwest, p.north, p.northeast],
)
func sides2(p: Point, h: Direction): (seq[Point], seq[Point]) =
let (l, r) = sides3(p, h)
(l[0..^2], r[0..^2])
proc walkPath(s: Sketch, path: seq[Point]): (HashSet[Point], HashSet[Point]) =
for (a, b) in zip(path[0..^2], path[1..^1]):
let h = a.heading(b)
let (left, right) = sides2(a, h)
for p in left:
result[0].incl p
for p in right:
result[1].incl p
type Side = enum
Left
Right
func whichSide(p: Point, left: HashSet[Point], right: HashSet[Point]): Option[Side] =
if p in left and p in right:
return none(Side)
if p in left:
return some(Left)
if p in right:
return some(Right)
return none(Side)
proc part2(): int =
let text = readFile("input/day10.txt")
let sketch = parseSketch(text)
let path = sketch.path
assert sketch.pipeAt(path[0]) == 'S'
assert sketch.pipeAt(path[^1]) == 'S'
let (left, right) = sketch.walkPath(path)
var candidates = sketch.cells
let pathSet = path.toHashSet
for p in pathSet:
candidates.excl p
var foundOutsideSide = false
var outside: Side
for p in sketch.floodFill((-1, -1), pathSet):
candidates.excl p
if p in pathSet:
continue
# Marked both ways, inconclusve?
if p in left and p in right:
continue
if p in left:
if foundOutsideSide:
assert(outside == Left)
foundOutsideSide = true
outside = Left
elif p in right:
if foundOutsideSide:
assert(outside == Right)
foundOutsideSide = true
outside = Right
var inside: HashSet[Point]
for r in 0 ..< sketch.rows:
for c in 0 ..< sketch.cols:
let p = (r, c)
# Already known to be outside or on path
if p notin candidates:
continue
let s = whichSide(p, left, right)
if s.isSome and s.get == outside:
for pOut in sketch.floodFill(p, pathSet):
candidates.excl pOut
elif s.isSome and s.get != outside:
for pIn in sketch.floodFill(p, pathSet):
inside.incl pIn
else:
discard "Inconclusive! I hope this doesn't matter!"
assert(candidates.len == inside.len)
inside.len
echo part1()
echo part2()