2020-12-08 06:55:34 -06:00
|
|
|
import streams, strutils, sequtils, sets, options, strformat, tables
|
2020-12-07 23:17:17 -06:00
|
|
|
|
|
|
|
type
|
|
|
|
InstructionType = enum nop acc jmp
|
2020-12-07 23:35:16 -06:00
|
|
|
Instruction = object
|
2020-12-07 23:17:17 -06:00
|
|
|
instruction: InstructionType
|
|
|
|
arg: int
|
2020-12-07 23:35:16 -06:00
|
|
|
HandheldState = object
|
|
|
|
bootcode: seq[Instruction]
|
|
|
|
acc: int
|
|
|
|
pointer: int
|
|
|
|
|
2020-12-08 06:55:34 -06:00
|
|
|
proc `$`(i: Instruction): string = return fmt"{i.instruction} {i.arg}"
|
|
|
|
proc `$`(h: HandheldState): string = return fmt"@{h.pointer} ({h.acc}): {h.bootcode}"
|
|
|
|
proc statusString(h: HandheldState): string =
|
|
|
|
let i = h.bootcode[h.pointer]
|
|
|
|
fmt"{h.pointer}: {i.instruction} {i.arg} ({h.acc})"
|
|
|
|
|
|
|
|
proc newHandheld(bootcode: seq[Instruction], acc = 0, pointer = 0): HandheldState =
|
|
|
|
HandheldState(bootcode: bootcode, acc: acc, pointer: pointer)
|
2020-12-07 23:17:17 -06:00
|
|
|
|
|
|
|
proc instructions(s: Stream): seq[Instruction] =
|
|
|
|
toSeq(s.lines).mapIt(it.split).mapIt(Instruction(instruction: parseEnum[InstructionType](it[0]), arg: parseInt(it[1].replace("+"))))
|
|
|
|
|
2020-12-08 06:55:34 -06:00
|
|
|
proc stepHandheld(state: var HandheldState): bool =
|
|
|
|
if state.pointer >= state.bootcode.len: return true
|
2020-12-07 23:35:16 -06:00
|
|
|
let i = state.bootcode[state.pointer]
|
|
|
|
case i.instruction:
|
|
|
|
of acc: inc state.acc, i.arg
|
2020-12-08 06:55:34 -06:00
|
|
|
of jmp: inc state.pointer, i.arg; return false
|
2020-12-07 23:35:16 -06:00
|
|
|
else: discard
|
|
|
|
inc state.pointer
|
|
|
|
|
2020-12-08 06:55:34 -06:00
|
|
|
proc bootHandheld(h: var HandheldState): bool =
|
2020-12-07 23:17:17 -06:00
|
|
|
var hasRun = initHashSet[int]()
|
2020-12-08 06:55:34 -06:00
|
|
|
while h.pointer <= h.bootcode.len:
|
|
|
|
if h.stepHandheld(): return true
|
|
|
|
if hasRun.contains h.pointer: return false
|
|
|
|
hasRun.incl h.pointer
|
|
|
|
true
|
|
|
|
|
|
|
|
proc part1*(s: Stream): int =
|
|
|
|
var h = s.instructions.newHandheld
|
|
|
|
if not h.bootHandheld: return h.acc
|
|
|
|
|
|
|
|
proc flipped(h: var HandheldState): HandheldState =
|
|
|
|
var nbc = h.bootcode.deepCopy
|
|
|
|
case h.bootcode[h.pointer].instruction:
|
|
|
|
of nop: nbc[h.pointer].instruction = jmp
|
|
|
|
of jmp: nbc[h.pointer].instruction = nop
|
2020-12-07 23:35:16 -06:00
|
|
|
else: discard
|
2020-12-08 06:55:34 -06:00
|
|
|
nbc.newHandheld
|
|
|
|
|
|
|
|
proc altHandheld(h: var HandheldState): Option[HandheldState] =
|
|
|
|
case h.bootcode[h.pointer].instruction:
|
|
|
|
of acc: return none(HandheldState)
|
2020-12-11 23:29:49 -06:00
|
|
|
of jmp, nop: return some(h.flipped)
|
2020-12-07 23:17:17 -06:00
|
|
|
|
|
|
|
proc part2*(s: Stream): int =
|
2020-12-08 06:55:34 -06:00
|
|
|
var alts = initTable[int, HandheldState]()
|
|
|
|
var h = s.instructions.newHandheld
|
2020-12-07 23:35:16 -06:00
|
|
|
var hasRun = initHashSet[int]()
|
2020-12-08 06:55:34 -06:00
|
|
|
while h.pointer <= h.bootcode.len:
|
|
|
|
var alt = h.altHandheld
|
|
|
|
if alt.isSome: alts[h.pointer] = alt.get
|
|
|
|
discard h.stepHandheld()
|
|
|
|
if hasRun.contains h.pointer: break
|
|
|
|
hasRun.incl h.pointer
|
|
|
|
#for i in 1..<alts.len:
|
|
|
|
var c = 0
|
|
|
|
for k,v in alts.pairs:
|
|
|
|
var ah = alts[k]
|
|
|
|
if ah.bootHandheld: return ah.acc
|
|
|
|
inc c
|