-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday8.nim
124 lines (99 loc) · 3.04 KB
/
day8.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import strscans
import strutils
# Declare 'Processor' type
type
Processor = object
accumulator: int16
programCounter: uint16
visitedLines: set[uint16]
Opcode = enum
acc
jmp
nop
# Instruction parsing, returns 'true' when program finishes
proc operation(processor: var Processor, opcode: Opcode, argument: int16):bool =
# Check if line was already visited
if processor.programCounter in processor.visitedLines:
return true # Program finished
# Else record operation
processor.visitedLines.incl(processor.programCounter)
# Perform operation
case opcode
of acc:
processor.accumulator += argument
of jmp:
processor.programCounter += uint16(argument) - 1
of nop:
discard
# Increase program counter
processor.programCounter += 1
proc reset(processor: var Processor) =
var newProcessor: Processor
processor = newProcessor
# Parse file
proc parseBoot(file: string): seq[(Opcode, int16)] =
# Parse file
var ops: seq[(Opcode, int16)]
for line in lines file:
# Read line
var opStr: string
var opArg: int
discard line.scanf("$+ $i", opStr, opArg)
# Convert and store
let op = (parseEnum[Opcode](opStr), int16(opArg))
ops.add(op)
return ops
# Run boot file on processor, returns 'true' on normal termination
proc run(processor: var Processor, ops: seq[(Opcode, int16)]): bool =
# Run program
var repeatedInstruction = false
while not repeatedInstruction:
# Check for normal termination
if processor.programCounter >= uint16(ops.len):
return true
# Execute operation
let (op, arg) = ops[processor.programCounter]
repeatedInstruction = processor.operation(op, arg)
return false
# Run boot file on processor while recording programCounter
proc debug(processor: var Processor, ops: seq[(Opcode, int16)]): seq[uint16] =
# Run program
var repeatedInstruction = false
while not repeatedInstruction:
result.add(processor.programCounter)
# Check for normal termination
if processor.programCounter >= uint16(ops.len):
break
# Execute operation
let (op, arg) = ops[processor.programCounter]
repeatedInstruction = processor.operation(op, arg)
# Run boot file
let inputFile = "resources/day8_input.txt"
var processor: Processor
var bootCode = parseBoot(inputFile)
discard processor.run(bootCode)
# Print results
echo "--- Part 1 Report ---"
echo "Accumulator = " & $processor.accumulator
## Part 2
# Run boot code on debug mode
processor.reset
let trace = processor.debug(bootCode)
# Find last instruction run of type JMP or NOP
for instruction in trace:
var opcodePtr: ptr Opcode = bootCode[instruction][0].addr
if opcodePtr[] in [jmp, nop]:
# Store old reference
let oldOpcode = opcodePtr[]
# Switch instruction
opcodePtr[] = (if opcodePtr[] == jmp: nop else: jmp)
# Run boot code
processor.reset
if processor.run(bootCode):
break
else:
# Restore instruction and move on
opcodePtr[] = oldOpcode
# Print results
echo "--- Part 2 Report ---"
echo "Accumulator = " & $processor.accumulator