Skip to content

Instantly share code, notes, and snippets.

@FelicitusNeko
Last active December 17, 2019 22:11
Show Gist options
  • Select an option

  • Save FelicitusNeko/af69e5e1e7775401f6a5c341a4dc66e8 to your computer and use it in GitHub Desktop.

Select an option

Save FelicitusNeko/af69e5e1e7775401f6a5c341a4dc66e8 to your computer and use it in GitHub Desktop.
Advent of Code 2019 Day 11
class IntOpcodeMachine {
constructor(pgm) {
this.pgm = pgm.split(',').map(i => parseInt(i));
this.originalPgm = this.pgm.slice(0);
this.position = 0;
this.relBase = 0;
this.cmdSize = { 1: 4, 2: 4, 3: 2, 4: 2, 5: 3, 6: 3, 7: 4, 8: 4, 9: 2, 99: 1 }
this.input = []; this.output = [];
}
run() {
let isRunning = true;
let retval = true;
let runCount = 0;
while (isRunning && this.position < this.pgm.length) {
let noun = 0, verb = 0;
let opcode = this.pgm[this.position] % 100;
if (this.cmdSize[opcode] === undefined) throw new Error(`Invalid opcode ${opcode} at position ${this.position}`);
let mode = [];
for (let x = 2; x <= this.cmdSize[opcode]; x++) mode.push(Math.round(this.pgm[this.position] / Math.pow(10, x)) % 10);
switch (opcode) {
case 1: // Add values
noun = this.getWithMode(this.position + 1, mode[0]);
verb = this.getWithMode(this.position + 2, mode[1]);
this.setWithMode(this.position + 3, mode[2], noun + verb);
break;
case 2: // Multiply values
noun = this.getWithMode(this.position + 1, mode[0]);
verb = this.getWithMode(this.position + 2, mode[1]);
this.setWithMode(this.position + 3, mode[2], noun * verb);
break;
case 3: // Get from input stack
if (this.input.length === 0) return false;
this.setWithMode(this.position + 1, mode[0], this.input.shift());
break;
case 4: // Push to output stack
this.output.push(this.getWithMode(this.position + 1, mode[0]));
break;
case 5: // Jump if true
if (this.getWithMode(this.position + 1, mode[0]) !== 0) this.position = this.getWithMode(this.position + 2, mode[1]) - 3;
break;
case 6: // Jump if false
if (this.getWithMode(this.position + 1, mode[0]) === 0) this.position = this.getWithMode(this.position + 2, mode[1]) - 3;
break;
case 7: // Evaluate less than
this.setWithMode(this.position + 3, mode[2], (this.getWithMode(this.position + 1, mode[0]) < this.getWithMode(this.position + 2, mode[1]) ? 1 : 0));
break;
case 8: // Evaluate equal
this.setWithMode(this.position + 3, mode[2], (this.getWithMode(this.position + 1, mode[0]) === this.getWithMode(this.position + 2, mode[1]) ? 1 : 0));
break;
case 9: // Adjust relative base
this.relBase += this.getWithMode(this.position + 1, mode[0]);
break;
case 99: // End program
isRunning = false; break;
default: throw new Error(`Invalid opcode ${opcode} at position ${this.position}`);
}
this.position += this.cmdSize[opcode];
if (runCount > 50000) {
console.debug(this.output);
console.debug(this.toString());
throw new Error('Run length cap exceeded');
}
}
return retval;
}
addInput(data) {
if (Array.isArray(data)) this.input = this.input.concat(data);
else this.input.push(data);
}
getOutput() { return this.output.shift(); }
getAllOutput() {
let retval = this.output;
this.output = [];
return retval;
}
zeroFillToPos(pos) {
while (this.pgm.length <= pos) this.pgm.push(0);
}
setAt(pos, data) {
this.zeroFillToPos(pos);
this.pgm[pos] = data;
}
getAt(pos) {
this.zeroFillToPos(pos);
return this.pgm[pos];
}
getModePos(pos, mode) {
switch (mode) {
case 0: return this.pgm[pos];
case 1: return pos;
case 2: return this.pgm[pos] + this.relBase;
default: throw new Error(`Invalid access mode #${mode}`);
}
}
setWithMode(pos, mode, data) {
let modePos = this.getModePos(pos, mode);
this.zeroFillToPos(modePos);
this.pgm[modePos] = data;
}
getWithMode(pos, mode) {
let modePos = this.getModePos(pos, mode);
this.zeroFillToPos(modePos);
return this.pgm[modePos];
}
goto(pos) { this.position = pos; }
restart() { this.pgm = this.originalPgm.slice(); this.position = 0; this.input = []; this.output = []; }
toString() { return this.pgm.join(','); }
}
const paintRoutine = (data) => {
let machine = new IntOpcodeMachine(data);
let paintData = {}, X = 0, Y = 0, dir = 0;
let totalDraws = 0;
do {
let output = machine.getAllOutput();
if (output.length > 0) {
totalDraws++;
paintData[`${X},${Y}`] = output[0];
dir = ((output[1] ? ++dir : --dir) + 4) % 4;
switch (dir) {
case 0: Y--; break;
case 1: X++; break;
case 2: Y++; break;
case 3: X--; break;
default: throw new Error(`Unknown direction value ${dir}`);
}
}
if (paintData[`${X},${Y}`] === undefined) machine.addInput(0);
else machine.addInput(paintData[`${X},${Y}`]);
} while (!machine.run())
return {
totalDraws,
totalUniqueDraws: Object.keys(paintData).length,
paintData
};
}
let bigData = '3,8,1005,8,311,1106,0,11,0,0,0,104,1,104,0,3,8,1002,8,-1,10,101,1,10,10,4,10,108,0,8,10,4,10,1002,8,1,28,2,103,7,10,3,8,1002,8,-1,10,101,1,10,10,4,10,1008,8,1,10,4,10,1001,8,0,55,2,3,6,10,1,101,5,10,1,6,7,10,3,8,1002,8,-1,10,101,1,10,10,4,10,1008,8,0,10,4,10,1001,8,0,89,1,1108,11,10,2,1002,13,10,1006,0,92,1,2,13,10,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,0,10,4,10,101,0,8,126,3,8,1002,8,-1,10,101,1,10,10,4,10,108,1,8,10,4,10,1002,8,1,147,1,7,0,10,3,8,1002,8,-1,10,1001,10,1,10,4,10,108,0,8,10,4,10,101,0,8,173,1006,0,96,3,8,102,-1,8,10,101,1,10,10,4,10,108,0,8,10,4,10,1001,8,0,198,1,3,7,10,1006,0,94,2,1003,20,10,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,1,10,4,10,102,1,8,232,3,8,102,-1,8,10,101,1,10,10,4,10,108,1,8,10,4,10,102,1,8,253,1006,0,63,1,109,16,10,3,8,1002,8,-1,10,101,1,10,10,4,10,1008,8,1,10,4,10,101,0,8,283,2,1107,14,10,1,105,11,10,101,1,9,9,1007,9,1098,10,1005,10,15,99,109,633,104,0,104,1,21102,837951005592,1,1,21101,328,0,0,1105,1,432,21101,0,847069840276,1,21101,0,339,0,1106,0,432,3,10,104,0,104,1,3,10,104,0,104,0,3,10,104,0,104,1,3,10,104,0,104,1,3,10,104,0,104,0,3,10,104,0,104,1,21102,179318123543,1,1,21102,386,1,0,1106,0,432,21102,1,29220688067,1,21102,1,397,0,1106,0,432,3,10,104,0,104,0,3,10,104,0,104,0,21102,709580567396,1,1,21102,1,420,0,1105,1,432,21102,1,868498694912,1,21102,431,1,0,1106,0,432,99,109,2,22101,0,-1,1,21101,40,0,2,21101,0,463,3,21101,0,453,0,1105,1,496,109,-2,2106,0,0,0,1,0,0,1,109,2,3,10,204,-1,1001,458,459,474,4,0,1001,458,1,458,108,4,458,10,1006,10,490,1102,1,0,458,109,-2,2105,1,0,0,109,4,1202,-1,1,495,1207,-3,0,10,1006,10,513,21102,0,1,-3,21201,-3,0,1,21202,-2,1,2,21101,0,1,3,21101,0,532,0,1106,0,537,109,-4,2106,0,0,109,5,1207,-3,1,10,1006,10,560,2207,-4,-2,10,1006,10,560,22102,1,-4,-4,1105,1,628,21201,-4,0,1,21201,-3,-1,2,21202,-2,2,3,21101,0,579,0,1105,1,537,22101,0,1,-4,21102,1,1,-1,2207,-4,-2,10,1006,10,598,21102,1,0,-1,22202,-2,-1,-2,2107,0,-3,10,1006,10,620,22102,1,-1,1,21101,0,620,0,106,0,495,21202,-2,-1,-2,22201,-4,-2,-4,109,-5,2106,0,0';
console.log(paintRoutine(bigData).totalUniqueDraws);
class IntOpcodeMachine {
constructor(pgm) {
this.pgm = pgm.split(',').map(i => parseInt(i));
this.originalPgm = this.pgm.slice(0);
this.position = 0;
this.relBase = 0;
this.cmdSize = { 1: 4, 2: 4, 3: 2, 4: 2, 5: 3, 6: 3, 7: 4, 8: 4, 9: 2, 99: 1 }
this.input = []; this.output = [];
}
run() {
let isRunning = true;
let retval = true;
let runCount = 0;
while (isRunning && this.position < this.pgm.length) {
let noun = 0, verb = 0;
let opcode = this.pgm[this.position] % 100;
if (this.cmdSize[opcode] === undefined) throw new Error(`Invalid opcode ${opcode} at position ${this.position}`);
let mode = [];
for (let x = 2; x <= this.cmdSize[opcode]; x++) mode.push(Math.round(this.pgm[this.position] / Math.pow(10, x)) % 10);
switch (opcode) {
case 1: // Add values
noun = this.getWithMode(this.position + 1, mode[0]);
verb = this.getWithMode(this.position + 2, mode[1]);
this.setWithMode(this.position + 3, mode[2], noun + verb);
break;
case 2: // Multiply values
noun = this.getWithMode(this.position + 1, mode[0]);
verb = this.getWithMode(this.position + 2, mode[1]);
this.setWithMode(this.position + 3, mode[2], noun * verb);
break;
case 3: // Get from input stack
if (this.input.length === 0) return false;
this.setWithMode(this.position + 1, mode[0], this.input.shift());
break;
case 4: // Push to output stack
this.output.push(this.getWithMode(this.position + 1, mode[0]));
break;
case 5: // Jump if true
if (this.getWithMode(this.position + 1, mode[0]) !== 0) this.position = this.getWithMode(this.position + 2, mode[1]) - 3;
break;
case 6: // Jump if false
if (this.getWithMode(this.position + 1, mode[0]) === 0) this.position = this.getWithMode(this.position + 2, mode[1]) - 3;
break;
case 7: // Evaluate less than
this.setWithMode(this.position + 3, mode[2], (this.getWithMode(this.position + 1, mode[0]) < this.getWithMode(this.position + 2, mode[1]) ? 1 : 0));
break;
case 8: // Evaluate equal
this.setWithMode(this.position + 3, mode[2], (this.getWithMode(this.position + 1, mode[0]) === this.getWithMode(this.position + 2, mode[1]) ? 1 : 0));
break;
case 9: // Adjust relative base
this.relBase += this.getWithMode(this.position + 1, mode[0]);
break;
case 99: // End program
isRunning = false; break;
default: throw new Error(`Invalid opcode ${opcode} at position ${this.position}`);
}
this.position += this.cmdSize[opcode];
if (runCount > 50000) {
console.debug(this.output);
console.debug(this.toString());
throw new Error('Run length cap exceeded');
}
}
return retval;
}
addInput(data) {
if (Array.isArray(data)) this.input = this.input.concat(data);
else this.input.push(data);
}
getOutput() { return this.output.shift(); }
getAllOutput() {
let retval = this.output;
this.output = [];
return retval;
}
zeroFillToPos(pos) {
while (this.pgm.length <= pos) this.pgm.push(0);
}
setAt(pos, data) {
this.zeroFillToPos(pos);
this.pgm[pos] = data;
}
getAt(pos) {
this.zeroFillToPos(pos);
return this.pgm[pos];
}
getModePos(pos, mode) {
switch (mode) {
case 0: return this.pgm[pos];
case 1: return pos;
case 2: return this.pgm[pos] + this.relBase;
default: throw new Error(`Invalid access mode #${mode}`);
}
}
setWithMode(pos, mode, data) {
let modePos = this.getModePos(pos, mode);
this.zeroFillToPos(modePos);
this.pgm[modePos] = data;
}
getWithMode(pos, mode) {
let modePos = this.getModePos(pos, mode);
this.zeroFillToPos(modePos);
return this.pgm[modePos];
}
goto(pos) { this.position = pos; }
restart() { this.pgm = this.originalPgm.slice(); this.position = 0; this.input = []; this.output = []; }
toString() { return this.pgm.join(','); }
}
const paintRoutine = data => {
let machine = new IntOpcodeMachine(data);
let paintData = { '0,0': 1 }, X = 0, Y = 0, dir = 0;
let totalDraws = 0;
do {
let output = machine.getAllOutput();
if (output.length > 0) {
totalDraws++;
paintData[`${X},${Y}`] = output[0];
dir = ((output[1] ? ++dir : --dir) + 4) % 4;
switch (dir) {
case 0: Y--; break;
case 1: X++; break;
case 2: Y++; break;
case 3: X--; break;
default: throw new Error(`Unknown direction value ${dir}`);
}
}
if (paintData[`${X},${Y}`] === undefined) machine.addInput(0);
else machine.addInput(paintData[`${X},${Y}`]);
} while (!machine.run())
return {
totalDraws,
totalUniqueDraws: Object.keys(paintData).length,
paintData
};
}
const mapPlotter = paintData => {
let offsetX = Number.MAX_SAFE_INTEGER, offsetY = Number.MAX_SAFE_INTEGER, maxX = Number.MIN_SAFE_INTEGER, maxY = Number.MIN_SAFE_INTEGER;
let enumeratedPaintData = Object.keys(paintData).map(i => i.split(',').map(ii => parseInt(ii)).concat([ paintData[i] ])).filter(i => i[2]);
enumeratedPaintData.forEach(i => {
offsetX = Math.min(offsetX, i[0]);
offsetY = Math.min(offsetY, i[1]);
maxX = Math.max(maxX, i[0]);
maxY = Math.max(maxY, i[1]);
});
let map = [];
for (let Y = offsetY; Y <= maxY; Y++) {
let row = [];
for (let X = offsetX; X <= maxX; X++) row.push(".");
map.push(row);
}
enumeratedPaintData.forEach(i => map[i[1] + offsetY][i[0] + offsetX] = (i[2] ? '#' : '.'));
return map.map(i => i.join('')).join('\n');
}
let bigData = '3,8,1005,8,311,1106,0,11,0,0,0,104,1,104,0,3,8,1002,8,-1,10,101,1,10,10,4,10,108,0,8,10,4,10,1002,8,1,28,2,103,7,10,3,8,1002,8,-1,10,101,1,10,10,4,10,1008,8,1,10,4,10,1001,8,0,55,2,3,6,10,1,101,5,10,1,6,7,10,3,8,1002,8,-1,10,101,1,10,10,4,10,1008,8,0,10,4,10,1001,8,0,89,1,1108,11,10,2,1002,13,10,1006,0,92,1,2,13,10,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,0,10,4,10,101,0,8,126,3,8,1002,8,-1,10,101,1,10,10,4,10,108,1,8,10,4,10,1002,8,1,147,1,7,0,10,3,8,1002,8,-1,10,1001,10,1,10,4,10,108,0,8,10,4,10,101,0,8,173,1006,0,96,3,8,102,-1,8,10,101,1,10,10,4,10,108,0,8,10,4,10,1001,8,0,198,1,3,7,10,1006,0,94,2,1003,20,10,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,1,10,4,10,102,1,8,232,3,8,102,-1,8,10,101,1,10,10,4,10,108,1,8,10,4,10,102,1,8,253,1006,0,63,1,109,16,10,3,8,1002,8,-1,10,101,1,10,10,4,10,1008,8,1,10,4,10,101,0,8,283,2,1107,14,10,1,105,11,10,101,1,9,9,1007,9,1098,10,1005,10,15,99,109,633,104,0,104,1,21102,837951005592,1,1,21101,328,0,0,1105,1,432,21101,0,847069840276,1,21101,0,339,0,1106,0,432,3,10,104,0,104,1,3,10,104,0,104,0,3,10,104,0,104,1,3,10,104,0,104,1,3,10,104,0,104,0,3,10,104,0,104,1,21102,179318123543,1,1,21102,386,1,0,1106,0,432,21102,1,29220688067,1,21102,1,397,0,1106,0,432,3,10,104,0,104,0,3,10,104,0,104,0,21102,709580567396,1,1,21102,1,420,0,1105,1,432,21102,1,868498694912,1,21102,431,1,0,1106,0,432,99,109,2,22101,0,-1,1,21101,40,0,2,21101,0,463,3,21101,0,453,0,1105,1,496,109,-2,2106,0,0,0,1,0,0,1,109,2,3,10,204,-1,1001,458,459,474,4,0,1001,458,1,458,108,4,458,10,1006,10,490,1102,1,0,458,109,-2,2105,1,0,0,109,4,1202,-1,1,495,1207,-3,0,10,1006,10,513,21102,0,1,-3,21201,-3,0,1,21202,-2,1,2,21101,0,1,3,21101,0,532,0,1106,0,537,109,-4,2106,0,0,109,5,1207,-3,1,10,1006,10,560,2207,-4,-2,10,1006,10,560,22102,1,-4,-4,1105,1,628,21201,-4,0,1,21201,-3,-1,2,21202,-2,2,3,21101,0,579,0,1105,1,537,22101,0,1,-4,21102,1,1,-1,2207,-4,-2,10,1006,10,598,21102,1,0,-1,22202,-2,-1,-2,2107,0,-3,10,1006,10,620,22102,1,-1,1,21101,0,620,0,106,0,495,21202,-2,-1,-2,22201,-4,-2,-4,109,-5,2106,0,0';
let paintOutput = paintRoutine(bigData);
console.log(mapPlotter(paintOutput.paintData));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment