Watch
1
0
Fork
You've already forked advent-of-code
0
advent-of-code/2023/day18.nim
2023-12-18 20:21:24 -08:00

151 lines
3.3 KiB
Nim

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 Direction = enum
N
W
S
E
func parseDirection(s: string): Direction =
case s:
of "U": N
of "D": S
of "L": W
of "R": E
else:
raise newException(ValueError, s)
type Instruction = tuple
dir: Direction
steps: int
color: int
func parseInstruction(line: string): Instruction =
var dir: string
if not scanf(line, "$+ $i (#$h)$.", dir, result.steps, result.color):
raise newException(ValueError, line)
result.dir = dir.parseDirection
func parseInstructions(text: string): seq[Instruction] =
text.strip.splitLines.map(parseInstruction)
type Point = tuple
r: int
c: int
func `$`(p: Point): string = "(" & $p.r & ", " & $p.c & ")"
func move(p: Point, d: Direction, n: int): Point =
case d:
of N: (p.r - n, p.c)
of S: (p.r + n, p.c)
of W: (p.r, p.c - n)
of E: (p.r, p.c + n)
func fix(i: Instruction): Instruction =
let steps = i.color shr 4
let dir = case i.color.masked 0xf
of 0: "R"
of 1: "D"
of 2: "L"
of 3: "U"
else: raise newException(ValueError, $i.color)
(dir.parseDirection, steps, i.color)
func `//`(a, b: int): int =
assert a mod b == 0
a.floorDiv(b)
func polygon(path: seq[Instruction]): seq[Point] =
var pos = (0, 0)
result.add pos
for i in path:
pos = pos.move(i.dir, i.steps)
result.add pos
proc area(path: seq[Point]): int =
for i in 0 ..< path.len - 1:
let a = path[i]
let b = path[(i+1) mod path.len]
let area = a.r * b.c - b.r * a.c
result.inc area
# echo a, " ", b, " = ", area, " => ", result
func axisAligned(a, b: Point): bool =
a.r == b.r or a.c == b.c
func distance(a, b: Point): int =
abs(b.r - a.r) + abs(b.c - a.c)
proc distance(path: seq[Point]): int =
for i in 0 ..< path.len - 1:
let a = path[i]
let b = path[(i+1) mod path.len]
assert axisAligned(a, b)
let len = distance(a, b)
result.inc len
func lagoonArea(ii: seq[Instruction]): int =
let path = polygon(ii)
(path.area // 2).abs + (path.distance // 2) + 1
proc part1(input: string): int =
let text = readFile(input)
let instructions = parseInstructions(text)
lagoonArea(instructions)
proc part2(input: string): int =
let text = readFile(input)
let instructions = parseInstructions(text)
lagoonArea(instructions.map(fix))
###################
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/day18.txt"))
echo timed(proc(): int = part2("input/test.txt"))
echo timed(proc(): int = part2("input/day18.txt"))