refactor: 2023 Day 3
This commit is contained in:
parent
b12783daff
commit
cfeaa92460
1 changed files with 76 additions and 54 deletions
166
2023/day03.nim
166
2023/day03.nim
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue