diff --git a/2019/JavaScript-TypeScript/day5/package.json b/2019/JavaScript-TypeScript/day5/package.json index 8d5aa60..5124fd5 100644 --- a/2019/JavaScript-TypeScript/day5/package.json +++ b/2019/JavaScript-TypeScript/day5/package.json @@ -1,6 +1,6 @@ { "scripts": { - "lint": "prettier solution_a.ts solution_b.ts --write", + "lint": "prettier solution_a.ts solution_b.ts --write --print-width=120", "a": "ts-node solution_a.ts", "b": "ts-node solution_b.ts" }, @@ -8,7 +8,8 @@ "prettier": "^2.1.2" }, "dependencies": { - "@types/node": "^14.14.6", + "@types/node": "^14.14.8", + "readline": "^1.3.0", "ts-node": "^9.0.0", "typescript": "^4.0.5" }, diff --git a/2019/JavaScript-TypeScript/day5/solution_a.ts b/2019/JavaScript-TypeScript/day5/solution_a.ts index 53e8c20..dbf0c5e 100644 --- a/2019/JavaScript-TypeScript/day5/solution_a.ts +++ b/2019/JavaScript-TypeScript/day5/solution_a.ts @@ -1,71 +1,207 @@ -import * as fs from 'fs'; -const input = fs.readFileSync("input").toString().split(",").map(num => parseInt(num)); +import * as fs from "fs"; +const fsPromises = fs.promises; +import * as readline from "readline"; -enum ParameterModes { +const rl = readline.createInterface(process.stdin, process.stdout); + +enum ParameterMode { position = 0, - immediate = 1 + immediate = 1, } -async function run(noun: number, verb: number) { - let memory = input.slice(); +enum Instruction { + add = 1, + mul = 2, + inp = 3, + out = 4, + end = 99, +} - // Restore integrity - memory[1] = noun - memory[2] = verb - // +type Memory = Array; - let index = 0; +class OpCode { + private _executable: boolean = false; + private _literalValue: number = 0; + private _instruction?: Instruction; + private _parameterModes?: [a: ParameterMode, b: ParameterMode, c: ParameterMode]; - let [a,b,x] = [0,0,0]; - - while(index < memory.length) { - switch(memory[index]){ - case 1: - // .add - [a, b, x] = memory.slice(index+1, index+4); - - memory[x] = memory[a] + memory[b]; - - index += 4; - break; - case 2: - // .mul - [a, b, x] = memory.slice(index+1, index+4); - - memory[x] = memory[a] * memory[b]; - - index += 4; - break; - case 3: - x = memory[index+1]; - memory[x]=x - break; - case 99: - // .end - return memory; - default: - // .hcf - throw new Error("Halt and catch fire"); + constructor(value: number); + constructor(value: string); + constructor(value: string | number) { + let valueNumber: number; + let valueString: string; + if (typeof value === "number") { + valueNumber = value; + valueString = value.toString(); + } else { + valueString = value; + valueNumber = parseInt(value); } - } -} + if (isNaN(valueNumber)) throw new Error(`Invalid value '${value}' for OpCode.`); + valueString = valueString.padStart(5, "0"); -async function main(){ - let noun = 0; - let verb = 0; - while(noun < 99) { - while(verb < 99) { - console.log(`Testing [${noun}, ${verb}]`) - if(await run(noun,verb) === 19690720) { - console.log("Success!") - return 100*noun+verb + this._literalValue = valueNumber; + + if (valueNumber >= 0 && valueNumber <= 99999 && valueNumber % 100 in Instruction) { + const parameterModes: [ParameterMode, ParameterMode, ParameterMode] = [ + Math.floor(valueNumber / 10000) % 10, + Math.floor(valueNumber / 1000) % 10, + Math.floor(valueNumber / 100) % 10, + ]; + if (parameterModes.filter((val) => val in ParameterMode).length === 3) { + this._parameterModes = parameterModes; + this._instruction = valueNumber % 100; + } else { + return; } - verb++; + + this._executable = true; } - noun++; - verb = 0; } - throw new Error("Found no solution!") + + get executable() { + if (this._instruction === undefined || this._parameterModes === undefined) { + this._executable = false; + } + return this._executable; + } + + get literalValue() { + return this._literalValue; + } + get instruction() { + return this._instruction; + } + get parameterModes() { + return this._parameterModes; + } + + toString() { + return this.literalValue.toString(); + } } -main().then(console.log); +class Machine { + private readonly initialMemory: Memory; + private memory: Memory; + constructor(program: Memory) { + this.initialMemory = program; + this.memory = program; + } + + async run(debug: boolean = false) { + let index = 0; + let debugIteration = 0; + const debugLog = await fsPromises.open("out.log", "w"); + let output: number[] = []; + try { + while (index < this.memory.length) { + /*for (let i = 0; i < this.memory.length; i++) { + this.memory[i] = this.memory[i] ?? new OpCode(0); + }*/ + if (debug) { + debugLog.write( + `${(++debugIteration).toString().padStart(5, "0")};${index.toString().padStart(5, "0")};[${this.memory + .map((op) => op.literalValue) + .join(",")}]\n` + ); + } + const opCode = this.memory[index]; + if (!opCode.executable) { + throw new Error(`Tried executing non-executable value ${this.memory[index]} at index ${index}`); + } + + const parameterModes = opCode.parameterModes!; + const valueA = this.memory[index + 1]?.literalValue ?? 0; + const valueB = this.memory[index + 2]?.literalValue ?? 0; + const valueC = this.memory[index + 3]?.literalValue ?? 0; + const paramA = parameterModes[2] === ParameterMode.immediate ? valueA : this.memory[valueA]?.literalValue ?? 0; + const paramB = parameterModes[1] === ParameterMode.immediate ? valueB : this.memory[valueB]?.literalValue ?? 0; + const paramC = parameterModes[0] === ParameterMode.immediate ? valueC : this.memory[valueC]?.literalValue ?? 0; + + /*console.debug( + opCode, + this.memory.slice(index, index + 4).map((val) => val.literalValue) + );*/ + + switch (opCode.instruction) { + case Instruction.add: + if (debug) { + console.debug(`Adding ${paramA}+${paramB} and writing ${paramA + paramB} to ${valueC}`); + } + this.memory[valueC] = new OpCode(paramA + paramB); + index += 4; + break; + case Instruction.mul: + if (debug) { + console.debug(`Multiplying ${paramA}*${paramB} and writing ${paramA * paramB} to ${valueC}`); + } + this.memory[valueC] = new OpCode(paramA * paramB); + index += 4; + break; + case Instruction.inp: + this.memory[valueA] = await new Promise((resolve, reject) => { + rl.question(`[${index.toString().padStart(5, "0")}]> `, (answer) => { + try { + const op = new OpCode(answer); + if (debug) { + console.debug(`Writing opCode with value ${op.literalValue} to ${valueA}`); + } + resolve(op); + } catch (e) { + reject(e); + } + }); + }); + index += 2; + break; + case Instruction.out: + if (debug) { + console.debug(`Outputting ${paramA}`); + } + output.push(paramA); + index += 2; + break; + case Instruction.end: + console.log("Final output:", output); + return this.memory; + } + } + } catch (e) { + console.debug(`Execution failed at index ${index}. Dumping memory.`); + debugLog.write( + `${debugIteration.toString().padStart(5, "0")};${index.toString().padStart(5, "0")};[${this.memory + .map((op) => op.literalValue) + .join(",")}]\n` + ); + throw e; + } + console.log("Final output:", output); + return this.memory; + } + + reset() { + this.memory = this.initialMemory; + } + + static memdump(memory: Memory) { + return memory.map((op) => op.toString()).join(","); + } +} + +async function main(code: string) { + const memdump: Memory = code.split(",").map((val) => new OpCode(val)); + + const machine = new Machine(memdump); + const result = await machine.run(); + rl.close(); + return; // result ? Machine.memdump(result) : result; +} + +const input = fs.readFileSync("input").toString(); +main(input) + .then(console.log) + .catch((...args) => { + rl.close(); + console.error(args); + }); diff --git a/2019/JavaScript-TypeScript/day5/solution_b.ts b/2019/JavaScript-TypeScript/day5/solution_b.ts index 2a5fc29..9aa0341 100644 --- a/2019/JavaScript-TypeScript/day5/solution_b.ts +++ b/2019/JavaScript-TypeScript/day5/solution_b.ts @@ -1,6 +1,247 @@ import * as fs from "fs"; -const input = fs.readFileSync("input"); +const fsPromises = fs.promises; +import * as readline from "readline"; -async function main() {} +const rl = readline.createInterface(process.stdin, process.stdout); -main().then(console.log); +enum ParameterMode { + position = 0, + immediate = 1, +} + +enum Instruction { + add = 1, + mul = 2, + inp = 3, + out = 4, + jnz = 5, + jz = 6, + lt = 7, + eq = 8, + end = 99, +} + +type Memory = Array; + +class OpCode { + private _executable: boolean = false; + private _literalValue: number = 0; + private _instruction?: Instruction; + private _parameterModes?: [a: ParameterMode, b: ParameterMode, c: ParameterMode]; + + constructor(value: number); + constructor(value: string); + constructor(value: string | number) { + let valueNumber: number; + let valueString: string; + if (typeof value === "number") { + valueNumber = value; + valueString = value.toString(); + } else { + valueString = value; + valueNumber = parseInt(value); + } + if (isNaN(valueNumber)) throw new Error(`Invalid value '${value}' for OpCode.`); + valueString = valueString.padStart(5, "0"); + + this._literalValue = valueNumber; + + if (valueNumber >= 0 && valueNumber <= 99999 && valueNumber % 100 in Instruction) { + const parameterModes: [ParameterMode, ParameterMode, ParameterMode] = [ + Math.floor(valueNumber / 10000) % 10, + Math.floor(valueNumber / 1000) % 10, + Math.floor(valueNumber / 100) % 10, + ]; + if (parameterModes.filter((val) => val in ParameterMode).length === 3) { + this._parameterModes = parameterModes; + this._instruction = valueNumber % 100; + } else { + return; + } + + this._executable = true; + } + } + + get executable() { + if (this._instruction === undefined || this._parameterModes === undefined) { + this._executable = false; + } + return this._executable; + } + + get literalValue() { + return this._literalValue; + } + get instruction() { + return this._instruction; + } + get parameterModes() { + return this._parameterModes; + } + + toString() { + return this.literalValue.toString(); + } +} + +class Machine { + private readonly initialMemory: Memory; + private memory: Memory; + constructor(program: Memory) { + this.initialMemory = program; + this.memory = program; + } + + async run(debug: boolean = false) { + let index = 0; + let debugIteration = 0; + const debugLog = debug ? await fsPromises.open("out.log", "w") : undefined; + let output: number[] = []; + try { + while (index < this.memory.length) { + /*for (let i = 0; i < this.memory.length; i++) { + this.memory[i] = this.memory[i] ?? new OpCode(0); + }*/ + if (debug) { + debugLog!.write( + `${(++debugIteration).toString().padStart(5, "0")};${index.toString().padStart(5, "0")};[${this.memory + .map((op) => op.literalValue) + .join(",")}]\n` + ); + } + const opCode = this.memory[index]; + if (!opCode.executable) { + throw new Error(`Tried executing non-executable value ${this.memory[index]} at index ${index}`); + } + + const parameterModes = opCode.parameterModes!; + const valueA = this.memory[index + 1]?.literalValue ?? 0; + const valueB = this.memory[index + 2]?.literalValue ?? 0; + const valueC = this.memory[index + 3]?.literalValue ?? 0; + const paramA = parameterModes[2] === ParameterMode.immediate ? valueA : this.memory[valueA]?.literalValue ?? 0; + const paramB = parameterModes[1] === ParameterMode.immediate ? valueB : this.memory[valueB]?.literalValue ?? 0; + const paramC = parameterModes[0] === ParameterMode.immediate ? valueC : this.memory[valueC]?.literalValue ?? 0; + + /*console.debug( + opCode, + this.memory.slice(index, index + 4).map((val) => val.literalValue) + );*/ + + switch (opCode.instruction) { + case Instruction.add: + if (debug) { + console.debug(`Adding ${paramA}+${paramB} and writing ${paramA + paramB} to ${valueC}`); + } + this.memory[valueC] = new OpCode(paramA + paramB); + index += 4; + break; + case Instruction.mul: + if (debug) { + console.debug(`Multiplying ${paramA}*${paramB} and writing ${paramA * paramB} to ${valueC}`); + } + this.memory[valueC] = new OpCode(paramA * paramB); + index += 4; + break; + case Instruction.inp: + this.memory[valueA] = await new Promise((resolve, reject) => { + rl.question(`[${index.toString().padStart(5, "0")}]> `, (answer) => { + try { + const op = new OpCode(answer); + if (debug) { + console.debug(`Writing opCode with value ${op.literalValue} to ${valueA}`); + } + resolve(op); + } catch (e) { + reject(e); + } + }); + }); + index += 2; + break; + case Instruction.out: + if (debug) { + console.debug(`Outputting ${paramA}`); + } + output.push(paramA); + index += 2; + break; + case Instruction.jnz: + if (debug) { + console.debug(`Testing if ${paramA}!=0, if so, jumping to ${paramB}`); + } + if (paramA !== 0) { + index = paramB; + } else { + index += 3; + } + break; + case Instruction.jz: + if (debug) { + console.debug(`Testing if ${paramA}==0, if so, jumping to ${paramB}`); + } + if (paramA === 0) { + index = paramB; + } else { + index += 3; + } + break; + case Instruction.lt: + if (debug) { + console.debug(`Testing if ${paramA}<${paramB}, writing result to ${valueC}`); + } + this.memory[valueC] = new OpCode(paramA < paramB ? 1 : 0); + index += 4; + break; + case Instruction.eq: + if (debug) { + console.debug(`Testing if ${paramA}==${paramB}, writing result to ${valueC}`); + } + this.memory[valueC] = new OpCode(paramA === paramB ? 1 : 0); + index += 4; + break; + case Instruction.end: + console.log("Final output:", output); + return this.memory; + } + } + } catch (e) { + console.debug(`Execution failed at index ${index}. Dumping memory.`); + if (debug) { + debugLog!.write( + `${debugIteration.toString().padStart(5, "0")};${index.toString().padStart(5, "0")};[${this.memory + .map((op) => op.literalValue) + .join(",")}]\n` + ); + } + throw e; + } + console.log("Final output:", output); + return this.memory; + } + + reset() { + this.memory = this.initialMemory; + } + + static memdump(memory: Memory) { + return memory.map((op) => op.toString()).join(","); + } +} + +async function main(code: string) { + const memdump: Memory = code.split(",").map((val) => new OpCode(val)); + + const machine = new Machine(memdump); + const result = await machine.run(); + rl.close(); + return; // result ? Machine.memdump(result) : result; +} + +const input = fs.readFileSync("input").toString(); +main(input) + .then(console.log) + .catch((...args) => { + rl.close(); + console.error(args); + }); diff --git a/2019/JavaScript-TypeScript/day5/tsconfig.json b/2019/JavaScript-TypeScript/day5/tsconfig.json index 46087de..4e619b9 100644 --- a/2019/JavaScript-TypeScript/day5/tsconfig.json +++ b/2019/JavaScript-TypeScript/day5/tsconfig.json @@ -12,14 +12,14 @@ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ // "declaration": true, /* Generates corresponding '.d.ts' file. */ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": true, /* Generates corresponding '.map' file. */ + "sourceMap": false, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ + "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ diff --git a/2019/JavaScript-TypeScript/day5/yarn.lock b/2019/JavaScript-TypeScript/day5/yarn.lock index 5f45304..2ef6316 100644 --- a/2019/JavaScript-TypeScript/day5/yarn.lock +++ b/2019/JavaScript-TypeScript/day5/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^14.14.6": - version "14.14.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.6.tgz#146d3da57b3c636cc0d1769396ce1cfa8991147f" - integrity sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw== +"@types/node@^14.14.8": + version "14.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.8.tgz#2127bd81949a95c8b7d3240f3254352d72563aec" + integrity sha512-z/5Yd59dCKI5kbxauAJgw6dLPzW+TNOItNE00PkpzNwUIEwdj/Lsqwq94H5DdYBX7C13aRA0CY32BK76+neEUA== arg@^4.1.0: version "4.1.3" @@ -32,6 +32,11 @@ prettier@^2.1.2: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.2.tgz#3050700dae2e4c8b67c4c3f666cdb8af405e1ce5" integrity sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg== +readline@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/readline/-/readline-1.3.0.tgz#c580d77ef2cfc8752b132498060dc9793a7ac01c" + integrity sha1-xYDXfvLPyHUrEySYBg3JeTp6wBw= + source-map-support@^0.5.17: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" diff --git a/2019/JavaScript-TypeScript/template/package.json b/2019/JavaScript-TypeScript/template/package.json index 8d5aa60..9ab417e 100644 --- a/2019/JavaScript-TypeScript/template/package.json +++ b/2019/JavaScript-TypeScript/template/package.json @@ -1,6 +1,6 @@ { "scripts": { - "lint": "prettier solution_a.ts solution_b.ts --write", + "lint": "prettier solution_a.ts solution_b.ts --write --print-width=120", "a": "ts-node solution_a.ts", "b": "ts-node solution_b.ts" }, @@ -8,7 +8,7 @@ "prettier": "^2.1.2" }, "dependencies": { - "@types/node": "^14.14.6", + "@types/node": "^14.14.8", "ts-node": "^9.0.0", "typescript": "^4.0.5" }, diff --git a/2019/JavaScript-TypeScript/template/solution_a.ts b/2019/JavaScript-TypeScript/template/solution_a.ts index 2a5fc29..6996064 100644 --- a/2019/JavaScript-TypeScript/template/solution_a.ts +++ b/2019/JavaScript-TypeScript/template/solution_a.ts @@ -3,4 +3,4 @@ const input = fs.readFileSync("input"); async function main() {} -main().then(console.log); +main().then(console.log).catch(console.error); diff --git a/2019/JavaScript-TypeScript/template/solution_b.ts b/2019/JavaScript-TypeScript/template/solution_b.ts index 2a5fc29..6996064 100644 --- a/2019/JavaScript-TypeScript/template/solution_b.ts +++ b/2019/JavaScript-TypeScript/template/solution_b.ts @@ -3,4 +3,4 @@ const input = fs.readFileSync("input"); async function main() {} -main().then(console.log); +main().then(console.log).catch(console.error); diff --git a/2019/JavaScript-TypeScript/template/tsconfig.json b/2019/JavaScript-TypeScript/template/tsconfig.json index 46087de..4e619b9 100644 --- a/2019/JavaScript-TypeScript/template/tsconfig.json +++ b/2019/JavaScript-TypeScript/template/tsconfig.json @@ -12,14 +12,14 @@ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ // "declaration": true, /* Generates corresponding '.d.ts' file. */ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": true, /* Generates corresponding '.map' file. */ + "sourceMap": false, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ + "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ diff --git a/2019/JavaScript-TypeScript/template/yarn.lock b/2019/JavaScript-TypeScript/template/yarn.lock index 5f45304..ff11529 100644 --- a/2019/JavaScript-TypeScript/template/yarn.lock +++ b/2019/JavaScript-TypeScript/template/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^14.14.6": - version "14.14.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.6.tgz#146d3da57b3c636cc0d1769396ce1cfa8991147f" - integrity sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw== +"@types/node@^14.14.8": + version "14.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.8.tgz#2127bd81949a95c8b7d3240f3254352d72563aec" + integrity sha512-z/5Yd59dCKI5kbxauAJgw6dLPzW+TNOItNE00PkpzNwUIEwdj/Lsqwq94H5DdYBX7C13aRA0CY32BK76+neEUA== arg@^4.1.0: version "4.1.3"