151 lines
3.3 KiB
Nim
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"))
|