advent/2020/day05.nim

91 lines
2.0 KiB
Nim

import std/[algorithm, math]
import lib/loader
type
BinSet = object
start: int
stop: int
Half = enum
hLow,
hHigh
Seat = object
row: int
col: int
proc newBinSet(size: int): BinSet =
# check to make sure size is a power of 2
var q = size
while q mod 2 == 0:
q = q div 2
if q != 1:
raise newException(ValueError, "BinSet must have a size that is a power of 2.")
result = BinSet(start: 0, stop: size - 1)
proc size(bs: BinSet): int = bs.stop - bs.start + 1 # bounds are inclusive
func seatId(s: Seat): int = s.row * 8 + s.col
func seatCmp(s1, s2: Seat): int =
let i1 = s1.seatId()
let i2 = s2.seatId()
if i1 < i2:
return -1
elif i1 == i2:
return 0
else:
return 1
proc split(bs: var BinSet, h: Half) =
if h == hLow:
bs.stop -= (bs.size div 2)
else:
bs.start += (bs.size div 2)
proc parseBinSet(spec: string; lowChar, hiChar: char): int =
var bset = newBinSet(2 ^ spec.len)
for c in spec:
if c == lowChar:
bset.split(hLow)
elif c == hiChar:
bset.split(hHigh)
else:
raise newException(ValueError, "Bad BinSet specifier: " & spec)
assert(bset.start == bset.stop) # this should never be false, but just in case
result = bset.start
proc loadSeats(): seq[Seat] =
for line in loadStrings(5):
let row = parseBinSet(line[0..6], 'F', 'B')
let col = parseBinSet(line[7..^1], 'L', 'R')
result.add(Seat(row: row, col: col))
result.sort(seatCmp)
proc partOne(seats: seq[Seat]): int =
for s in seats:
if s.seatId() > result:
result = s.seatId()
proc partTwo(seats: seq[Seat]): int =
for i, s in seats:
let curId = s.seatId()
let nextId = seats[i + 1].seatId()
if nextId - curId != 1: # i.e. if there's a gap
return curId + 1
when isMainModule:
let seats = loadSeats()
echo "One: ", partOne(seats)
echo "Two: ", partTwo(seats)