Skip to content

Instantly share code, notes, and snippets.

@zoldar
Created December 13, 2022 16:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zoldar/e4857df1b9906c7f418cbe8795c9ab15 to your computer and use it in GitHub Desktop.
Save zoldar/e4857df1b9906c7f418cbe8795c9ab15 to your computer and use it in GitHub Desktop.
import sequtils, strformat, strutils, algorithm
type
CheckResult = enum Valid, Equal, Invalid
PacketKind = enum Seq, Int
Packet = ref PacketObj
PacketObj = object
case kind: PacketKind
of Seq: seqVal: seq[Packet]
of Int: intVal: int
proc `$`(p: Packet): string =
if p.kind == Int:
result.add($p.intVal)
else:
result.add("Packet{")
for x in p.seqVal:
if result.len > "Packet{".len: result.add(",")
result.addQuoted(x)
result.add("}")
proc parsePacket(input: string): Packet =
if input.startsWith("["):
let contents = input[1..^2]
var depth: int
var elements: seq[string]
var element: seq[char]
for ch in contents:
case ch:
of '[':
element.add(ch)
depth.inc
of ']':
element.add(ch)
depth.dec
of ',':
if depth == 0:
elements.add(element.join)
element = @[]
else:
element.add(ch)
else:
element.add(ch)
if element.len > 0: elements.add(element.join)
return Packet(kind: Seq, seqVal: elements.mapIt(parsePacket(it)))
else:
return Packet(kind: Int, intVal: parseInt(input))
proc readPackets(input: seq[string]): seq[Packet] =
input.filterIt(it != "").mapIt(parsePacket(it))
proc checkPair(pair: (Packet, Packet)): CheckResult =
let (left, right) = pair
if left.kind == Int and right.kind == Int:
if left.intVal < right.intVal:
return Valid
elif left.intVal == right.intVal:
return Equal
else:
return Invalid
elif left.kind == Seq and right.kind == Int:
return checkPair((left, Packet(kind: Seq, seqVal: @[right])))
elif left.kind == Int and right.kind == Seq:
return checkPair((Packet(kind: Seq, seqVal: @[left]), right))
else:
for innerPair in zip(left.seqVal, right.seqVal):
let checkResult = checkPair(innerPair)
if checkResult in [Valid, Invalid]:
return checkResult
let leftLen = left.seqVal.len
let rightLen = right.seqVal.len
if leftLen == rightLen:
return Equal
elif leftLen < rightLen:
return Valid
else:
return Invalid
var sum: int
let packets = readPackets("day-13/input.txt".lines.toSeq)
for (idx, pair) in packets.distribute(packets.len div 2).pairs:
echo fmt"Pair #{idx+1}"
let left = pair[0]
let right = pair[1]
echo left
echo right
if checkPair((left, right)) in [Valid, Equal]:
echo "Valid!"
sum.inc(idx+1)
else:
echo "Invalid!"
echo "==="
echo fmt"Sum of indices of valid packet pairs: {sum}"
let dividerPackets = readPackets(@["[[2]]", "[[6]]"])
var packetsWithDividers = packets.concat(dividerPackets)
packetsWithDividers.sort do (x, y: Packet) -> int:
case checkPair((x, y)):
of Invalid: 1
of Equal: 0
of Valid: -1
var dividerIndexes: seq[int]
for (idx, packet) in packetsWithDividers.pairs:
if packet in dividerPackets:
dividerIndexes.add(idx + 1)
echo fmt"Decoder key: {dividerIndexes.foldl(a * b)}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment