-
Notifications
You must be signed in to change notification settings - Fork 0
/
day8.js
191 lines (167 loc) · 5.35 KB
/
day8.js
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
// function day8(){
// let fs = require('fs')
// fs.readFile('./day8.txt', 'utf8', function (err, data) {
// if (err) throw err;
// let directions = data.split('\n')
// let obj = new Set()
// let value = 0
// let i = 0
// let isInfinite = false
// while (i < directions.length ){
// if(obj.has(i)){
// isInfinite = true
// break;
// }
// obj.add(i)
// let key = directions[i].split(' ')
// switch(key[0]){
// case 'acc':
// value += parseInt(key[1])
// i++
// break;
// case 'jmp':
// i += parseInt(key[1])
// break
// default:
// i++;
// break
// }
// // console.log(value)
// }
// console.log(value)
// })
// }
// console.log(day8())
// function day8part2(){
// let fs = require('fs')
// fs.readFile('./day8.txt', 'utf8', function (err, data) {
// if (err) throw err;
// let directions = data.split('\n')
// for(let i = 0; i < directions.length; i++){
// let key = directions[i].split(' ')
// if(key[0] === "acc"){
// continue;
// }
// const keyBackup = key[0]
// try{
// key[0] = (key[0] === "jmp") ? 'nop' : 'jmp'
// const result = day8()
// if(result.isInfinite){ return false }
// }
// finally {
// key[0] = keyBackup
// }
// }
// })
// }
// console.log(day8part2())
const fs = require('fs');
/**
* @typedef Instruction A single program instruction
* @type { object }
* @property { string } instruction Instruction to execute
* @property { number } argument Argument to the instruction
*
* @typedef Program A sequence of {@link Instruction}s to execute
* @type { Instruction[] }
*
* @typedef ProgramResult The result of executing a {@link Program}
* @type { object }
* @property { boolean } isCleanExit If true, then the program exited cleanly
* @property { boolean } isInfiniteLoop If true, then the program reached an infinite loop
* @property { number } accumulator Value of the accumulator when program exited
* @property { number } programCounter Value of the program counter (instruction number) when program exited
* @property { number[] } trace Sequence of instructions executed
*/
/**
* Executes a program until exit or an infinite loop
* @param { Program } program Program to execute
* @returns { ProgramResult } Status of the program when finished
*/
function executeProgram(program) {
/** @type { number[] } */
const trace = [];
let pc = 0;
let acc = 0;
let isInfiniteLoop = false;
// loop as long as there are instructions to execute (infinite loops are broken later)
while (pc < program.length) {
// break if we have been here before
if (trace.includes(pc)) {
isInfiniteLoop = true;
break;
}
// get current instruction
const ins = program[pc];
// add to trace
trace.push(pc);
// execute it
switch (ins.instruction) {
case 'nop': {
pc++;
break;
}
case 'acc': {
pc++;
acc += ins.argument;
break;
}
case 'jmp': {
pc += ins.argument;
break;
}
default: throw new Error(`Unknown instruction '${ ins.instruction }' at line ${ pc }`, ins);
}
}
return {
programCounter: pc,
accumulator: acc,
isCleanExit: !isInfiniteLoop,
isInfiniteLoop,
trace
};
}
/**
* Parsed program input.
* @type { Program }
*/
const programInput =
Array.from(
fs.readFileSync('day8.txt', 'utf-8')
.matchAll(/^(.*) \+?([-\d]+)$/gm)
)
.map(([_, instruction, argument]) => ({
instruction,
argument: parseInt(argument)
}))
;
// Part 1
const part1Result = executeProgram(programInput);
console.log(`Part 1: Program executed with return code of ${ part1Result.accumulator }.`, part1Result);
// Part 2
function autoFixProgram() {
// test each instruction until the program is fixed
for (let i = 0; i < programInput.length; i++) {
const ins = programInput[i];
// skip ACCs
if (ins.instruction === 'acc') {
continue;
}
// backup modified instruction to restore after
const insBackup = ins.instruction;
try {
// flip the instruction from jmp -> nop or vice versa
ins.instruction = (ins.instruction === 'jmp') ? 'nop' : 'jmp';
// if program exited cleanly, then it is fixed
const result = executeProgram(programInput);
if (result.isCleanExit) {
return result;
}
} finally {
// fix up the instruction before looping or exiting
ins.instruction = insBackup;
}
}
}
const part2Result = autoFixProgram();
console.log(`Part 2: Program exited successfully with return code of ${ part2Result.accumulator }.`, part2Result);