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

refactor: 2023 Day 3

This commit is contained in:
Jeremy Kaplan 2023-12-02 23:04:13 -08:00
commit cfeaa92460

View file

@ -1,60 +1,18 @@
import std/sets import std/sets
import std/enumerate import std/enumerate
import std/tables import std/tables
import std/sequtils
import std/strutils import std/strutils
const digits = "0123456789" const digits = "0123456789"
type Coord = tuple func as_int(c: char): int = c.int - '0'.int
type Point = tuple
r: int r: int
c: int c: int
type Slice = tuple func neighbors(p: Point): seq[Point] =
lo: Coord
hi: Coord
type Grid = object
rows: int
cols: int
table: Table[Coord, char]
numbers: seq[Slice]
gears: seq[Coord]
proc parseGrid(text: string): Grid =
result.table = initTable[Coord, char]()
let lines = text.strip.splitLines()
result.rows = lines.len
result.cols = lines[0].len
for (r, line) in enumerate(lines):
var num: Slice
var in_num = false
for (c, sym) in enumerate(line):
result.table[(r, c)] = sym
if sym == '*':
result.gears.add((r, c))
if sym in digits:
if in_num:
num.hi = (r, c)
else:
num.lo = (r, c)
num.hi = (r, c)
in_num = true
continue
if in_num:
result.numbers.add(num)
in_num = false
if in_num:
result.numbers.add(num)
func neighbors(p: Coord): seq[Coord] =
for (dr, dc) in [ for (dr, dc) in [
(-1, -1), (-1, -1),
(-1, 0), (-1, 0),
@ -68,25 +26,89 @@ func neighbors(p: Coord): seq[Coord] =
]: ]:
result.add((p.r + dr, p.c + dc)) result.add((p.r + dr, p.c + dc))
proc grid_sym(grid: Grid, p: Coord): char = type Slice = tuple
if 0 <= p.r and p.r < grid.rows and 0 <= p.c and p.c < grid.cols: lo: Point
hi: Point
func row_range(s: Slice): seq[int] =
toSeq(s.lo.r .. s.hi.r)
func col_range(s: Slice): seq[int] =
toSeq(s.lo.c .. s.hi.c)
func contains(s: Slice, p: Point): bool =
p.r in s.row_range and p.c in s.col_range
type Part = tuple
id: int
slice: Slice
func contains(part: Part, p: Point): bool =
part.slice.contains(p)
type Grid = object
rows: int
cols: int
table: Table[Point, char]
parts: seq[Part]
gears: seq[Point]
func parseGrid(text: string): Grid =
result.table = initTable[Point, char]()
let lines = text.strip.splitLines()
result.rows = lines.len
result.cols = lines[0].len
for (r, line) in enumerate(lines):
var part: Part
var in_num = false
for (c, sym) in enumerate(line):
result.table[(r, c)] = sym
if sym == '*':
result.gears.add((r, c))
if sym in digits:
if in_num:
part.slice.hi = (r, c)
part.id = 10 * part.id + sym.as_int
else:
part.slice.lo = (r, c)
part.slice.hi = (r, c)
part.id = sym.as_int
in_num = true
continue
if in_num:
result.parts.add(part)
in_num = false
if in_num:
result.parts.add(part)
func symbol(grid: Grid, p: Point): char =
if p.r in 0 ..< grid.rows and p.c in 0 ..< grid.cols:
grid.table[p] grid.table[p]
else: else:
'.' '.'
proc grid_slice(grid: Grid, s: Slice): string = func text(grid: Grid, s: Slice): string =
for r in s.lo.r .. s.hi.r: assert(s.lo.r == s.hi.r)
for c in s.lo.c .. s.hi.c:
result.add(grid_sym(grid, (r, c)))
proc has_neighbor(grid: Grid, s: Slice): bool =
let r = s.lo.r let r = s.lo.r
for c in s.lo.c .. s.hi.c:
for c in s.col_range:
result.add(grid.symbol((r, c)))
func is_attached(part: Part, grid: Grid): bool =
let r = part.slice.lo.r
for c in part.slice.col_range:
for n in neighbors((r, c)): for n in neighbors((r, c)):
if n.r == r and s.lo.c <= n.c and n.c <= s.hi.c: if part.contains(n):
continue continue
let nsym = grid_sym(grid, n) let nsym = grid.symbol(n)
if nsym != '.': if nsym != '.':
return true return true
return false return false
@ -95,20 +117,20 @@ proc part1(): int =
let text = readFile("input/day03.txt") let text = readFile("input/day03.txt")
let grid = parseGrid(text) let grid = parseGrid(text)
for num in grid.numbers: for part in grid.parts:
assert(num.lo.r == num.hi.r) assert(part.slice.lo.r == part.slice.hi.r)
if grid.has_neighbor(num): if part.is_attached(grid):
result += grid_slice(grid, num).parseInt result += grid.text(part.slice).parseInt
proc get_containing(grid: Grid, p: Coord): Slice = func get_containing(grid: Grid, p: Point): Part =
for num in grid.numbers: for part in grid.parts:
if p.r == num.lo.r and num.lo.c <= p.c and p.c <= num.hi.c: if part.contains(p):
return num return part
proc gear_neighbors(grid: Grid, p: Coord): HashSet[Slice] = func gear_neighbors(grid: Grid, p: Point): HashSet[Part] =
for n in p.neighbors: for n in p.neighbors:
if grid.grid_sym(n)in digits: if grid.symbol(n) in digits:
result.incl(grid.get_containing(n)) result.incl(grid.get_containing(n))
proc part2(): int = proc part2(): int =
@ -116,13 +138,13 @@ proc part2(): int =
let grid = parseGrid(text) let grid = parseGrid(text)
for gear in grid.gears: for gear in grid.gears:
let nums = grid.gear_neighbors(gear) let parts = grid.gear_neighbors(gear)
if nums.len != 2: if parts.len != 2:
continue continue
var ratio = 1 var ratio = 1
for s in nums: for part in parts:
ratio *= grid.grid_slice(s).parseInt ratio *= grid.text(part.slice).parseInt
result += ratio result += ratio