Last active
December 17, 2019 22:11
-
-
Save FelicitusNeko/af69e5e1e7775401f6a5c341a4dc66e8 to your computer and use it in GitHub Desktop.
Advent of Code 2019 Day 11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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