diff --git a/www/statics/aoc/2019/16_solution-2.ts b/www/statics/aoc/2019/16_solution-2.ts index e776ea8..19b54a5 100644 --- a/www/statics/aoc/2019/16_solution-2.ts +++ b/www/statics/aoc/2019/16_solution-2.ts @@ -23,7 +23,7 @@ namespace AdventOfCode2019_16_2 step = StupidBigFFT(step); AdventOfCode.outputConsole(`[${i}] ` + step.slice(0, 8).map(p=>p.toString()).reduce((a,b)=>a+b)); - await AdventOfCode.sleep(0); + await AdventOfCode.sleepIfConsole(0); } AdventOfCode.output(DAY, PROBLEM, step.slice(0, 8).map(p=>p.toString()).reduce((a,b)=>a+b)); diff --git a/www/statics/aoc/2019/17_challenge.txt b/www/statics/aoc/2019/17_challenge.txt new file mode 100644 index 0000000..c738262 --- /dev/null +++ b/www/statics/aoc/2019/17_challenge.txt @@ -0,0 +1,122 @@ +--- Day 17: Set and Forget --- + +An early warning system detects an incoming solar flare and automatically activates the ship's electromagnetic shield. Unfortunately, this has cut off the Wi-Fi for many small robots that, unaware of the impending danger, are now trapped on exterior scaffolding on the unsafe side of the shield. To rescue them, you'll have to act quickly! + +The only tools at your disposal are some wired cameras and a small vacuum robot currently asleep at its charging station. The video quality is poor, but the vacuum robot has a needlessly bright LED that makes it easy to spot no matter where it is. + +An Intcode program, the Aft Scaffolding Control and Information Interface (ASCII, your puzzle input), provides access to the cameras and the vacuum robot. Currently, because the vacuum robot is asleep, you can only access the cameras. + +Running the ASCII program on your Intcode computer will provide the current view of the scaffolds. This is output, purely coincidentally, as ASCII code: 35 means #, 46 means ., 10 starts a new line of output below the current one, and so on. (Within a line, characters are drawn left-to-right.) + +In the camera output, # represents a scaffold and . represents open space. The vacuum robot is visible as ^, v, <, or > depending on whether it is facing up, down, left, or right respectively. When drawn like this, the vacuum robot is always on a scaffold; if the vacuum robot ever walks off of a scaffold and begins tumbling through space uncontrollably, it will instead be visible as X. + +In general, the scaffold forms a path, but it sometimes loops back onto itself. For example, suppose you can see the following view from the cameras: + +..#.......... +..#.......... +#######...### +#.#...#...#.# +############# +..#...#...#.. +..#####...^.. + +Here, the vacuum robot, ^ is facing up and sitting at one end of the scaffold near the bottom-right of the image. The scaffold continues up, loops across itself several times, and ends at the top-left of the image. + +The first step is to calibrate the cameras by getting the alignment parameters of some well-defined points. Locate all scaffold intersections; for each, its alignment parameter is the distance between its left edge and the left edge of the view multiplied by the distance between its top edge and the top edge of the view. Here, the intersections from the above image are marked O: + +..#.......... +..#.......... +##O####...### +#.#...#...#.# +##O###O###O## +..#...#...#.. +..#####...^.. + +For these intersections: + + The top-left intersection is 2 units from the left of the image and 2 units from the top of the image, so its alignment parameter is 2 * 2 = 4. + The bottom-left intersection is 2 units from the left and 4 units from the top, so its alignment parameter is 2 * 4 = 8. + The bottom-middle intersection is 6 from the left and 4 from the top, so its alignment parameter is 24. + The bottom-right intersection's alignment parameter is 40. + +To calibrate the cameras, you need the sum of the alignment parameters. In the above example, this is 76. + +Run your ASCII program. What is the sum of the alignment parameters for the scaffold intersections? + +--- Part Two --- + +Now for the tricky part: notifying all the other robots about the solar flare. The vacuum robot can do this automatically if it gets into range of a robot. However, you can't see the other robots on the camera, so you need to be thorough instead: you need to make the vacuum robot visit every part of the scaffold at least once. + +The vacuum robot normally wanders randomly, but there isn't time for that today. Instead, you can override its movement logic with new rules. + +Force the vacuum robot to wake up by changing the value in your ASCII program at address 0 from 1 to 2. When you do this, you will be automatically prompted for the new movement rules that the vacuum robot should use. The ASCII program will use input instructions to receive them, but they need to be provided as ASCII code; end each line of logic with a single newline, ASCII code 10. + +First, you will be prompted for the main movement routine. The main routine may only call the movement functions: A, B, or C. Supply the movement functions to use as ASCII text, separating them with commas (,, ASCII code 44), and ending the list with a newline (ASCII code 10). For example, to call A twice, then alternate between B and C three times, provide the string A,A,B,C,B,C,B,C and then a newline. + +Then, you will be prompted for each movement function. Movement functions may use L to turn left, R to turn right, or a number to move forward that many units. Movement functions may not call other movement functions. Again, separate the actions with commas and end the list with a newline. For example, to move forward 10 units, turn left, move forward 8 units, turn right, and finally move forward 6 units, provide the string 10,L,8,R,6 and then a newline. + +Finally, you will be asked whether you want to see a continuous video feed; provide either y or n and a newline. Enabling the continuous video feed can help you see what's going on, but it also requires a significant amount of processing power, and may even cause your Intcode computer to overheat. + +Due to the limited amount of memory in the vacuum robot, the ASCII definitions of the main routine and the movement functions may each contain at most 20 characters, not counting the newline. + +For example, consider the following camera feed: + +#######...##### +#.....#...#...# +#.....#...#...# +......#...#...# +......#...###.# +......#.....#.# +^########...#.# +......#.#...#.# +......######### +........#...#.. +....#########.. +....#...#...... +....#...#...... +....#...#...... +....#####...... + +In order for the vacuum robot to visit every part of the scaffold at least once, one path it could take is: + +R,8,R,8,R,4,R,4,R,8,L,6,L,2,R,4,R,4,R,8,R,8,R,8,L,6,L,2 + +Without the memory limit, you could just supply this whole string to function A and have the main routine call A once. However, you'll need to split it into smaller parts. + +One approach is: + + Main routine: A,B,C,B,A,C + (ASCII input: 65, 44, 66, 44, 67, 44, 66, 44, 65, 44, 67, 10) + Function A: R,8,R,8 + (ASCII input: 82, 44, 56, 44, 82, 44, 56, 10) + Function B: R,4,R,4,R,8 + (ASCII input: 82, 44, 52, 44, 82, 44, 52, 44, 82, 44, 56, 10) + Function C: L,6,L,2 + (ASCII input: 76, 44, 54, 44, 76, 44, 50, 10) + +Visually, this would break the desired path into the following parts: + +A, B, C, B, A, C +R,8,R,8, R,4,R,4,R,8, L,6,L,2, R,4,R,4,R,8, R,8,R,8, L,6,L,2 + +CCCCCCA...BBBBB +C.....A...B...B +C.....A...B...B +......A...B...B +......A...CCC.B +......A.....C.B +^AAAAAAAA...C.B +......A.A...C.B +......AAAAAA#AB +........A...C.. +....BBBB#BBBB.. +....B...A...... +....B...A...... +....B...A...... +....BBBBA...... + +Of course, the scaffolding outside your ship is much more complex. + +As the vacuum robot finds other robots and notifies them of the impending solar flare, it also can't help but leave them squeaky clean, collecting any space dust it finds. Once it finishes the programmed set of movements, assuming it hasn't drifted off into space, the cleaning robot will return to its docking station and report the amount of space dust it collected as a large, non-ASCII value in a single output instruction. + +After visiting every part of the scaffold at least once, how much dust does the vacuum robot report it has collected? diff --git a/www/statics/aoc/2019/17_input.txt b/www/statics/aoc/2019/17_input.txt new file mode 100644 index 0000000..84520cf --- /dev/null +++ b/www/statics/aoc/2019/17_input.txt @@ -0,0 +1 @@ +1,330,331,332,109,3016,1101,1182,0,16,1101,1441,0,24,102,1,0,570,1006,570,36,1002,571,1,0,1001,570,-1,570,1001,24,1,24,1106,0,18,1008,571,0,571,1001,16,1,16,1008,16,1441,570,1006,570,14,21101,58,0,0,1105,1,786,1006,332,62,99,21101,333,0,1,21101,73,0,0,1105,1,579,1101,0,0,572,1101,0,0,573,3,574,101,1,573,573,1007,574,65,570,1005,570,151,107,67,574,570,1005,570,151,1001,574,-64,574,1002,574,-1,574,1001,572,1,572,1007,572,11,570,1006,570,165,101,1182,572,127,102,1,574,0,3,574,101,1,573,573,1008,574,10,570,1005,570,189,1008,574,44,570,1006,570,158,1106,0,81,21101,340,0,1,1106,0,177,21102,1,477,1,1106,0,177,21101,514,0,1,21102,176,1,0,1105,1,579,99,21102,1,184,0,1105,1,579,4,574,104,10,99,1007,573,22,570,1006,570,165,1002,572,1,1182,21101,0,375,1,21101,211,0,0,1105,1,579,21101,1182,11,1,21101,222,0,0,1105,1,979,21102,1,388,1,21102,1,233,0,1105,1,579,21101,1182,22,1,21102,244,1,0,1106,0,979,21102,401,1,1,21101,0,255,0,1106,0,579,21101,1182,33,1,21101,266,0,0,1106,0,979,21102,414,1,1,21101,0,277,0,1105,1,579,3,575,1008,575,89,570,1008,575,121,575,1,575,570,575,3,574,1008,574,10,570,1006,570,291,104,10,21102,1,1182,1,21101,0,313,0,1106,0,622,1005,575,327,1102,1,1,575,21101,327,0,0,1106,0,786,4,438,99,0,1,1,6,77,97,105,110,58,10,33,10,69,120,112,101,99,116,101,100,32,102,117,110,99,116,105,111,110,32,110,97,109,101,32,98,117,116,32,103,111,116,58,32,0,12,70,117,110,99,116,105,111,110,32,65,58,10,12,70,117,110,99,116,105,111,110,32,66,58,10,12,70,117,110,99,116,105,111,110,32,67,58,10,23,67,111,110,116,105,110,117,111,117,115,32,118,105,100,101,111,32,102,101,101,100,63,10,0,37,10,69,120,112,101,99,116,101,100,32,82,44,32,76,44,32,111,114,32,100,105,115,116,97,110,99,101,32,98,117,116,32,103,111,116,58,32,36,10,69,120,112,101,99,116,101,100,32,99,111,109,109,97,32,111,114,32,110,101,119,108,105,110,101,32,98,117,116,32,103,111,116,58,32,43,10,68,101,102,105,110,105,116,105,111,110,115,32,109,97,121,32,98,101,32,97,116,32,109,111,115,116,32,50,48,32,99,104,97,114,97,99,116,101,114,115,33,10,94,62,118,60,0,1,0,-1,-1,0,1,0,0,0,0,0,0,1,14,0,0,109,4,1202,-3,1,587,20102,1,0,-1,22101,1,-3,-3,21102,0,1,-2,2208,-2,-1,570,1005,570,617,2201,-3,-2,609,4,0,21201,-2,1,-2,1105,1,597,109,-4,2105,1,0,109,5,1202,-4,1,630,20101,0,0,-2,22101,1,-4,-4,21102,0,1,-3,2208,-3,-2,570,1005,570,781,2201,-4,-3,652,21002,0,1,-1,1208,-1,-4,570,1005,570,709,1208,-1,-5,570,1005,570,734,1207,-1,0,570,1005,570,759,1206,-1,774,1001,578,562,684,1,0,576,576,1001,578,566,692,1,0,577,577,21101,0,702,0,1106,0,786,21201,-1,-1,-1,1105,1,676,1001,578,1,578,1008,578,4,570,1006,570,724,1001,578,-4,578,21101,0,731,0,1105,1,786,1105,1,774,1001,578,-1,578,1008,578,-1,570,1006,570,749,1001,578,4,578,21101,756,0,0,1106,0,786,1105,1,774,21202,-1,-11,1,22101,1182,1,1,21101,774,0,0,1106,0,622,21201,-3,1,-3,1105,1,640,109,-5,2106,0,0,109,7,1005,575,802,20101,0,576,-6,20101,0,577,-5,1106,0,814,21101,0,0,-1,21101,0,0,-5,21102,1,0,-6,20208,-6,576,-2,208,-5,577,570,22002,570,-2,-2,21202,-5,45,-3,22201,-6,-3,-3,22101,1441,-3,-3,2101,0,-3,843,1005,0,863,21202,-2,42,-4,22101,46,-4,-4,1206,-2,924,21102,1,1,-1,1105,1,924,1205,-2,873,21101,35,0,-4,1106,0,924,1201,-3,0,878,1008,0,1,570,1006,570,916,1001,374,1,374,1202,-3,1,895,1101,2,0,0,1201,-3,0,902,1001,438,0,438,2202,-6,-5,570,1,570,374,570,1,570,438,438,1001,578,558,922,20101,0,0,-4,1006,575,959,204,-4,22101,1,-6,-6,1208,-6,45,570,1006,570,814,104,10,22101,1,-5,-5,1208,-5,35,570,1006,570,810,104,10,1206,-1,974,99,1206,-1,974,1101,0,1,575,21101,973,0,0,1105,1,786,99,109,-7,2106,0,0,109,6,21102,0,1,-4,21102,1,0,-3,203,-2,22101,1,-3,-3,21208,-2,82,-1,1205,-1,1030,21208,-2,76,-1,1205,-1,1037,21207,-2,48,-1,1205,-1,1124,22107,57,-2,-1,1205,-1,1124,21201,-2,-48,-2,1105,1,1041,21102,-4,1,-2,1105,1,1041,21101,-5,0,-2,21201,-4,1,-4,21207,-4,11,-1,1206,-1,1138,2201,-5,-4,1059,1202,-2,1,0,203,-2,22101,1,-3,-3,21207,-2,48,-1,1205,-1,1107,22107,57,-2,-1,1205,-1,1107,21201,-2,-48,-2,2201,-5,-4,1090,20102,10,0,-1,22201,-2,-1,-2,2201,-5,-4,1103,2101,0,-2,0,1106,0,1060,21208,-2,10,-1,1205,-1,1162,21208,-2,44,-1,1206,-1,1131,1106,0,989,21102,439,1,1,1105,1,1150,21102,477,1,1,1106,0,1150,21101,514,0,1,21102,1149,1,0,1105,1,579,99,21101,1157,0,0,1105,1,579,204,-2,104,10,99,21207,-3,22,-1,1206,-1,1138,2101,0,-5,1176,2101,0,-4,0,109,-6,2106,0,0,10,5,40,1,44,1,44,1,44,7,44,1,44,1,7,13,24,1,7,1,11,1,8,7,9,1,7,1,11,1,8,1,5,1,9,1,7,1,11,1,8,1,5,1,9,1,1,5,1,1,5,9,6,1,5,1,9,1,1,1,3,1,1,1,5,1,5,1,1,1,6,1,5,1,7,11,1,11,1,1,6,1,5,1,7,1,1,1,1,1,3,1,3,1,3,1,7,1,6,1,5,1,7,1,1,7,3,1,3,1,7,1,6,1,5,1,7,1,3,1,7,1,3,1,7,1,6,1,5,1,1,11,1,11,7,1,6,1,5,1,1,1,5,1,5,1,5,1,11,1,6,1,5,9,5,1,5,1,11,1,6,1,7,1,11,1,5,1,11,1,6,7,1,1,11,1,5,7,5,7,6,1,1,1,11,1,11,1,11,1,6,1,1,13,11,1,11,1,6,1,25,1,11,1,6,1,25,1,11,1,6,1,25,1,11,1,6,1,25,1,11,1,6,1,25,1,11,1,6,1,25,1,1,11,6,1,25,1,1,1,16,7,19,7,40,1,3,1,40,1,3,1,40,1,3,1,40,5,6 diff --git a/www/statics/aoc/2019/17_solution-1.ts b/www/statics/aoc/2019/17_solution-1.ts new file mode 100644 index 0000000..7c947bc --- /dev/null +++ b/www/statics/aoc/2019/17_solution-1.ts @@ -0,0 +1,341 @@ +namespace AdventOfCode2019_17_1 +{ + const DAY = 17; + const PROBLEM = 1; + + export async function run() + { + let input = await AdventOfCode.getInput(DAY); + if (input == null) return; + + const code = input.trim().split(",").map(p => parseInt(p.trim())); + + let rnr = new Interpreter(code, []); + + let map: {[_:number]: number} = {}; + let minx = 0; + let maxx = 0; + let miny = 0; + let maxy = 0; + let out = ""; + let x=0; + let y=0 + while (!rnr.is_halted) + { + rnr.singleStep(); + if (rnr.output.length>0) + { + const v = rnr.output.pop()!; + out += String.fromCharCode(v); + await AdventOfCode.outputIntermed(out); + + if (v === 10) + { + y++; + x=0; + } + else + { + AdventOfCode.outputConsole(`# [${x}|${y}] = ${v}`); + + map[y*1_0000 + x] = v; + x++; + + minx = Math.min(minx, x); + maxx = Math.max(maxx, x); + miny = Math.min(miny, y); + maxy = Math.max(maxy, y); + } + } + } + + let result = 0; + + for(let yy=miny+1;yy<=maxy-1;yy++) + for(let xx=minx+1;xx<=maxx-1;xx++) + { + const v0 = map[(yy )*1_0000 + (xx )]; + const vN = map[(yy-1)*1_0000 + (xx )]; + const vE = map[(yy )*1_0000 + (xx+1)]; + const vS = map[(yy+1)*1_0000 + (xx )]; + const vW = map[(yy )*1_0000 + (xx-1)]; + + if (v0 != 35) continue; + if (vN != 35) continue; + if (vE != 35) continue; + if (vS != 35) continue; + if (vW != 35) continue; + + let vv = xx*yy; + + AdventOfCode.outputConsole(`[${xx}|${yy}] = ${vv}`); + + result += vv; + } + + AdventOfCode.output(DAY, PROBLEM, result.toString()); + } + + class Interpreter + { + program: InfMem; + inputqueue: number[]; + instructionpointer: number; + output: number[]; + relative_base: number; + + is_halted: boolean = false; + + constructor(prog: number[], input: number[]) + { + this.program = new InfMem(prog); + this.inputqueue = input; + this.instructionpointer = 0; + this.output = []; + this.relative_base = 0; + } + + fullRun() : number[] + { + while(!this.is_halted) + { + const r = this.singleStep(); + + if (r === StepResult.EXECUTED) continue; + if (r === StepResult.HALTED) return this.output; + if (r === StepResult.WAITING_FOR_IN) throw "not enough input"; + + throw "unknown output of singleStep"; + } + + return this.output; + } + + autoRun() : StepResult + { + while(!this.is_halted) + { + const r = this.singleStep(); + + if (r === StepResult.EXECUTED) continue; + if (r === StepResult.HALTED) return StepResult.HALTED; + if (r === StepResult.WAITING_FOR_IN) return StepResult.WAITING_FOR_IN; + + throw "unknown output of singleStep"; + } + + return StepResult.HALTED; + } + + singleStep() : StepResult + { + const cmd = new Op(this.program.r(this.instructionpointer)); + + if (cmd.opcode == OpCode.ADD) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 + p1; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.MUL) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 * p1; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.HALT) + { + this.is_halted = true; + return StepResult.HALTED; + } + else if (cmd.opcode == OpCode.IN) + { + if (this.inputqueue.length == 0) return StepResult.WAITING_FOR_IN; + + const pv = this.inputqueue[0]; + cmd.setParameter(this, 0, pv); + this.inputqueue = this.inputqueue.slice(1); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.OUT) + { + const p0 = cmd.getParameter(this, 0); + this.output.push(p0); + //AdventOfCode.outputConsole("# " + p0); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.TJMP) + { + const p0 = cmd.getParameter(this, 0); + if (p0 != 0) this.instructionpointer = cmd.getParameter(this, 1); + else this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.FJMP) + { + const p0 = cmd.getParameter(this, 0); + if (p0 == 0) this.instructionpointer = cmd.getParameter(this, 1); + else this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.LT) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 < p1 ? 1 : 0; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.EQ) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 == p1 ? 1 : 0; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.ARB) + { + const p0 = cmd.getParameter(this, 0); + this.relative_base = this.relative_base+p0; + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else throw "Unknown Op: " + cmd.opcode + " @ " + this.instructionpointer; + } + + private incInstrPtr(cmd: Op) + { + this.instructionpointer += 1 + cmd.parametercount; + } + } + + enum StepResult { EXECUTED, HALTED, WAITING_FOR_IN } + + enum OpCode + { + ADD = 1, + MUL = 2, + IN = 3, + OUT = 4, + TJMP = 5, + FJMP = 6, + LT = 7, + EQ = 8, + ARB = 9, + HALT = 99, + } + + enum ParamMode + { + POSITION_MODE = 0, + IMMEDIATE_MODE = 1, + RELATIVE_MODE = 2, + } + + class Op + { + opcode: OpCode; + modes: ParamMode[]; + + name: string; + parametercount: number; + + constructor(v: number) + { + this.opcode = v%100; + v = Math.floor(v/100); + this.modes = []; + for(let i=0; i<4; i++) + { + this.modes.push(v%10); + v = Math.floor(v/10); + } + + if (this.opcode == OpCode.ADD) { this.name="ADD"; this.parametercount=3; } + else if (this.opcode == OpCode.MUL) { this.name="MUL"; this.parametercount=3; } + else if (this.opcode == OpCode.HALT) { this.name="HALT"; this.parametercount=0; } + else if (this.opcode == OpCode.IN) { this.name="IN"; this.parametercount=1; } + else if (this.opcode == OpCode.OUT) { this.name="OUT"; this.parametercount=1; } + else if (this.opcode == OpCode.TJMP) { this.name="TJMP"; this.parametercount=2; } + else if (this.opcode == OpCode.FJMP) { this.name="FJMP"; this.parametercount=2; } + else if (this.opcode == OpCode.LT) { this.name="LT"; this.parametercount=3; } + else if (this.opcode == OpCode.EQ) { this.name="EQ"; this.parametercount=3; } + else if (this.opcode == OpCode.ARB) { this.name="ARB"; this.parametercount=1; } + else throw "Unknown opcode: "+this.opcode; + } + + getParameter(proc: Interpreter, index: number): number + { + const prog = proc.program; + const ip = proc.instructionpointer; + + let p = prog.r(ip+1+index); + + if (this.modes[index] == ParamMode.POSITION_MODE) p = prog.r(p); + else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) p = p; + else if (this.modes[index] == ParamMode.RELATIVE_MODE) p = prog.r(proc.relative_base+p); + else throw "Unknown ParamMode: "+this.modes[index]; + + return p; + } + + setParameter(proc: Interpreter, index: number, value: number): void + { + const prog = proc.program; + const ip = proc.instructionpointer; + + let p = prog.r(ip+1+index); + + if (this.modes[index] == ParamMode.POSITION_MODE) prog.w(p, value); + else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) throw "Immediate mode not allowed in write"; + else if (this.modes[index] == ParamMode.RELATIVE_MODE) prog.w(proc.relative_base+p, value); + else throw "Unknown ParamMode: "+this.modes[index]; + } + } + + class InfMem + { + private data: { [_:number]:number } = {}; + + constructor(v: number[]) + { + for(let i=0; i parseInt(p.trim())); + + let [map, maxx, maxy] = await getMap(code); + + const fullpath = getFullPath(map, maxx, maxy); + + let proginput = await splitPath(fullpath, "y"); + + code[0]=2; + let rnr = new Interpreter(code, proginput); + while(!rnr.is_halted && (rnr.output.length < 0 || rnr.output[0] !== "?".charCodeAt(0))) { rnr.output=[]; rnr.singleStep(); } + while(!rnr.is_halted && (rnr.output.length < 0 || rnr.output[0] !== ".".charCodeAt(0))) { rnr.output=[]; rnr.singleStep(); } + while(!rnr.is_halted) + { + rnr.singleStep(); + + if (rnr.output.length === (maxx+1)*(maxy+1)+1) + { + let str = rnr.output.map(p => String.fromCharCode(p)).reduce((a,b)=>a+b); + await AdventOfCode.outputIntermed(str); + await AdventOfCode.sleepIfIntermed(10); + rnr.output = []; + + AdventOfCode.outputConsole(str); + AdventOfCode.outputConsole("---------------NEXT------------"); + } + } + + AdventOfCode.outputConsole(rnr.output.map(p => String.fromCharCode(p)).reduce((a,b)=>a+b)); + + + AdventOfCode.output(DAY, PROBLEM, rnr.output[0].toString()); + } + + async function splitPath(fullpath: RoboCommand[], live: string): Promise + { + let allsplits: { [pos:number]: [ number, RoboCommand[], number[] ][] } = {}; + for(let i=0; i0;splitlen--) + { + let localfounds: Set = new Set(); + for (let start=0; start <= fullpath.length-splitlen; start++) + { + if (localfounds.has(start)) continue; + + let occurences = findInPath(fullpath, start, splitlen); + if (occurences.length === 1) continue; + + for(const occ of occurences) localfounds.add(occ); + + let localsplit = fullpath.slice(start, start+splitlen); + + let localsplitstr = localsplit.map(p=>p.toString()).reduce((a,b)=>a+","+b); + if (localsplitstr.length>20) continue; + + let r: [ number, RoboCommand[], number[] ] = [ splitid++, localsplit, occurences ]; + + splitmap[r[0]] = [ r[1], r[2] ]; + + AdventOfCode.outputConsole(`||(${r[0]}) ${occurences.length} x [${localsplitstr}] --> [${occurences.map(p=>p.toString()).reduce((a,b)=>a+";"+b)}]`); + + for(const occ of occurences) allsplits[occ].push(r); + } + } + + // ------------ + + let sf = new Splitfinder(); + sf.allsplits = allsplits; + sf.fulllength = fullpath.length; + + sf.run(); + + let result = ""; + let intresult = []; + + let progmap: {[_:number]: string} = {}; + let first = true; + let rc0: RoboCommand[] = []; + let rc1: RoboCommand[] = []; + let rc2: RoboCommand[] = []; + for(const comp of sf.results[0]) + { + let id = ""; + if (comp in progmap) id = progmap[comp]; + else + { + if (Object.entries(progmap).length==0) { id="A"; rc0 = splitmap[comp][0]; } + else if (Object.entries(progmap).length==1) { id="B"; rc1 = splitmap[comp][0]; } + else if (Object.entries(progmap).length==2) { id="C"; rc2 = splitmap[comp][0]; } + progmap[comp] = id; + } + + if (!first) { result += ","; intresult.push(44); } + result += id; + intresult.push(id.charCodeAt(0)); + first = false; + } + + result += "\n"; + intresult.push(10); + + let str0 = rc0.map(p => p.toString()).reduce((a,b)=>a+","+b); + result += str0+"\n"; + str0.split('').forEach(p => intresult.push(p.charCodeAt(0))); + intresult.push(10); + + let str1 = rc1.map(p => p.toString()).reduce((a,b)=>a+","+b); + result += str1+"\n"; + str1.split('').forEach(p => intresult.push(p.charCodeAt(0))); + intresult.push(10); + + let str2 = rc2.map(p => p.toString()).reduce((a,b)=>a+","+b); + result += str2+"\n"; + str2.split('').forEach(p => intresult.push(p.charCodeAt(0))); + intresult.push(10); + + result += live + "\n"; + intresult.push(live.charCodeAt(0)); + intresult.push(10); + + AdventOfCode.outputConsole(result); + + return intresult; + } + + class Splitfinder + { + allsplits: { [pos:number]: [ number, RoboCommand[], number[] ][] } = {}; // pos => [ id, path, starts ] + fulllength: number = 0; + + results: number[][] = []; + + path: number[] = []; + used_segments: [number, number][] = []; + position = 0; + + run() + { + // abort if we finished + if (this.position === this.fulllength) + { + this.results.push(Object.assign([], this.path)); + AdventOfCode.outputConsole(">>> " + this.path.map(p => "["+p.toString()+"]").reduce((a,b)=>a+","+b) ) + return; + } + + for(const seg of this.allsplits[this.position]) + { + // ======== add to used_segments ======== + let added = false; + for(let i=0; i p[1] > 0); + } + } + } + } + + function findInPath(fullpath: RoboCommand[], start: number, len: number): number[] + { + let r = [start]; + for (let s = start+len; s<=fullpath.length-len;s++) + { + if (isPathEqual(fullpath, start, s, len)) r.push(s); + } + return r; + } + + function isPathEqual(path: RoboCommand[], start1: number, start2: number, len: number) + { + for (let i=0; i 0) result.push(new RoboCommand(RoboCommandType.Move, moves)); + moves = 0; + result.push(new RoboCommand(RoboCommandType.Left, 1)); + + dir = turnLeft(dir); + } + else if (isScaffolding(map, moveDir(pos, turnRight(dir)))) + { + if (moves > 0) result.push(new RoboCommand(RoboCommandType.Move, moves)); + moves = 0; + result.push(new RoboCommand(RoboCommandType.Right, 1)); + + dir = turnRight(dir); + } + else + { + if (moves > 0) result.push(new RoboCommand(RoboCommandType.Move, moves)); + + break; + } + } + + AdventOfCode.outputConsole(result.map(p => p.toString()).reduce((a,b)=> a+","+b)); + + return result; + } + + function moveDir(pos: [number, number], dir: Direction): [number, number] + { + return move(pos, dirToVec(dir)) + } + + function move(pos: [number, number], vec: [number, number]): [number, number] + { + return [ pos[0]+vec[0], pos[1]+vec[1] ]; + } + + function isScaffolding(map: {[_:number]: number}, pos: [number, number]) + { + let id = (pos[1]*1_0000 + pos[0]); + return map[id] === 35; + } + + class RoboCommand + { + cmd: RoboCommandType = RoboCommandType.Move; + len: number = 0; + + constructor(c: RoboCommandType, l: number) + { + this.cmd = c; + this.len = l; + } + + eq(o: RoboCommand) + { + if (this.cmd !== o.cmd) return false; + if (this.len !== o.len) return false; + return true; + } + + toString(): string + { + if (this.cmd === RoboCommandType.Left) return "L"; + if (this.cmd === RoboCommandType.Right) return "R"; + if (this.cmd === RoboCommandType.Move) return ""+this.len; + + throw "??"; + } + } + + enum RoboCommandType { Left, Right, Move } + + enum Direction + { + North = 1, + South = 2, + West = 3, + East = 4, + } + + function dirToVec(d: Direction): [number, number] + { + if (d === Direction.North) return [0, -1]; + if (d === Direction.East) return [+1, 0]; + if (d === Direction.South) return [0, +1]; + if (d === Direction.West) return [-1, 0]; + + throw "-.-"; + } + + function turnRight(d: Direction): Direction + { + if (d === Direction.North) return Direction.East; + if (d === Direction.East) return Direction.South; + if (d === Direction.South) return Direction.West; + if (d === Direction.West) return Direction.North; + + throw "dr"; + } + + function turnLeft(d: Direction): Direction + { + if (d === Direction.North) return Direction.West; + if (d === Direction.East) return Direction.North; + if (d === Direction.South) return Direction.East; + if (d === Direction.West) return Direction.South; + + throw "dl"; + } + + async function getMap(code: number[]): Promise<[{[_:number]: number}, number, number]> + { + let rnr = new Interpreter(code, []); + + let map: {[_:number]: number} = {}; + let maxx = 0; + let maxy = 0; + let out = ""; + let ipx=0; + let ipy=0 + while (!rnr.is_halted) + { + rnr.singleStep(); + if (rnr.output.length>0) + { + const v = rnr.output.pop()!; + out += String.fromCharCode(v); + //await AdventOfCode.outputIntermed(out); + + if (v === 10) + { + ipy++; + ipx=0; + } + else + { + // AdventOfCode.outputConsole(`# [${ipx}|${ipy}] = ${v}`); + + map[ipy*1_0000 + ipx] = v; + ipx++; + + maxx = Math.max(maxx, ipx); + maxy = Math.max(maxy, ipy); + } + } + } + + await AdventOfCode.outputIntermed(out); + + for(let yy=-1;yy<=maxy+1;yy++) + for(let xx=-1;xx<=maxx+1;xx++) + { + if (!((yy*1_0000 + xx) in map)) map[yy*1_0000 + xx] = 46; + } + + return [map, maxx, maxy]; + } + + class Interpreter + { + program: InfMem; + inputqueue: number[]; + instructionpointer: number; + output: number[]; + relative_base: number; + + is_halted: boolean = false; + + constructor(prog: number[], input: number[]) + { + this.program = new InfMem(prog); + this.inputqueue = input; + this.instructionpointer = 0; + this.output = []; + this.relative_base = 0; + } + + fullRun() : number[] + { + while(!this.is_halted) + { + const r = this.singleStep(); + + if (r === StepResult.EXECUTED) continue; + if (r === StepResult.HALTED) return this.output; + if (r === StepResult.WAITING_FOR_IN) throw "not enough input"; + + throw "unknown output of singleStep"; + } + + return this.output; + } + + autoRun() : StepResult + { + while(!this.is_halted) + { + const r = this.singleStep(); + + if (r === StepResult.EXECUTED) continue; + if (r === StepResult.HALTED) return StepResult.HALTED; + if (r === StepResult.WAITING_FOR_IN) return StepResult.WAITING_FOR_IN; + + throw "unknown output of singleStep"; + } + + return StepResult.HALTED; + } + + singleStep() : StepResult + { + const cmd = new Op(this.program.r(this.instructionpointer)); + + if (cmd.opcode == OpCode.ADD) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 + p1; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.MUL) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 * p1; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.HALT) + { + this.is_halted = true; + return StepResult.HALTED; + } + else if (cmd.opcode == OpCode.IN) + { + if (this.inputqueue.length == 0) return StepResult.WAITING_FOR_IN; + + const pv = this.inputqueue[0]; + cmd.setParameter(this, 0, pv); + this.inputqueue = this.inputqueue.slice(1); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.OUT) + { + const p0 = cmd.getParameter(this, 0); + this.output.push(p0); + //AdventOfCode.outputConsole("# " + p0); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.TJMP) + { + const p0 = cmd.getParameter(this, 0); + if (p0 != 0) this.instructionpointer = cmd.getParameter(this, 1); + else this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.FJMP) + { + const p0 = cmd.getParameter(this, 0); + if (p0 == 0) this.instructionpointer = cmd.getParameter(this, 1); + else this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.LT) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 < p1 ? 1 : 0; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.EQ) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 == p1 ? 1 : 0; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.ARB) + { + const p0 = cmd.getParameter(this, 0); + this.relative_base = this.relative_base+p0; + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else throw "Unknown Op: " + cmd.opcode + " @ " + this.instructionpointer; + } + + private incInstrPtr(cmd: Op) + { + this.instructionpointer += 1 + cmd.parametercount; + } + } + + enum StepResult { EXECUTED, HALTED, WAITING_FOR_IN } + + enum OpCode + { + ADD = 1, + MUL = 2, + IN = 3, + OUT = 4, + TJMP = 5, + FJMP = 6, + LT = 7, + EQ = 8, + ARB = 9, + HALT = 99, + } + + enum ParamMode + { + POSITION_MODE = 0, + IMMEDIATE_MODE = 1, + RELATIVE_MODE = 2, + } + + class Op + { + opcode: OpCode; + modes: ParamMode[]; + + name: string; + parametercount: number; + + constructor(v: number) + { + this.opcode = v%100; + v = Math.floor(v/100); + this.modes = []; + for(let i=0; i<4; i++) + { + this.modes.push(v%10); + v = Math.floor(v/10); + } + + if (this.opcode == OpCode.ADD) { this.name="ADD"; this.parametercount=3; } + else if (this.opcode == OpCode.MUL) { this.name="MUL"; this.parametercount=3; } + else if (this.opcode == OpCode.HALT) { this.name="HALT"; this.parametercount=0; } + else if (this.opcode == OpCode.IN) { this.name="IN"; this.parametercount=1; } + else if (this.opcode == OpCode.OUT) { this.name="OUT"; this.parametercount=1; } + else if (this.opcode == OpCode.TJMP) { this.name="TJMP"; this.parametercount=2; } + else if (this.opcode == OpCode.FJMP) { this.name="FJMP"; this.parametercount=2; } + else if (this.opcode == OpCode.LT) { this.name="LT"; this.parametercount=3; } + else if (this.opcode == OpCode.EQ) { this.name="EQ"; this.parametercount=3; } + else if (this.opcode == OpCode.ARB) { this.name="ARB"; this.parametercount=1; } + else throw "Unknown opcode: "+this.opcode; + } + + getParameter(proc: Interpreter, index: number): number + { + const prog = proc.program; + const ip = proc.instructionpointer; + + let p = prog.r(ip+1+index); + + if (this.modes[index] == ParamMode.POSITION_MODE) p = prog.r(p); + else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) p = p; + else if (this.modes[index] == ParamMode.RELATIVE_MODE) p = prog.r(proc.relative_base+p); + else throw "Unknown ParamMode: "+this.modes[index]; + + return p; + } + + setParameter(proc: Interpreter, index: number, value: number): void + { + const prog = proc.program; + const ip = proc.instructionpointer; + + let p = prog.r(ip+1+index); + + if (this.modes[index] == ParamMode.POSITION_MODE) prog.w(p, value); + else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) throw "Immediate mode not allowed in write"; + else if (this.modes[index] == ParamMode.RELATIVE_MODE) prog.w(proc.relative_base+p, value); + else throw "Unknown ParamMode: "+this.modes[index]; + } + } + + class InfMem + { + private data: { [_:number]:number } = {}; + + constructor(v: number[]) + { + for(let i=0; i ####### +##...## ##@#@## +#cB#Ab# #cB#Ab# +####### ####### + +Because some of the keys are for doors in other vaults, it would take much too long to collect all of the keys by yourself. Instead, you deploy four remote-controlled robots. Each starts at one of the entrances (@). + +Your goal is still to collect all of the keys in the fewest steps, but now, each robot has its own position and can move independently. You can only remotely control a single robot at a time. Collecting a key instantly unlocks any corresponding doors, regardless of the vault in which the key or door is found. + +For example, in the map above, the top-left robot first collects key a, unlocking door A in the bottom-right vault: + +####### +#@.#Cd# +##.#@## +####### +##@#@## +#cB#.b# +####### + +Then, the bottom-right robot collects key b, unlocking door B in the bottom-left vault: + +####### +#@.#Cd# +##.#@## +####### +##@#.## +#c.#.@# +####### + +Then, the bottom-left robot collects key c: + +####### +#@.#.d# +##.#@## +####### +##.#.## +#@.#.@# +####### + +Finally, the top-right robot collects key d: + +####### +#@.#.@# +##.#.## +####### +##.#.## +#@.#.@# +####### + +In this example, it only took 8 steps to collect all of the keys. + +Sometimes, multiple robots might have keys available, or a robot might have to wait for multiple keys to be collected: + +############### +#d.ABC.#.....a# +######@#@###### +############### +######@#@###### +#b.....#.....c# +############### + +First, the top-right, bottom-left, and bottom-right robots take turns collecting keys a, b, and c, a total of 6 + 6 + 6 = 18 steps. Then, the top-left robot can access key d, spending another 6 steps; collecting all of the keys here takes a minimum of 24 steps. + +Here's a more complex example: + +############# +#DcBa.#.GhKl# +#.###@#@#I### +#e#d#####j#k# +###C#@#@###J# +#fEbA.#.FgHi# +############# + + Top-left robot collects key a. + Bottom-left robot collects key b. + Top-left robot collects key c. + Bottom-left robot collects key d. + Top-left robot collects key e. + Bottom-left robot collects key f. + Bottom-right robot collects key g. + Top-right robot collects key h. + Bottom-right robot collects key i. + Top-right robot collects key j. + Bottom-right robot collects key k. + Top-right robot collects key l. + +In the above example, the fewest steps to collect all of the keys is 32. + +Here's an example with more choices: + +############# +#g#f.D#..h#l# +#F###e#E###.# +#dCba@#@BcIJ# +############# +#nK.L@#@G...# +#M###N#H###.# +#o#m..#i#jk.# +############# + +One solution with the fewest steps is: + + Top-left robot collects key e. + Top-right robot collects key h. + Bottom-right robot collects key i. + Top-left robot collects key a. + Top-left robot collects key b. + Top-right robot collects key c. + Top-left robot collects key d. + Top-left robot collects key f. + Top-left robot collects key g. + Bottom-right robot collects key k. + Bottom-right robot collects key j. + Top-right robot collects key l. + Bottom-left robot collects key n. + Bottom-left robot collects key m. + Bottom-left robot collects key o. + +This example requires at least 72 steps to collect all keys. + +After updating your map and using the remote-controlled robots, what is the fewest steps necessary to collect all of the keys? diff --git a/www/statics/aoc/2019/18_input.txt b/www/statics/aoc/2019/18_input.txt new file mode 100644 index 0000000..123b7c7 --- /dev/null +++ b/www/statics/aoc/2019/18_input.txt @@ -0,0 +1,81 @@ +################################################################################# +#........h#..........t..#...#.#e........#.............#.....#....g..#...........# +#.#######.#.#####.#####.#.#.#.#.#.#####.#.###########.#.#.###.#####.#.#.#.#####.# +#.#.....#.#.#.#...#.#...#.#.#...#.#.#...#...#.....#...#.#.....#..l#...#.#.#...#.# +#.#.###.#.#.#.#.###.#.###.#.#####.#.#.###.#.#.###.###.#####.###.#.#####.###.#.#.# +#.#.#.#.#.....#.#.........#.......#...#.#.#.#.#.#...#.....#...#.#.....#.....#.#.# +#.#.#.#.#######.###################.###.#.#.#.#.###.#####.###.#.#####.#######.#.# +#.#.#.#...#...#.........#.....#...#.#...#.#.......#.#.....#...#.#...#...#.....#.# +###I#.###.#.#.#########.#.###.#.#.#.#.#.#########.#.#####.#.###.#.#.###.#.#####.# +#...#...#.#.#.........#.#.#.#...#...#.#.#.....#...#.....#.#.#...#.#...#...#...#.# +#.###.#.#.#.#########.#.#.#.#########.#.#Q###.#.#######.#.###.#######.#######.#.# +#...#.#.....#...#.......#.#...........#.#.#...#.....#...#.....#.....#.........#.# +###.#########.#.#########.#####.###.###.#.#.#########.#######.#.###.###.###.###.# +#...#...#...#.#.........#.....#.#...#...#.#.........#.#...#...#.#.#...#.#.#.#...# +#.###.#.#.#.#.#########.#####.#.#.#####.#.#########.#.#.#.#.###.#.###.#.#.#.#.### +#.....#...#.#...#.....#...#.#.#.#.#...#.#...#.....#...#.#.#.#...#.....#...#...#.# +#.#########.###.#.#######.#.#.#.#.#.#.#.#.#.#.#########.###.#.#.#####.###.#####.# +#.#.....#.#.#.....#.......#...#.#...#.#.#.#.#.........#.....#.#.#...#.#.......#.# +#Z#.#.#.#.#.#.#####.#######.###C#####.#.###.#.#######.#.#####.###.#.#.#######.#.# +#.#.#.#...#.....#.D.#.....#.#.......#.#.#...#.#...#.#...#...#...#.#.#.....#...#.# +#.###.###.#######.###S###.#.#########.###.###.#.#.#.#####.#####.#.#.#####.###.#.# +#.#..u#...#j#...#...#.#...#.........#...#.#.#...#...#.......#.#...#.....#...#.#.# +#.#.###.###.#.#.#.#.###.###########.###.#.#.#######.#######.#.#.#######.###.#.#.# +#...#.......#r#.#.#...#...#.......#.....#.#...#...#.........#.#.......#.#.#.#...# +#.#####.#####.#.#.#.#.###.#.###.#.#####.#.###.#.#.###.#######.#########.#.#.###.# +#.#...#.#...#.#.#.#.#...#.#...#.#.#.....#...#...#.....#.....#...........#...#...# +###.#.###.#.#.#.###.#####.###.#.###.#####.#.#####.#####.###.#############.#####.# +#...#.....#...#...W.#...#s..#.#.....#...#.#.....#.#.....#..y..#.....#.....#...#.# +#F#################.#.#.###.#.#######.#.#.#####.###.#####.###.#.###.#.#####.#.#.# +#.........#.........#.#.B...#...#...#.#.#.....#.....#.#...#...#.#.#.#.......#.#.# +#####.###.###########.#########.#.#.#.#.#####.#######.#.#######.#.#.#########.### +#...#..v#...#.........#.......#...#.#.#.#.....#a......#...........#.#.....#.#...# +#.#.#######.#.#########.#####.#####.#.#.#.###########.#############.#.#.#.#.###.# +#.#.........#.#...#...#.....#.#...#.#.#.#.......#.....#..m......#...#.#.#...#...# +#.###########.#.#.###.#####.#.#.#.#.#.#########.#.###.#.#######.#.#.#.#.#####.### +#p....#n......#.#.........#.#...#...#...#.....#...#...#.#.......#.#.#.#x#.....#.# +#.###.#.#######.#########.#.#########.#.#.#.#.#####.#####.#####.#.###.#.#.#####.# +#.#.#.#.#.....#.....O...#.#.#.......#.#.#.#.#...#...#...#...#.#.#.#...#...#.....# +#.#.#.#.#.#####.#######.###.#####.#.###.#.#.#####.###.#.###.#.#.#.#.#########.#.# +#...#...#.............#...........#.......#..o........#.....#...#.............#.# +#######################################.@.####################################### +#q......M.............#.....#.....#.#.........#...#..w..........#.#.....#.......# +#######.#####.#######.#.#.#.#.###.#.#.#.#.###.###.#.###########.#.#.#.#.#####.#.# +#.....#...#...#.....#.#.#.#.#.#.#.#...#.#...#.....#...#...#...#.#...#.#...J...#.# +#.###.#####.###.#####.#.#.#.#.#.#.#####.###.#####.###.#.#.#.#.#.#####.#########.# +#.#.#.#.....#.......#.#.#.#...#.#.......#...#.#...#.#...#...#.#.......#.....#.#.# +#.#.#.#.###########.#.###.#####.#######.#.###.#.###.#########.#######.#.###V#.#.# +#.#...#.#...........#.....#.......#.....#.#...#.#.....#.#.R...#...#.#.#.#...#.#.# +#.#.###.#.###.#####.#######.###.#.#.#####.#.###.#.###.#.#.###.#.#.#.#.#.#.###U#.# +#.#.....#...#...#.#.#.......#.#.#.#.#...#.#...#...#.#.#.#...#.#.#.#..d#.#...#...# +#.#########.###.#.#.#.#######.#.#.#.###.#.#.#.#####.#.#.###.###.#.#####.###.#.### +#.#...#.......#.#...#...#.....#.#.#.#...#.#.#.#...........#....f#.......#.P.#...# +#.#.#.#########.#.#####.#.#####.#.#.#.###.#.#.#.#######.###########.#####.#####.# +#.Y.#.......#...#.....#.#...#...#.#.#...#.#.#...#.#...#.#.....#...#...#...#...#.# +#.#########.#.#####.###.###.#.###.#.###.#.#.#####.#.#.###.###.#.#.#####.###.###.# +#.#...#.....#.#.....#...#...#...#.#...#.#.#.#.....#.#.....#...#.#.......#...#...# +#.#.#.#.#####.#.#####.###.#.###.#####.#.#.#.#.###.#.#######.###N#########.###.### +#...#.#.#...#.#.....#...#.#...#.....#.#.#.#...#...#...#.....#...#.#.........#...# +#####.#.#.#.#.#########.#.###.#####.#.#.#.#####.#####.#.###.#.###.#.#######.###.# +#...#.#...#.#.............#.....#...#...#.#...#...#...#...#.#.#...#...#...#..z#.# +#K###.#####.#.###################.#####.#.#.#.###.#.#####.#.#.#.#.###.#.#.#.#.#.# +#.....#...#.#.#.....#.....#.....#.....#.#.#.#...#.#...#...#.#b#.#...#...#.#.#.#.# +#####.#.###.#.#####.#####.#.###.#####.#.#.#.###.###.###.###.#######.#####.#.###.# +#...#.#.#...#...#.......#...#.#.....#.#.#...#.#.#...#...#.........#...#...#.....# +#.#.#.#.#.#####.#.#####.#####.#####.#.#.#.###.#.#.###.###########.#.#.#.######### +#.#.#...#.#...#...#...#...#.#...#...#...#.#.A.#.....#.....#..k..#.#.#.#.#.......# +#.#.#####.###.#####.#####.#.#.#.#.###.###.###.#######.###.#####.#.#.###.#.####### +#.#.......#.......#.....#.#...#.#...#...#.......#...#.#.#.....#...#.#...#.......# +#X#########.#####.#.###.#.#####.###.#####.#######.#.#.#.#####.#.###.#.###.#####.# +#.....#.......#...#.#.#.#...#.....#.....#.#.......#.#...#.....#...#.#...#.....#.# +#####.#.#####.#.###.#.#.###.#.#########.###.#######.###.#.#######.#.###.#.#####.# +#.....#...#...#...#.#.#.....#.#...#.....#...#.....#.#...#.#.#.....#...#.#.#...#.# +#.#######.#.#######.#.#######.#.#.#.#####.#####.###.#####.#.#.#####.#.#.###.#.#.# +#.......#.#.............#.....#.#...#...#.....#.......#...#...#.....#.#.....#.#.# +#.#####.###############.#.###.#.#####.#.#.###.#######.#.###.#######.#.#######.#.# +#.#.....#...T.....#...#.#.#...#.....#.#.#...#.......#...#......c....#.......#...# +###.###.#.#######.#H#.#.#.#.#######.#.#####.#######.#####.#####.###############.# +#...#...#.#.#.....#.#.#...#...#.....#...#...#.E...#...#...#...#.#.......#.......# +#.#######.#.#.#####.#.#########.#######.#.###.###.###.#####.#.###.#####.#.####### +#.....L.....#.......#.............G..i..#.....#.....#.......#.........#.........# +################################################################################# diff --git a/www/statics/aoc/2019/18_solution-1.ts b/www/statics/aoc/2019/18_solution-1.ts new file mode 100644 index 0000000..febfe2f --- /dev/null +++ b/www/statics/aoc/2019/18_solution-1.ts @@ -0,0 +1,404 @@ +namespace AdventOfCode2019_18_1 +{ + const DAY = 18; + const PROBLEM = 1; + + export async function run() + { + let input = await AdventOfCode.getInput(DAY); + if (input == null) return; + + AdventOfCode.setIntermedOutputSize("0.46vw"); + + let grid = input + .trim() + .split(new RegExp('\r?\n')) + .filter(p => p.trim().length > 0) + .map(p => p.trim().split('').map(q => q.charCodeAt(0)) ); + + + let str = ""; + for(let line of grid) + { + str += line.map(p => + { + if (p == 46) return " "; + if (p == 35) return "\u2591"; + return String.fromCharCode(p); + }).reduce((a,b)=>a+b)+"\n"; + await AdventOfCode.outputIntermed(str); + } + + let map = new AOCMap(grid); + + await map.dump(); + + await createReachabilityMap(map, "@", map.start, 0); + for(let i=0;i<26;i++) await createReachabilityMap(map, String.fromCharCode(i+97), map.keys[i], 1); + + await map.dump(); + + let best_path = (await findPaths(map))!; + + await map.dump(); + + AdventOfCode.outputConsole(best_path[0] + " (" + map.getPathLength(best_path[1]) + ") --> " + best_path[1]); + + AdventOfCode.output(DAY, PROBLEM, best_path[0].toString()); + } + + async function createReachabilityMap(map: AOCMap, letter:string, pos: [number, number], dumpmode: number) + { + let quadrant: {[_:string]: [number, number]} = {}; + for(let i=0;i<26;i++) + { + quadrant[String.fromCharCode(i+97)] = [ Math.sign(map.keys[i][0] - map.start[0]), Math.sign(map.keys[i][1] - map.start[1]) ]; + } + + const no_doors = new Array(26).fill(false); + map.reachability.set("@>@", [0, no_doors]); + for(let i=0;i<26;i++) map.reachability.set(String.fromCharCode(i+97)+">"+String.fromCharCode(i+97), [0, no_doors]); + + let visited_map: { [_:number]: number } = {}; + + let next: [number, number, boolean[], string[] ][] = []; // x, y, currently_passed_doors, currently_found_keys + next.push([pos[0], pos[1], no_doors, ['@']]); + + let counter = 0; + + if (dumpmode===0 || dumpmode===1) await map.dumpWithFillAndNext(visited_map, next); + + for(;;) + { + let ls = next.slice(); + next = []; + + let updates = 0; + for(let pos of ls) + { + const x = pos[0]; + const y = pos[1]; + + let currently_passed_doors: boolean[] = pos[2]; + let currently_found_keys: string[] = pos[3]; + + const i = (y*10000000 + x); + if (i in visited_map) { if (visited_map[i] < counter && visited_map[i]>4) throw "loop in map :("; continue; } + + visited_map[i] = counter; + updates++; + + if (map.iskey([x,y])) + { + const key1 = letter; + const key2 = String.fromCharCode(map.get([x, y])); + + { + let dist = counter; + const keys = currently_passed_doors; + + //if (key1 !== "@" && key2 !== "@") + //{ + // const q1 = quadrant[key1]; + // const q2 = quadrant[key2]; +// + // if (q1[0]*q2[0]*q1[1]*q2[1] === -1) dist -= 2; // big middle area + //} + + + map.reachability.set(key1+">"+key2, [ dist, keys ]); + map.reachability.set(key2+">"+key1, [ dist, keys ]); + + AdventOfCode.outputConsole("Add reachability ("+key1+" <-> " + key2 + ") == " + dist); + + if (dumpmode===1) await map.dumpWithFillAndNext(visited_map, next); + } + + currently_found_keys = currently_found_keys.slice(); + currently_found_keys.push(key2); + } + else if (map.isdoor([x,y])) + { + const v = map.get([x, y]); + currently_passed_doors = currently_passed_doors.slice(); + currently_passed_doors[v-65] = true; + } + + if (map.iswalkable([x-1, y]) && !(((y )*10000000 + (x-1)) in visited_map)) next.push([x-1, y, currently_passed_doors, currently_found_keys]); + if (map.iswalkable([x+1, y]) && !(((y )*10000000 + (x+1)) in visited_map)) next.push([x+1, y, currently_passed_doors, currently_found_keys]); + if (map.iswalkable([x, y-1]) && !(((y-1)*10000000 + (x )) in visited_map)) next.push([x, y-1, currently_passed_doors, currently_found_keys]); + if (map.iswalkable([x, y+1]) && !(((y+1)*10000000 + (x )) in visited_map)) next.push([x, y+1, currently_passed_doors, currently_found_keys]); + } + + if (dumpmode===0) await map.dumpWithFillAndNext(visited_map, next); + + if (updates === 0) break; + + counter++; + } + + await map.dumpWithFill(visited_map); + + return; + } + + async function findPaths(map: AOCMap): Promise<[number, string]|null> + { + let queue: [string, boolean[], number, number, string][] = []; // < pos, keys, key_count, path_len, path > + + queue.push(["@", new Array(26).fill(false), 0, 0, ""]); + + let best_result: [number, string]|null = null; + + let seen = new Set(); + + for(let ctr=1;;ctr++) + { + + let [ pos, keys, key_count, path_len, path ] = queue.shift()!; + + let hskey = pos+keys.map(p=>p?1:0).join()+""+path_len; + if (seen.has(hskey)) continue; + seen.add(hskey); + + + if (ctr % 100 === 0) AdventOfCode.outputConsole("[INTERMED] ["+path_len+"] " + path); + if (ctr % 500 === 0) await AdventOfCode.sleep(0); + + if (key_count === 26) + { + if (best_result === null || best_result[0]>path_len) + { + best_result = [path_len, path]; + AdventOfCode.outputConsole("[RESULT] ["+path_len+"] ==== " + path); + //return [path_len, path]; + } + } + + if (best_result !== null && best_result[0] < path_len) return best_result; + + let reachable = "abcdefghijklmnopqrstuvwxyz" + .split('') + .filter(p => !keys[p.charCodeAt(0)-97]) + .filter(p => map.isreachable(pos, p, keys)) + .map(p => [p, map.reachability.get(pos+">"+p)![0]] as [string, number] ) + .sort((a,b) => a[1] - b[1]) + ; + + for(const [nextkey, steplen] of reachable) + { + let keys_copy = keys.slice(); + keys_copy[nextkey.charCodeAt(0)-97]=true; + + if (best_result!==null && best_result[0] < path_len+steplen) continue; + + array_insert(queue, [nextkey, keys_copy, key_count+1, path_len+steplen, path+nextkey]); + //queue.push([nextkey, keys_copy, key_count+1, path_len+steplen, path+nextkey]); + //queue.sort((a, b) => b[3] - a[3] ); + } + + if (queue.length===0) break; + } + + return best_result; + + } + + function array_insert(array: [string, boolean[], number, number, string][], element: [string, boolean[], number, number, string]) + { + function sortedIndex(array: [string, boolean[], number, number, string][], value: [string, boolean[], number, number, string]) + { + var low = 0, high = array.length; + + while (low < high) { + var mid = (low + high) >>> 1; + if (array[mid][3] < value[3]) low = mid + 1; + else high = mid; + } + return low; + } + + array.splice(sortedIndex(array, element) + 1, 0, element); + return array; + } + + function key_except(base: boolean[], exc: boolean[]) + { + let r = base; + let cp = false; + for(let i=0; i<26; i++) + { + if (exc[i] && base[i]) + { + if (!cp) {r = base.slice(); cp=true; } + r[i]=false; + } + } + return r; + } + + function key_combine(a: boolean[], b: boolean[]) + { + let r = a; + let cp = false; + for(let i=0; i<26; i++) + { + if (b[i] && !r[i]) + { + if (!cp) {r = a.slice(); cp=true; } + r[i]=true; + } + } + return r; + } + + class AOCMap + { + grid: number[][]; + width: number; + height: number; + start: [number, number]; + doors: [number, number][]; + keys: [number, number][]; + + reachability: Map = new Map(); // [x;y] => [length;keys] + + constructor(g: number[][]) + { + this.grid = g; + this.width = g[0].length; + this.height = g.length; + this.doors = new Array(26); + this.keys = new Array(26); + this.start = [-1, -1]; + + for(let y=0; y= "A".charCodeAt(0) && v.charCodeAt(0) <= "Z".charCodeAt(0)) + { + this.doors[v.charCodeAt(0) - "A".charCodeAt(0)] = [x,y]; + continue; + } + + if (v.charCodeAt(0) >= "a".charCodeAt(0) && v.charCodeAt(0) <= "z".charCodeAt(0)) + { + this.keys[v.charCodeAt(0) - "a".charCodeAt(0)] = [x,y]; + continue; + } + + throw "input:"+v+":"; + } + } + + getPathLength(path: string): number + { + path = "@" + path; + + let sum = 0; + for (let i=1; i"+path.charAt(i))![0]; + } + return sum; + } + + isreachable(p0: string, p1: string, keys: boolean[]): boolean + { + const k_needed = this.reachability.get(p0+">"+p1)![1]; + for(let i=0; i<26; i++) + { + if (k_needed[i] && !keys[i]) return false; + } + return true; + } + + get(pos: [number, number]): number { + return this.grid[pos[1]][pos[0]]; + } + + iswalkable(pos: [number, number]): boolean { + const v = this.get(pos); + return (v !== 35 && v !== 64); // # && @ + } + + iskey(pos: [number, number]): boolean { + const v = this.get(pos); + return (v >= 97 && v <= 122); // a-z + } + + isdoor(pos: [number, number]): boolean { + const v = this.get(pos); + return (v >= 65 && v <= 90); // A-Z + } + + async dump() + { + let str = ""; + for(let y=0; y p[0]==x && p[1]==y)>=0) str += "\u25cb" + else if (i in visited_map) str += "\u25cf" + else + { + let v = this.get([x, y]); + if (v == 46) str += " "; + else if (v == 35) str += "\u2591"; + else str += String.fromCharCode(v); + } + } + str += "\n"; + } + await AdventOfCode.outputIntermed(str); + } + } +} diff --git a/www/statics/aoc/2019/18_solution-2.ts b/www/statics/aoc/2019/18_solution-2.ts new file mode 100644 index 0000000..7528224 --- /dev/null +++ b/www/statics/aoc/2019/18_solution-2.ts @@ -0,0 +1,226 @@ +namespace AdventOfCode2019_18_2 +{ + const DAY = 18; + const PROBLEM = 2; + + type P = [number, number]; + type PSet = [P, P, P, P]; + + export async function run() + { + let input = await AdventOfCode.getInput(DAY); + if (input == null) return; + + AdventOfCode.setIntermedOutputSize("0.46vw"); + + let grid = input + .trim() + .split(new RegExp('\r?\n')) + .filter(p => p.trim().length > 0) + .map(p => p.trim().split('').map(q => q.charCodeAt(0)) ); + + grid[40][40] = "#".charCodeAt(0); + + grid[39][40] = "#".charCodeAt(0); + grid[41][40] = "#".charCodeAt(0); + grid[40][39] = "#".charCodeAt(0); + grid[40][41] = "#".charCodeAt(0); + + let keys = grid.flatMap(p => p.filter(q => q>=97 && q<=122)); + + await AdventOfCode.outputIntermed( grid.map(p => p.map(q=>String.fromCharCode(q)).join("") ).join("\n") ); + + let dictionary: {[_:number]: ReachableKey[] } = {}; + + dictionary[id([39,39])] = ReachableKeys(grid, [39,39], ""); + dictionary[id([41,39])] = ReachableKeys(grid, [41,39], ""); + dictionary[id([39,41])] = ReachableKeys(grid, [39,41], ""); + dictionary[id([41,41])] = ReachableKeys(grid, [41,41], ""); + + for (let k of keys) + { + const pos = FindPositionOf(grid, k); + dictionary[id(pos)] = ReachableKeys(grid, pos, ""); + } + + let positions: {[_:number]: P} = {}; + for (let k of keys) { positions[k] = FindPositionOf(grid, k); } + + let minimumSteps = CollectKeys(grid, dictionary, positions, [[39,39], [41,39], [39,41], [41,41]]) + + AdventOfCode.output(DAY, PROBLEM, minimumSteps.toString()); + } + + function CollectKeys(grid: number[][], keyPaths: {[_:number]: ReachableKey[]}, positions: {[_:number]: P}, pos: P[]) + { + let currentMinimum = Number.MAX_SAFE_INTEGER; + + let startingSet: PSet = [ pos[0], pos[1], pos[2], pos[3] ]; + + let q: State[] = []; + q.unshift(new State(startingSet, 0, 0)); + + let visited = new Map(); + let finishValue = 0; + for (var i = 0; i < Object.entries(positions).length; ++i) + { + finishValue = finishValue | Math.floor(Math.pow(2, i)); + } + + while (q.length>0) + { + let state = q.pop()!; + + let valueTupleKey = state.strPositionsOwnedKeys(); + if (valueTupleKey in visited) + { + let steps = visited.get(valueTupleKey)!; + + if (steps <= state.Steps) continue; + + visited.set(valueTupleKey, state.Steps); + } + else + { + visited.set(valueTupleKey, state.Steps); + } + + if (state.OwnedKeys === finishValue) + { + currentMinimum = Math.min(currentMinimum, state.Steps); + continue; + } + + for (let i=0; i < 4; i++) + { + for (let k of keyPaths[id(state.Positions[i])]) + { + let ki = Math.floor(Math.pow(2, k.Key.charCodeAt(0) - "a".charCodeAt(0))); + if ((state.OwnedKeys & ki) === ki || (k.Obstacles & state.OwnedKeys) != k.Obstacles) continue; + + let newOwned = state.OwnedKeys | ki; + + var newPos = state.Positions.slice() as PSet; + newPos[i] = positions[k.Key.charCodeAt(0)]; + + q.unshift(new State(newPos, newOwned, state.Steps + k.Distance)); + } + } + } + + return currentMinimum; + } + + function id (p: P) { return p[1]*10000+p[0]; } + + function FindPositionOf(grid: number[][], needle: number): P + { + for(let y=0; y = new Set(); + + var q: P[] = []; + var s: number[] = []; + var o: number[] = []; + q.unshift(start); + s.unshift(0); + o.unshift(0); + + while (q.length>0) + { + const pos = q.pop()!; + const dist = s.pop()!; + let obst = o.pop()!; + + if (visited.has(id(pos))) continue; + visited.add(id(pos)); + + var c = map[pos[1]][pos[0]]; + + if (c>=97 && c<=122) // lower + { + let rk = new ReachableKey(); + rk.Distance = dist; rk.Key = String.fromCharCode(c); rk.Obstacles = obst; + list.push(rk); + pos_around(pos).forEach(p => + { + q.unshift(p); + s.unshift(dist+1); + o.unshift(obst); + }); + } + else if (c>=65 && c<=90) // upper + { + pos_around(pos).forEach(p => + { + q.unshift(p); + s.unshift(dist+1); + obst = obst | Math.floor(Math.pow(2, c-65)) + o.unshift(obst); + }); + } + else if (c === 46) // . + { + pos_around(pos).forEach(p => + { + q.unshift(p); + s.unshift(dist+1); + o.unshift(obst); + }); + } + else if (c === 35) {}// # + else throw "["+c+"]"; + } + + return list; + } + + function pos_around(pos: P): P[] + { + return [ + [pos[0], pos[1]-1], + [pos[0]-1, pos[1]], + [pos[0]+1, pos[1]], + [pos[0], pos[1]+1], + ]; + } + + class ReachableKey + { + Key: string = ""; + Distance: number = 0; + Obstacles: number = 0; + } +} diff --git a/www/statics/aoc/2019/19_challenge.txt b/www/statics/aoc/2019/19_challenge.txt new file mode 100644 index 0000000..e1096f3 --- /dev/null +++ b/www/statics/aoc/2019/19_challenge.txt @@ -0,0 +1,76 @@ +--- Day 19: Tractor Beam --- + +Unsure of the state of Santa's ship, you borrowed the tractor beam technology from Triton. Time to test it out. + +When you're safely away from anything else, you activate the tractor beam, but nothing happens. It's hard to tell whether it's working if there's nothing to use it on. Fortunately, your ship's drone system can be configured to deploy a drone to specific coordinates and then check whether it's being pulled. There's even an Intcode program (your puzzle input) that gives you access to the drone system. + +The program uses two input instructions to request the X and Y position to which the drone should be deployed. Negative numbers are invalid and will confuse the drone; all numbers should be zero or positive. + +Then, the program will output whether the drone is stationary (0) or being pulled by something (1). For example, the coordinate X=0, Y=0 is directly in front of the tractor beam emitter, so the drone control program will always report 1 at that location. + +To better understand the tractor beam, it is important to get a good picture of the beam itself. For example, suppose you scan the 10x10 grid of points closest to the emitter: + + X + 0-> 9 + 0#......... + |.#........ + v..##...... + ...###.... + ....###... +Y .....####. + ......#### + ......#### + .......### + 9........## + +In this example, the number of points affected by the tractor beam in the 10x10 area closest to the emitter is 27. + +However, you'll need to scan a larger area to understand the shape of the beam. How many points are affected by the tractor beam in the 50x50 area closest to the emitter? (For each of X and Y, this will be 0 through 49.) + +--- Part Two --- + +You aren't sure how large Santa's ship is. You aren't even sure if you'll need to use this thing on Santa's ship, but it doesn't hurt to be prepared. You figure Santa's ship might fit in a 100x100 square. + +The beam gets wider as it travels away from the emitter; you'll need to be a minimum distance away to fit a square of that size into the beam fully. (Don't rotate the square; it should be aligned to the same axes as the drone grid.) + +For example, suppose you have the following tractor beam readings: + +#....................................... +.#...................................... +..##.................................... +...###.................................. +....###................................. +.....####............................... +......#####............................. +......######............................ +.......#######.......................... +........########........................ +.........#########...................... +..........#########..................... +...........##########................... +...........############................. +............############................ +.............#############.............. +..............##############............ +...............###############.......... +................###############......... +................#################....... +.................########OOOOOOOOOO..... +..................#######OOOOOOOOOO#.... +...................######OOOOOOOOOO###.. +....................#####OOOOOOOOOO##### +.....................####OOOOOOOOOO##### +.....................####OOOOOOOOOO##### +......................###OOOOOOOOOO##### +.......................##OOOOOOOOOO##### +........................#OOOOOOOOOO##### +.........................OOOOOOOOOO##### +..........................############## +..........................############## +...........................############# +............................############ +.............................########### + +In this example, the 10x10 square closest to the emitter that fits entirely within the tractor beam has been marked O. Within it, the point closest to the emitter (the only highlighted O) is at X=25, Y=20. + +Find the 100x100 square closest to the emitter that fits entirely within the tractor beam; within that square, find the point closest to the emitter. What value do you get if you take that point's X coordinate, multiply it by 10000, then add the point's Y coordinate? (In the example above, this would be 250020.) diff --git a/www/statics/aoc/2019/19_input.txt b/www/statics/aoc/2019/19_input.txt new file mode 100644 index 0000000..7472a39 --- /dev/null +++ b/www/statics/aoc/2019/19_input.txt @@ -0,0 +1 @@ +109,424,203,1,21102,1,11,0,1105,1,282,21102,1,18,0,1105,1,259,2102,1,1,221,203,1,21101,31,0,0,1105,1,282,21101,38,0,0,1105,1,259,20102,1,23,2,21201,1,0,3,21102,1,1,1,21101,57,0,0,1106,0,303,2101,0,1,222,20101,0,221,3,21001,221,0,2,21101,259,0,1,21101,80,0,0,1106,0,225,21102,1,97,2,21102,1,91,0,1105,1,303,1201,1,0,223,20102,1,222,4,21101,259,0,3,21101,225,0,2,21102,1,225,1,21102,1,118,0,1106,0,225,21001,222,0,3,21102,1,21,2,21102,133,1,0,1106,0,303,21202,1,-1,1,22001,223,1,1,21101,0,148,0,1105,1,259,1201,1,0,223,20101,0,221,4,21001,222,0,3,21101,14,0,2,1001,132,-2,224,1002,224,2,224,1001,224,3,224,1002,132,-1,132,1,224,132,224,21001,224,1,1,21101,195,0,0,106,0,109,20207,1,223,2,20102,1,23,1,21102,-1,1,3,21101,0,214,0,1106,0,303,22101,1,1,1,204,1,99,0,0,0,0,109,5,2101,0,-4,249,21201,-3,0,1,21202,-2,1,2,21201,-1,0,3,21101,0,250,0,1106,0,225,22101,0,1,-4,109,-5,2105,1,0,109,3,22107,0,-2,-1,21202,-1,2,-1,21201,-1,-1,-1,22202,-1,-2,-2,109,-3,2105,1,0,109,3,21207,-2,0,-1,1206,-1,294,104,0,99,21202,-2,1,-2,109,-3,2105,1,0,109,5,22207,-3,-4,-1,1206,-1,346,22201,-4,-3,-4,21202,-3,-1,-1,22201,-4,-1,2,21202,2,-1,-1,22201,-4,-1,1,22102,1,-2,3,21101,0,343,0,1105,1,303,1105,1,415,22207,-2,-3,-1,1206,-1,387,22201,-3,-2,-3,21202,-2,-1,-1,22201,-3,-1,3,21202,3,-1,-1,22201,-3,-1,2,22101,0,-4,1,21102,1,384,0,1106,0,303,1105,1,415,21202,-4,-1,-4,22201,-4,-3,-4,22202,-3,-2,-2,22202,-2,-4,-4,22202,-3,-2,-3,21202,-4,-1,-2,22201,-3,-2,1,21202,1,1,-4,109,-5,2106,0,0 diff --git a/www/statics/aoc/2019/19_solution-1.ts b/www/statics/aoc/2019/19_solution-1.ts new file mode 100644 index 0000000..baddb87 --- /dev/null +++ b/www/statics/aoc/2019/19_solution-1.ts @@ -0,0 +1,299 @@ +namespace AdventOfCode2019_19_1 +{ + const DAY = 19; + const PROBLEM = 1; + + export async function run() + { + let input = await AdventOfCode.getInput(DAY); + if (input == null) return; + + const code = input.trim().split(",").map(p => parseInt(p.trim())); + + let sum = 0; + let out = ""; + for(let y=0; y<50; y++) + { + for(let x=0; x<50; x++) + { + let rnr = new Interpreter(code, [x, y]); + rnr.fullRun(); + if (rnr.output[0] === 0) + { + out += "."; + } + else if (rnr.output[0] === 1) + { + out += "#"; + sum += 1; + } + await AdventOfCode.outputIntermed(out); + } + out += "\n"; + } + + + AdventOfCode.output(DAY, PROBLEM, sum.toString()); + } + + class Interpreter + { + program: InfMem; + inputqueue: number[]; + instructionpointer: number; + output: number[]; + relative_base: number; + + is_halted: boolean = false; + + constructor(prog: number[], input: number[]) + { + this.program = new InfMem(prog); + this.inputqueue = input; + this.instructionpointer = 0; + this.output = []; + this.relative_base = 0; + } + + fullRun() : number[] + { + while(!this.is_halted) + { + const r = this.singleStep(); + + if (r === StepResult.EXECUTED) continue; + if (r === StepResult.HALTED) return this.output; + if (r === StepResult.WAITING_FOR_IN) throw "not enough input"; + + throw "unknown output of singleStep"; + } + + return this.output; + } + + autoRun() : StepResult + { + while(!this.is_halted) + { + const r = this.singleStep(); + + if (r === StepResult.EXECUTED) continue; + if (r === StepResult.HALTED) return StepResult.HALTED; + if (r === StepResult.WAITING_FOR_IN) return StepResult.WAITING_FOR_IN; + + throw "unknown output of singleStep"; + } + + return StepResult.HALTED; + } + + singleStep() : StepResult + { + const cmd = new Op(this.program.r(this.instructionpointer)); + + if (cmd.opcode == OpCode.ADD) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 + p1; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.MUL) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 * p1; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.HALT) + { + this.is_halted = true; + return StepResult.HALTED; + } + else if (cmd.opcode == OpCode.IN) + { + if (this.inputqueue.length == 0) return StepResult.WAITING_FOR_IN; + + const pv = this.inputqueue[0]; + cmd.setParameter(this, 0, pv); + this.inputqueue = this.inputqueue.slice(1); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.OUT) + { + const p0 = cmd.getParameter(this, 0); + this.output.push(p0); + //AdventOfCode.outputConsole("# " + p0); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.TJMP) + { + const p0 = cmd.getParameter(this, 0); + if (p0 != 0) this.instructionpointer = cmd.getParameter(this, 1); + else this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.FJMP) + { + const p0 = cmd.getParameter(this, 0); + if (p0 == 0) this.instructionpointer = cmd.getParameter(this, 1); + else this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.LT) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 < p1 ? 1 : 0; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.EQ) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 == p1 ? 1 : 0; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.ARB) + { + const p0 = cmd.getParameter(this, 0); + this.relative_base = this.relative_base+p0; + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else throw "Unknown Op: " + cmd.opcode + " @ " + this.instructionpointer; + } + + private incInstrPtr(cmd: Op) + { + this.instructionpointer += 1 + cmd.parametercount; + } + } + + enum StepResult { EXECUTED, HALTED, WAITING_FOR_IN } + + enum OpCode + { + ADD = 1, + MUL = 2, + IN = 3, + OUT = 4, + TJMP = 5, + FJMP = 6, + LT = 7, + EQ = 8, + ARB = 9, + HALT = 99, + } + + enum ParamMode + { + POSITION_MODE = 0, + IMMEDIATE_MODE = 1, + RELATIVE_MODE = 2, + } + + class Op + { + opcode: OpCode; + modes: ParamMode[]; + + name: string; + parametercount: number; + + constructor(v: number) + { + this.opcode = v%100; + v = Math.floor(v/100); + this.modes = []; + for(let i=0; i<4; i++) + { + this.modes.push(v%10); + v = Math.floor(v/10); + } + + if (this.opcode == OpCode.ADD) { this.name="ADD"; this.parametercount=3; } + else if (this.opcode == OpCode.MUL) { this.name="MUL"; this.parametercount=3; } + else if (this.opcode == OpCode.HALT) { this.name="HALT"; this.parametercount=0; } + else if (this.opcode == OpCode.IN) { this.name="IN"; this.parametercount=1; } + else if (this.opcode == OpCode.OUT) { this.name="OUT"; this.parametercount=1; } + else if (this.opcode == OpCode.TJMP) { this.name="TJMP"; this.parametercount=2; } + else if (this.opcode == OpCode.FJMP) { this.name="FJMP"; this.parametercount=2; } + else if (this.opcode == OpCode.LT) { this.name="LT"; this.parametercount=3; } + else if (this.opcode == OpCode.EQ) { this.name="EQ"; this.parametercount=3; } + else if (this.opcode == OpCode.ARB) { this.name="ARB"; this.parametercount=1; } + else throw "Unknown opcode: "+this.opcode; + } + + getParameter(proc: Interpreter, index: number): number + { + const prog = proc.program; + const ip = proc.instructionpointer; + + let p = prog.r(ip+1+index); + + if (this.modes[index] == ParamMode.POSITION_MODE) p = prog.r(p); + else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) p = p; + else if (this.modes[index] == ParamMode.RELATIVE_MODE) p = prog.r(proc.relative_base+p); + else throw "Unknown ParamMode: "+this.modes[index]; + + return p; + } + + setParameter(proc: Interpreter, index: number, value: number): void + { + const prog = proc.program; + const ip = proc.instructionpointer; + + let p = prog.r(ip+1+index); + + if (this.modes[index] == ParamMode.POSITION_MODE) prog.w(p, value); + else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) throw "Immediate mode not allowed in write"; + else if (this.modes[index] == ParamMode.RELATIVE_MODE) prog.w(proc.relative_base+p, value); + else throw "Unknown ParamMode: "+this.modes[index]; + } + } + + class InfMem + { + private data: { [_:number]:number } = {}; + + constructor(v: number[]) + { + for(let i=0; i parseInt(p.trim())); + + const RECT_LEN = 100; + + let grid: {[_:number]: boolean} = {}; + + let lout = []; + + for(let y=0; y<10; y++) + { + let out = ""; + for(let x=0; x<50; x++) + { + let rnr = new Interpreter(code, [x, y]); rnr.fullRun(); + if (rnr.output[0] === 0) out += " "; + else if (rnr.output[0] === 1) out += "#"; + } + out = out.trimEnd(); + out = out.replace(/ /g, "."); + lout.push(out); + } + + let xstart = 0; + for(let y=10;; y++) + { + let out = ""; + let xsum = 0; + for(let x=xstart;; x++) + { + let rnr = new Interpreter(code, [x, y]); + rnr.fullRun(); + + if (rnr.output[0] === 0) + { + if (xsum===0)xstart=x; + out += "."; + grid[y*100000+x]=false; + } + else if (rnr.output[0] === 1) + { + out += "#"; + xsum += 1; + grid[y*100000+x]=true; + + if (xsum>=RECT_LEN) + { + const ibr = (y)*100000+(x); + const ibl = (y)*100000+(x-RECT_LEN+1); + const itr = (y-RECT_LEN+1)*100000+(x); + const itl = (y-RECT_LEN+1)*100000+(x-RECT_LEN+1); + + const br = ibr in grid && grid[ibr]; + const bl = ibl in grid && grid[ibl]; + const tr = itr in grid && grid[itr]; + const tl = itl in grid && grid[itl]; + + if (br && bl && tr && tl) + { + lout.push(out); + if (lout.length>128) lout.shift(); + await AdventOfCode.outputIntermed(lout.join("\n")); + + AdventOfCode.outputConsole(`[${x-RECT_LEN+1}|${y-RECT_LEN+1}]`); + AdventOfCode.outputConsole(`[${x}|${y-RECT_LEN+1}]`); + AdventOfCode.outputConsole(`[${x-RECT_LEN+1}|${y}]`); + AdventOfCode.outputConsole(`[${x}|${y}]`); + + //let fstr = ""; + //for (let yy=0; yy<=y+1; yy++) + //{ + // for (let xx=0; xx<=x+1; xx++) + // { + // let iidd = yy*100000+xx; + // let trac = iidd in grid && grid[iidd]; + // let ship = (xx >= (x-RECT_LEN+1)) && + // (xx <= (x)) && + // (yy >= (y-RECT_LEN+1)) && + // (yy <= (y)); + // if (!trac) fstr += "."; + // else if (ship) fstr += "O"; + // else fstr += "#"; + // } + // fstr += "\n"; + //} + //AdventOfCode.outputConsole(fstr); + + AdventOfCode.output(DAY, PROBLEM, ((x-RECT_LEN+1)*10000 + (y-RECT_LEN+1)).toString()); + return; + } + } + } + + //await AdventOfCode.outputIntermed(out); + + if (xsum>0 && rnr.output[0] === 0) break; + } + lout.push(out); + if (lout.length>100) lout.shift(); + await AdventOfCode.outputIntermed(lout.join("\n")); + } + } + + class Interpreter + { + program: InfMem; + inputqueue: number[]; + instructionpointer: number; + output: number[]; + relative_base: number; + + is_halted: boolean = false; + + constructor(prog: number[], input: number[]) + { + this.program = new InfMem(prog); + this.inputqueue = input; + this.instructionpointer = 0; + this.output = []; + this.relative_base = 0; + } + + fullRun() : number[] + { + while(!this.is_halted) + { + const r = this.singleStep(); + + if (r === StepResult.EXECUTED) continue; + if (r === StepResult.HALTED) return this.output; + if (r === StepResult.WAITING_FOR_IN) throw "not enough input"; + + throw "unknown output of singleStep"; + } + + return this.output; + } + + autoRun() : StepResult + { + while(!this.is_halted) + { + const r = this.singleStep(); + + if (r === StepResult.EXECUTED) continue; + if (r === StepResult.HALTED) return StepResult.HALTED; + if (r === StepResult.WAITING_FOR_IN) return StepResult.WAITING_FOR_IN; + + throw "unknown output of singleStep"; + } + + return StepResult.HALTED; + } + + singleStep() : StepResult + { + const cmd = new Op(this.program.r(this.instructionpointer)); + + if (cmd.opcode == OpCode.ADD) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 + p1; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.MUL) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 * p1; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.HALT) + { + this.is_halted = true; + return StepResult.HALTED; + } + else if (cmd.opcode == OpCode.IN) + { + if (this.inputqueue.length == 0) return StepResult.WAITING_FOR_IN; + + const pv = this.inputqueue[0]; + cmd.setParameter(this, 0, pv); + this.inputqueue = this.inputqueue.slice(1); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.OUT) + { + const p0 = cmd.getParameter(this, 0); + this.output.push(p0); + //AdventOfCode.outputConsole("# " + p0); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.TJMP) + { + const p0 = cmd.getParameter(this, 0); + if (p0 != 0) this.instructionpointer = cmd.getParameter(this, 1); + else this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.FJMP) + { + const p0 = cmd.getParameter(this, 0); + if (p0 == 0) this.instructionpointer = cmd.getParameter(this, 1); + else this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.LT) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 < p1 ? 1 : 0; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.EQ) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 == p1 ? 1 : 0; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.ARB) + { + const p0 = cmd.getParameter(this, 0); + this.relative_base = this.relative_base+p0; + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else throw "Unknown Op: " + cmd.opcode + " @ " + this.instructionpointer; + } + + private incInstrPtr(cmd: Op) + { + this.instructionpointer += 1 + cmd.parametercount; + } + } + + enum StepResult { EXECUTED, HALTED, WAITING_FOR_IN } + + enum OpCode + { + ADD = 1, + MUL = 2, + IN = 3, + OUT = 4, + TJMP = 5, + FJMP = 6, + LT = 7, + EQ = 8, + ARB = 9, + HALT = 99, + } + + enum ParamMode + { + POSITION_MODE = 0, + IMMEDIATE_MODE = 1, + RELATIVE_MODE = 2, + } + + class Op + { + opcode: OpCode; + modes: ParamMode[]; + + name: string; + parametercount: number; + + constructor(v: number) + { + this.opcode = v%100; + v = Math.floor(v/100); + this.modes = []; + for(let i=0; i<4; i++) + { + this.modes.push(v%10); + v = Math.floor(v/10); + } + + if (this.opcode == OpCode.ADD) { this.name="ADD"; this.parametercount=3; } + else if (this.opcode == OpCode.MUL) { this.name="MUL"; this.parametercount=3; } + else if (this.opcode == OpCode.HALT) { this.name="HALT"; this.parametercount=0; } + else if (this.opcode == OpCode.IN) { this.name="IN"; this.parametercount=1; } + else if (this.opcode == OpCode.OUT) { this.name="OUT"; this.parametercount=1; } + else if (this.opcode == OpCode.TJMP) { this.name="TJMP"; this.parametercount=2; } + else if (this.opcode == OpCode.FJMP) { this.name="FJMP"; this.parametercount=2; } + else if (this.opcode == OpCode.LT) { this.name="LT"; this.parametercount=3; } + else if (this.opcode == OpCode.EQ) { this.name="EQ"; this.parametercount=3; } + else if (this.opcode == OpCode.ARB) { this.name="ARB"; this.parametercount=1; } + else throw "Unknown opcode: "+this.opcode; + } + + getParameter(proc: Interpreter, index: number): number + { + const prog = proc.program; + const ip = proc.instructionpointer; + + let p = prog.r(ip+1+index); + + if (this.modes[index] == ParamMode.POSITION_MODE) p = prog.r(p); + else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) p = p; + else if (this.modes[index] == ParamMode.RELATIVE_MODE) p = prog.r(proc.relative_base+p); + else throw "Unknown ParamMode: "+this.modes[index]; + + return p; + } + + setParameter(proc: Interpreter, index: number, value: number): void + { + const prog = proc.program; + const ip = proc.instructionpointer; + + let p = prog.r(ip+1+index); + + if (this.modes[index] == ParamMode.POSITION_MODE) prog.w(p, value); + else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) throw "Immediate mode not allowed in write"; + else if (this.modes[index] == ParamMode.RELATIVE_MODE) prog.w(proc.relative_base+p, value); + else throw "Unknown ParamMode: "+this.modes[index]; + } + } + + class InfMem + { + private data: { [_:number]:number } = {}; + + constructor(v: number[]) + { + for(let i=0; i p.trim().length > 0) + .map(p => p.split('').map(q => q) ); + + const width = grid[0].length; + const height = grid.length; + + let tunnels: {[_:string]: [Point, Point]} = {}; + let tunnelsList: {[_:number]: Point} = {}; + let entry: Point = [0, 0]; + let exit: Point = [0, 0]; + + for (let y=1; y " + point_to_str(tunnels[key][1])); + } + else + { + tunnels[key] = [pos, [-1, -1]]; + } + } + } + + for (let y=0; y0) + { + await AdventOfCode.outputIntermed(toStr2(grid, dqueue, tunnelsList)); + //await AdventOfCode.sleep(1000); + + const [dist, pt] = dqueue.shift()!; + const [ptx, pty] = pt; + let ptid = id(pt); + + if (ptid in distmap && distmap[ptid] <= dist) continue; + + distmap[ptid] = dist; + + if (grid[pty-1][ptx] === '.') dqueue.push([ dist+1, [ptx, pty-1] ]); + if (grid[pty][ptx+1] === '.') dqueue.push([ dist+1, [ptx+1, pty] ]); + if (grid[pty+1][ptx] === '.') dqueue.push([ dist+1, [ptx, pty+1] ]); + if (grid[pty][ptx-1] === '.') dqueue.push([ dist+1, [ptx-1, pty] ]); + + if (ptid in tunnelsList) dqueue.push([dist+1, tunnelsList[ptid]]); + } + + await AdventOfCode.outputIntermed(toStr(grid)); + + AdventOfCode.output(DAY, PROBLEM, distmap[id(exit)].toString()); + } + + function id (p: Point) { return p[1]*10000+p[0]; } + + function isUpperChar(c: string): boolean + { + return c.charCodeAt(0) >= "A".charCodeAt(0) && c.charCodeAt(0) <= "Z".charCodeAt(0); + } + + function toStr(grid: string[][]): string + { + return grid.map(p => p.join("") ).join("\n"); + } + + function toStr2(grid: string[][], highlights: [any, Point][], tunnels: {[_:number]: Point}): string + { + const width = grid[0].length; + const height = grid.length; + + let str = ""; + for (let y=0; y p[1][0] === x && p[1][1] === y )) str += "O"; + else if (id([x,y]) in tunnels) str += "@"; + else str += grid[y][x]; + } + str += "\n"; + } + + return str + } + + function point_to_str (p: Point) { return `[${p[0]}|${p[1]}]`; } +} diff --git a/www/statics/aoc/2019/20_solution-2.ts b/www/statics/aoc/2019/20_solution-2.ts new file mode 100644 index 0000000..ea9bb13 --- /dev/null +++ b/www/statics/aoc/2019/20_solution-2.ts @@ -0,0 +1,183 @@ +namespace AdventOfCode2019_20_2 +{ + const DAY = 20; + const PROBLEM = 2; + + type Point = [number, number]; + + export async function run() + { + let input = await AdventOfCode.getInput(DAY); + if (input == null) return; + + AdventOfCode.setIntermedOutputSize("0.35vw"); + + let grid = input + .split(new RegExp('\r?\n')) + .filter(p => p.trim().length > 0) + .map(p => p.split('').map(q => q) ); + + const width = grid[0].length; + const height = grid.length; + + let tunnels: {[_:string]: [Point, Point]} = {}; + let tunnelsList: {[_:number]: [boolean, Point]} = {}; + let entry: Point = [0, 0]; + let exit: Point = [0, 0]; + + for (let y=1; y3 && y>3 && x " + point_to_str(tunnels[key][1])); + } + else + { + if (isinner) tunnels[key] = [pos, [-1, -1]]; + else tunnels[key] = [[-1, -1], pos]; + } + } + } + + for (let y=0; y + dqueue.push([0, 0, entry]); + + let last_dist = -1; + while (dqueue.length>0) + { + + const [dist, depth, pt] = dqueue.shift()!; + const [ptx, pty] = pt; + let ptid2 = id2(pt, depth); + let ptid1 = id(pt); + + if (ptid2 in distmap && distmap[ptid2] <= dist) continue; + + if (dist !== last_dist) + { + await AdventOfCode.outputIntermed(toStr2(grid, dqueue, tunnelsList)+"\n\n" + dist); + last_dist = dist; + } + + if (depth === 0 && ptx === exit[0] && pty === exit[1]) + { + AdventOfCode.output(DAY, PROBLEM, dist.toString()); + await AdventOfCode.outputIntermed(toStr(grid)); + return; + } + + distmap[ptid2] = dist; + + if (grid[pty-1][ptx] === '.') dqueue.push([ dist+1, depth, [ptx, pty-1] ]); + if (grid[pty][ptx+1] === '.') dqueue.push([ dist+1, depth, [ptx+1, pty] ]); + if (grid[pty+1][ptx] === '.') dqueue.push([ dist+1, depth, [ptx, pty+1] ]); + if (grid[pty][ptx-1] === '.') dqueue.push([ dist+1, depth, [ptx-1, pty] ]); + + if (ptid1 in tunnelsList) + { + const [tunnel_dir, tunnel_pos] = tunnelsList[ptid1]; + if (depth ===0 && !tunnel_dir) { /* wall */ } + else dqueue.push([dist+1, depth + (tunnel_dir ? 1 : -1), tunnel_pos]); + } + } + + + } + + function id (p: Point) { return p[1]*10000+p[0]; } + + function id2(p: Point, d: number) { return p[1]*1000000+1000*p[0]+d; } + + function isUpperChar(c: string): boolean + { + return c.charCodeAt(0) >= "A".charCodeAt(0) && c.charCodeAt(0) <= "Z".charCodeAt(0); + } + + function toStr(grid: string[][]): string + { + return grid.map(p => p.join("") ).join("\n"); + } + + function toStr2(grid: string[][], highlights: [any, any, Point][], tunnels: {[_:number]: [any, Point]}): string + { + const width = grid[0].length; + const height = grid.length; + + let str = ""; + for (let y=0; y p[2][0] === x && p[2][1] === y )) str += "O"; + else if (id([x,y]) in tunnels) str += "@"; + else str += grid[y][x]; + } + str += "\n"; + } + + return str + } + + function point_to_str (p: Point) { return `[${p[0]}|${p[1]}]`; } +} diff --git a/www/statics/aoc/2019/21_challenge.txt b/www/statics/aoc/2019/21_challenge.txt new file mode 100644 index 0000000..e08b317 --- /dev/null +++ b/www/statics/aoc/2019/21_challenge.txt @@ -0,0 +1,102 @@ +--- Day 21: Springdroid Adventure --- + +You lift off from Pluto and start flying in the direction of Santa. + +While experimenting further with the tractor beam, you accidentally pull an asteroid directly into your ship! It deals significant damage to your hull and causes your ship to begin tumbling violently. + +You can send a droid out to investigate, but the tumbling is causing enough artificial gravity that one wrong step could send the droid through a hole in the hull and flying out into space. + +The clear choice for this mission is a droid that can jump over the holes in the hull - a springdroid. + +You can use an Intcode program (your puzzle input) running on an ASCII-capable computer to program the springdroid. However, springdroids don't run Intcode; instead, they run a simplified assembly language called springscript. + +While a springdroid is certainly capable of navigating the artificial gravity and giant holes, it has one downside: it can only remember at most 15 springscript instructions. + +The springdroid will move forward automatically, constantly thinking about whether to jump. The springscript program defines the logic for this decision. + +Springscript programs only use Boolean values, not numbers or strings. Two registers are available: T, the temporary value register, and J, the jump register. If the jump register is true at the end of the springscript program, the springdroid will try to jump. Both of these registers start with the value false. + +Springdroids have a sensor that can detect whether there is ground at various distances in the direction it is facing; these values are provided in read-only registers. Your springdroid can detect ground at four distances: one tile away (A), two tiles away (B), three tiles away (C), and four tiles away (D). If there is ground at the given distance, the register will be true; if there is a hole, the register will be false. + +There are only three instructions available in springscript: + + AND X Y sets Y to true if both X and Y are true; otherwise, it sets Y to false. + OR X Y sets Y to true if at least one of X or Y is true; otherwise, it sets Y to false. + NOT X Y sets Y to true if X is false; otherwise, it sets Y to false. + +In all three instructions, the second argument (Y) needs to be a writable register (either T or J). The first argument (X) can be any register (including A, B, C, or D). + +For example, the one-instruction program NOT A J means "if the tile immediately in front of me is not ground, jump". + +Or, here is a program that jumps if a three-tile-wide hole (with ground on the other side of the hole) is detected: + +NOT A J +NOT B T +AND T J +NOT C T +AND T J +AND D J + +The Intcode program expects ASCII inputs and outputs. It will begin by displaying a prompt; then, input the desired instructions one per line. End each line with a newline (ASCII code 10). When you have finished entering your program, provide the command WALK followed by a newline to instruct the springdroid to begin surveying the hull. + +If the springdroid falls into space, an ASCII rendering of the last moments of its life will be produced. In these, @ is the springdroid, # is hull, and . is empty space. For example, suppose you program the springdroid like this: + +NOT D J +WALK + +This one-instruction program sets J to true if and only if there is no ground four tiles away. In other words, it attempts to jump into any hole it finds: + +................. +................. +@................ +#####.########### + +................. +................. +.@............... +#####.########### + +................. +..@.............. +................. +#####.########### + +...@............. +................. +................. +#####.########### + +................. +....@............ +................. +#####.########### + +................. +................. +.....@........... +#####.########### + +................. +................. +................. +#####@########### + +However, if the springdroid successfully makes it across, it will use an output instruction to indicate the amount of damage to the hull as a single giant integer outside the normal ASCII range. + +Program the springdroid with logic that allows it to survey the hull without falling into space. What amount of hull damage does it report? + +--- Part Two --- + +There are many areas the springdroid can't reach. You flip through the manual and discover a way to increase its sensor range. + +Instead of ending your springcode program with WALK, use RUN. Doing this will enable extended sensor mode, capable of sensing ground up to nine tiles away. This data is available in five new read-only registers: + + Register E indicates whether there is ground five tiles away. + Register F indicates whether there is ground six tiles away. + Register G indicates whether there is ground seven tiles away. + Register H indicates whether there is ground eight tiles away. + Register I indicates whether there is ground nine tiles away. + +All other functions remain the same. + +Successfully survey the rest of the hull by ending your program with RUN. What amount of hull damage does the springdroid now report? diff --git a/www/statics/aoc/2019/21_input.txt b/www/statics/aoc/2019/21_input.txt new file mode 100644 index 0000000..014445c --- /dev/null +++ b/www/statics/aoc/2019/21_input.txt @@ -0,0 +1 @@ +109,2050,21101,966,0,1,21101,13,0,0,1106,0,1378,21102,1,20,0,1105,1,1337,21101,27,0,0,1106,0,1279,1208,1,65,748,1005,748,73,1208,1,79,748,1005,748,110,1208,1,78,748,1005,748,132,1208,1,87,748,1005,748,169,1208,1,82,748,1005,748,239,21102,1041,1,1,21102,1,73,0,1106,0,1421,21101,0,78,1,21101,1041,0,2,21102,1,88,0,1106,0,1301,21101,0,68,1,21101,1041,0,2,21101,0,103,0,1105,1,1301,1102,1,1,750,1105,1,298,21101,0,82,1,21102,1041,1,2,21101,125,0,0,1105,1,1301,1101,0,2,750,1106,0,298,21102,79,1,1,21102,1,1041,2,21101,0,147,0,1105,1,1301,21102,84,1,1,21101,1041,0,2,21102,162,1,0,1106,0,1301,1101,3,0,750,1106,0,298,21102,65,1,1,21102,1,1041,2,21101,184,0,0,1106,0,1301,21102,76,1,1,21101,1041,0,2,21102,1,199,0,1106,0,1301,21101,0,75,1,21102,1041,1,2,21101,214,0,0,1105,1,1301,21101,221,0,0,1105,1,1337,21102,10,1,1,21101,0,1041,2,21101,0,236,0,1105,1,1301,1106,0,553,21101,0,85,1,21102,1,1041,2,21102,254,1,0,1106,0,1301,21101,78,0,1,21102,1,1041,2,21102,1,269,0,1105,1,1301,21102,276,1,0,1105,1,1337,21101,10,0,1,21101,0,1041,2,21101,291,0,0,1106,0,1301,1101,1,0,755,1106,0,553,21101,32,0,1,21101,1041,0,2,21102,1,313,0,1106,0,1301,21102,1,320,0,1106,0,1337,21101,327,0,0,1106,0,1279,1201,1,0,749,21102,65,1,2,21101,0,73,3,21101,346,0,0,1106,0,1889,1206,1,367,1007,749,69,748,1005,748,360,1102,1,1,756,1001,749,-64,751,1105,1,406,1008,749,74,748,1006,748,381,1101,-1,0,751,1105,1,406,1008,749,84,748,1006,748,395,1102,1,-2,751,1105,1,406,21101,1100,0,1,21102,1,406,0,1106,0,1421,21102,32,1,1,21102,1100,1,2,21101,0,421,0,1106,0,1301,21102,428,1,0,1106,0,1337,21101,0,435,0,1105,1,1279,2102,1,1,749,1008,749,74,748,1006,748,453,1101,0,-1,752,1105,1,478,1008,749,84,748,1006,748,467,1101,0,-2,752,1105,1,478,21101,1168,0,1,21102,1,478,0,1106,0,1421,21101,0,485,0,1105,1,1337,21101,0,10,1,21101,1168,0,2,21102,500,1,0,1106,0,1301,1007,920,15,748,1005,748,518,21101,0,1209,1,21102,518,1,0,1105,1,1421,1002,920,3,529,1001,529,921,529,102,1,750,0,1001,529,1,537,101,0,751,0,1001,537,1,545,101,0,752,0,1001,920,1,920,1106,0,13,1005,755,577,1006,756,570,21101,0,1100,1,21101,570,0,0,1106,0,1421,21101,987,0,1,1105,1,581,21101,0,1001,1,21102,1,588,0,1106,0,1378,1101,758,0,594,102,1,0,753,1006,753,654,20101,0,753,1,21101,0,610,0,1106,0,667,21102,1,0,1,21102,1,621,0,1106,0,1463,1205,1,647,21102,1,1015,1,21101,635,0,0,1106,0,1378,21101,0,1,1,21102,646,1,0,1105,1,1463,99,1001,594,1,594,1106,0,592,1006,755,664,1101,0,0,755,1106,0,647,4,754,99,109,2,1101,726,0,757,22101,0,-1,1,21101,0,9,2,21102,1,697,3,21101,692,0,0,1105,1,1913,109,-2,2105,1,0,109,2,101,0,757,706,2101,0,-1,0,1001,757,1,757,109,-2,2105,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,255,63,127,95,159,191,223,0,238,92,110,214,173,123,213,231,162,79,205,117,59,118,233,247,136,102,93,243,126,242,50,244,47,156,51,46,232,99,158,142,106,152,204,249,251,181,53,198,199,121,201,188,169,94,186,171,139,120,172,248,227,43,54,217,185,215,124,153,216,154,71,109,100,241,207,174,125,226,254,78,116,212,200,55,187,87,250,108,85,76,253,58,237,122,234,202,166,101,170,57,140,218,42,114,69,229,98,84,182,175,137,190,119,178,60,157,228,38,111,61,62,77,203,39,143,35,113,235,155,107,168,179,230,163,34,239,56,196,138,68,246,220,206,221,222,183,219,177,141,167,49,115,189,86,103,236,245,252,70,184,197,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,73,110,112,117,116,32,105,110,115,116,114,117,99,116,105,111,110,115,58,10,13,10,87,97,108,107,105,110,103,46,46,46,10,10,13,10,82,117,110,110,105,110,103,46,46,46,10,10,25,10,68,105,100,110,39,116,32,109,97,107,101,32,105,116,32,97,99,114,111,115,115,58,10,10,58,73,110,118,97,108,105,100,32,111,112,101,114,97,116,105,111,110,59,32,101,120,112,101,99,116,101,100,32,115,111,109,101,116,104,105,110,103,32,108,105,107,101,32,65,78,68,44,32,79,82,44,32,111,114,32,78,79,84,67,73,110,118,97,108,105,100,32,102,105,114,115,116,32,97,114,103,117,109,101,110,116,59,32,101,120,112,101,99,116,101,100,32,115,111,109,101,116,104,105,110,103,32,108,105,107,101,32,65,44,32,66,44,32,67,44,32,68,44,32,74,44,32,111,114,32,84,40,73,110,118,97,108,105,100,32,115,101,99,111,110,100,32,97,114,103,117,109,101,110,116,59,32,101,120,112,101,99,116,101,100,32,74,32,111,114,32,84,52,79,117,116,32,111,102,32,109,101,109,111,114,121,59,32,97,116,32,109,111,115,116,32,49,53,32,105,110,115,116,114,117,99,116,105,111,110,115,32,99,97,110,32,98,101,32,115,116,111,114,101,100,0,109,1,1005,1262,1270,3,1262,20102,1,1262,0,109,-1,2106,0,0,109,1,21102,1288,1,0,1105,1,1263,20101,0,1262,0,1102,1,0,1262,109,-1,2105,1,0,109,5,21101,0,1310,0,1105,1,1279,21201,1,0,-2,22208,-2,-4,-1,1205,-1,1332,22101,0,-3,1,21102,1,1332,0,1106,0,1421,109,-5,2106,0,0,109,2,21101,1346,0,0,1105,1,1263,21208,1,32,-1,1205,-1,1363,21208,1,9,-1,1205,-1,1363,1106,0,1373,21101,0,1370,0,1105,1,1279,1106,0,1339,109,-2,2105,1,0,109,5,2102,1,-4,1386,20101,0,0,-2,22101,1,-4,-4,21102,1,0,-3,22208,-3,-2,-1,1205,-1,1416,2201,-4,-3,1408,4,0,21201,-3,1,-3,1106,0,1396,109,-5,2106,0,0,109,2,104,10,22101,0,-1,1,21102,1436,1,0,1105,1,1378,104,10,99,109,-2,2105,1,0,109,3,20002,594,753,-1,22202,-1,-2,-1,201,-1,754,754,109,-3,2105,1,0,109,10,21102,5,1,-5,21101,0,1,-4,21101,0,0,-3,1206,-9,1555,21102,3,1,-6,21102,5,1,-7,22208,-7,-5,-8,1206,-8,1507,22208,-6,-4,-8,1206,-8,1507,104,64,1106,0,1529,1205,-6,1527,1201,-7,716,1515,21002,0,-11,-8,21201,-8,46,-8,204,-8,1106,0,1529,104,46,21201,-7,1,-7,21207,-7,22,-8,1205,-8,1488,104,10,21201,-6,-1,-6,21207,-6,0,-8,1206,-8,1484,104,10,21207,-4,1,-8,1206,-8,1569,21102,1,0,-9,1106,0,1689,21208,-5,21,-8,1206,-8,1583,21101,0,1,-9,1105,1,1689,1201,-5,716,1588,21001,0,0,-2,21208,-4,1,-1,22202,-2,-1,-1,1205,-2,1613,21202,-5,1,1,21101,0,1613,0,1106,0,1444,1206,-1,1634,22102,1,-5,1,21102,1627,1,0,1106,0,1694,1206,1,1634,21101,0,2,-3,22107,1,-4,-8,22201,-1,-8,-8,1206,-8,1649,21201,-5,1,-5,1206,-3,1663,21201,-3,-1,-3,21201,-4,1,-4,1106,0,1667,21201,-4,-1,-4,21208,-4,0,-1,1201,-5,716,1676,22002,0,-1,-1,1206,-1,1686,21102,1,1,-4,1105,1,1477,109,-10,2105,1,0,109,11,21101,0,0,-6,21102,0,1,-8,21102,1,0,-7,20208,-6,920,-9,1205,-9,1880,21202,-6,3,-9,1201,-9,921,1724,21001,0,0,-5,1001,1724,1,1732,21002,0,1,-4,21202,-4,1,1,21101,0,1,2,21102,1,9,3,21101,0,1754,0,1105,1,1889,1206,1,1772,2201,-10,-4,1767,1001,1767,716,1767,20102,1,0,-3,1106,0,1790,21208,-4,-1,-9,1206,-9,1786,22102,1,-8,-3,1106,0,1790,22101,0,-7,-3,1001,1732,1,1796,20101,0,0,-2,21208,-2,-1,-9,1206,-9,1812,21202,-8,1,-1,1105,1,1816,21201,-7,0,-1,21208,-5,1,-9,1205,-9,1837,21208,-5,2,-9,1205,-9,1844,21208,-3,0,-1,1105,1,1855,22202,-3,-1,-1,1105,1,1855,22201,-3,-1,-1,22107,0,-1,-1,1106,0,1855,21208,-2,-1,-9,1206,-9,1869,21201,-1,0,-8,1106,0,1873,21202,-1,1,-7,21201,-6,1,-6,1106,0,1708,21202,-8,1,-10,109,-11,2106,0,0,109,7,22207,-6,-5,-3,22207,-4,-6,-2,22201,-3,-2,-1,21208,-1,0,-6,109,-7,2106,0,0,0,109,5,1201,-2,0,1912,21207,-4,0,-1,1206,-1,1930,21101,0,0,-4,21201,-4,0,1,21201,-3,0,2,21101,0,1,3,21102,1949,1,0,1105,1,1954,109,-5,2105,1,0,109,6,21207,-4,1,-1,1206,-1,1977,22207,-5,-3,-1,1206,-1,1977,21201,-5,0,-5,1106,0,2045,21202,-5,1,1,21201,-4,-1,2,21202,-3,2,3,21102,1996,1,0,1106,0,1954,22102,1,1,-5,21101,1,0,-2,22207,-5,-3,-1,1206,-1,2015,21102,0,1,-2,22202,-3,-2,-3,22107,0,-4,-1,1206,-1,2037,22102,1,-2,1,21102,1,2037,0,105,1,1912,21202,-3,-1,-3,22201,-5,-3,-5,109,-6,2105,1,0 diff --git a/www/statics/aoc/2019/21_solution-1.ts b/www/statics/aoc/2019/21_solution-1.ts new file mode 100644 index 0000000..3f8b3d1 --- /dev/null +++ b/www/statics/aoc/2019/21_solution-1.ts @@ -0,0 +1,312 @@ +namespace AdventOfCode2019_21_1 +{ + const DAY = 21; + const PROBLEM = 1; + + export async function run() + { + let input = await AdventOfCode.getInput(DAY); + if (input == null) return; + + const code = input.trim().split(",").map(p => parseInt(p.trim())); + + let springcode = + [ + "NOT A T", // T = !A + "NOT B J", // J = !B + "OR T J", // J = !A or !B + "NOT C T", // T = !B + "OR T J", // J = !A or !B or !C + "NOT D T", // T = !D + "NOT T T", // T = D + "AND T J", // J = (!A or !B or !C) and D + "WALK" + ]; + + let rnr = new Interpreter(code, springcode.map(p => p+"\n").join("").split("").map(p => p.charCodeAt(0))); + + let output = springcode.join("\n")+"\n\n"; + AdventOfCode.outputConsole(output); + while (!rnr.is_halted) + { + rnr.singleStep(); + + if (rnr.output.length>0) + { + let v = rnr.output[0]; + output += String.fromCharCode(v); + rnr.output = []; + + if (v > 255) + { + AdventOfCode.output(DAY, PROBLEM, v.toString()); + return; + } + } + + } + await AdventOfCode.outputIntermed(output); // droid fell in hole + + } + + class Interpreter + { + program: InfMem; + inputqueue: number[]; + instructionpointer: number; + output: number[]; + relative_base: number; + + is_halted: boolean = false; + + constructor(prog: number[], input: number[]) + { + this.program = new InfMem(prog); + this.inputqueue = input; + this.instructionpointer = 0; + this.output = []; + this.relative_base = 0; + } + + fullRun() : number[] + { + while(!this.is_halted) + { + const r = this.singleStep(); + + if (r === StepResult.EXECUTED) continue; + if (r === StepResult.HALTED) return this.output; + if (r === StepResult.WAITING_FOR_IN) throw "not enough input"; + + throw "unknown output of singleStep"; + } + + return this.output; + } + + autoRun() : StepResult + { + while(!this.is_halted) + { + const r = this.singleStep(); + + if (r === StepResult.EXECUTED) continue; + if (r === StepResult.HALTED) return StepResult.HALTED; + if (r === StepResult.WAITING_FOR_IN) return StepResult.WAITING_FOR_IN; + + throw "unknown output of singleStep"; + } + + return StepResult.HALTED; + } + + singleStep() : StepResult + { + const cmd = new Op(this.program.r(this.instructionpointer)); + + if (cmd.opcode == OpCode.ADD) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 + p1; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.MUL) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 * p1; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.HALT) + { + this.is_halted = true; + return StepResult.HALTED; + } + else if (cmd.opcode == OpCode.IN) + { + if (this.inputqueue.length == 0) return StepResult.WAITING_FOR_IN; + + const pv = this.inputqueue[0]; + cmd.setParameter(this, 0, pv); + this.inputqueue = this.inputqueue.slice(1); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.OUT) + { + const p0 = cmd.getParameter(this, 0); + this.output.push(p0); + //AdventOfCode.outputConsole("# " + p0); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.TJMP) + { + const p0 = cmd.getParameter(this, 0); + if (p0 != 0) this.instructionpointer = cmd.getParameter(this, 1); + else this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.FJMP) + { + const p0 = cmd.getParameter(this, 0); + if (p0 == 0) this.instructionpointer = cmd.getParameter(this, 1); + else this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.LT) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 < p1 ? 1 : 0; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.EQ) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 == p1 ? 1 : 0; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.ARB) + { + const p0 = cmd.getParameter(this, 0); + this.relative_base = this.relative_base+p0; + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else throw "Unknown Op: " + cmd.opcode + " @ " + this.instructionpointer; + } + + private incInstrPtr(cmd: Op) + { + this.instructionpointer += 1 + cmd.parametercount; + } + } + + enum StepResult { EXECUTED, HALTED, WAITING_FOR_IN } + + enum OpCode + { + ADD = 1, + MUL = 2, + IN = 3, + OUT = 4, + TJMP = 5, + FJMP = 6, + LT = 7, + EQ = 8, + ARB = 9, + HALT = 99, + } + + enum ParamMode + { + POSITION_MODE = 0, + IMMEDIATE_MODE = 1, + RELATIVE_MODE = 2, + } + + class Op + { + opcode: OpCode; + modes: ParamMode[]; + + name: string; + parametercount: number; + + constructor(v: number) + { + this.opcode = v%100; + v = Math.floor(v/100); + this.modes = []; + for(let i=0; i<4; i++) + { + this.modes.push(v%10); + v = Math.floor(v/10); + } + + if (this.opcode == OpCode.ADD) { this.name="ADD"; this.parametercount=3; } + else if (this.opcode == OpCode.MUL) { this.name="MUL"; this.parametercount=3; } + else if (this.opcode == OpCode.HALT) { this.name="HALT"; this.parametercount=0; } + else if (this.opcode == OpCode.IN) { this.name="IN"; this.parametercount=1; } + else if (this.opcode == OpCode.OUT) { this.name="OUT"; this.parametercount=1; } + else if (this.opcode == OpCode.TJMP) { this.name="TJMP"; this.parametercount=2; } + else if (this.opcode == OpCode.FJMP) { this.name="FJMP"; this.parametercount=2; } + else if (this.opcode == OpCode.LT) { this.name="LT"; this.parametercount=3; } + else if (this.opcode == OpCode.EQ) { this.name="EQ"; this.parametercount=3; } + else if (this.opcode == OpCode.ARB) { this.name="ARB"; this.parametercount=1; } + else throw "Unknown opcode: "+this.opcode; + } + + getParameter(proc: Interpreter, index: number): number + { + const prog = proc.program; + const ip = proc.instructionpointer; + + let p = prog.r(ip+1+index); + + if (this.modes[index] == ParamMode.POSITION_MODE) p = prog.r(p); + else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) p = p; + else if (this.modes[index] == ParamMode.RELATIVE_MODE) p = prog.r(proc.relative_base+p); + else throw "Unknown ParamMode: "+this.modes[index]; + + return p; + } + + setParameter(proc: Interpreter, index: number, value: number): void + { + const prog = proc.program; + const ip = proc.instructionpointer; + + let p = prog.r(ip+1+index); + + if (this.modes[index] == ParamMode.POSITION_MODE) prog.w(p, value); + else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) throw "Immediate mode not allowed in write"; + else if (this.modes[index] == ParamMode.RELATIVE_MODE) prog.w(proc.relative_base+p, value); + else throw "Unknown ParamMode: "+this.modes[index]; + } + } + + class InfMem + { + private data: { [_:number]:number } = {}; + + constructor(v: number[]) + { + for(let i=0; i parseInt(p.trim())); + + let springcode = + [ + "NOT A T", // T = !A + "NOT B J", // J = !B + "OR T J", // J = !A or !B + "NOT C T", // T = !B + "OR T J", // J = !A or !B or !C + "NOT D T", // T = !D + "NOT T T", // T = D + "AND T J", // J = (!A or !B or !C) and D + + "NOT E T", // T = !E + "NOT T T", // T = E + "OR H T", // T = H or E + "AND T J", // J = (!A or !B or !C) and D and (H or E) + + "RUN" + ]; + + let rnr = new Interpreter(code, springcode.map(p => p+"\n").join("").split("").map(p => p.charCodeAt(0))); + + let output = springcode.join("\n")+"\n\n"; + AdventOfCode.outputConsole(output); + while (!rnr.is_halted) + { + rnr.singleStep(); + + if (rnr.output.length>0) + { + let v = rnr.output[0]; + output += String.fromCharCode(v); + rnr.output = []; + + if (v > 255) + { + AdventOfCode.output(DAY, PROBLEM, v.toString()); + return; + } + } + + } + await AdventOfCode.outputIntermed(output); // droid fell in hole + + } + + class Interpreter + { + program: InfMem; + inputqueue: number[]; + instructionpointer: number; + output: number[]; + relative_base: number; + + is_halted: boolean = false; + + constructor(prog: number[], input: number[]) + { + this.program = new InfMem(prog); + this.inputqueue = input; + this.instructionpointer = 0; + this.output = []; + this.relative_base = 0; + } + + fullRun() : number[] + { + while(!this.is_halted) + { + const r = this.singleStep(); + + if (r === StepResult.EXECUTED) continue; + if (r === StepResult.HALTED) return this.output; + if (r === StepResult.WAITING_FOR_IN) throw "not enough input"; + + throw "unknown output of singleStep"; + } + + return this.output; + } + + autoRun() : StepResult + { + while(!this.is_halted) + { + const r = this.singleStep(); + + if (r === StepResult.EXECUTED) continue; + if (r === StepResult.HALTED) return StepResult.HALTED; + if (r === StepResult.WAITING_FOR_IN) return StepResult.WAITING_FOR_IN; + + throw "unknown output of singleStep"; + } + + return StepResult.HALTED; + } + + singleStep() : StepResult + { + const cmd = new Op(this.program.r(this.instructionpointer)); + + if (cmd.opcode == OpCode.ADD) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 + p1; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.MUL) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 * p1; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.HALT) + { + this.is_halted = true; + return StepResult.HALTED; + } + else if (cmd.opcode == OpCode.IN) + { + if (this.inputqueue.length == 0) return StepResult.WAITING_FOR_IN; + + const pv = this.inputqueue[0]; + cmd.setParameter(this, 0, pv); + this.inputqueue = this.inputqueue.slice(1); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.OUT) + { + const p0 = cmd.getParameter(this, 0); + this.output.push(p0); + //AdventOfCode.outputConsole("# " + p0); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.TJMP) + { + const p0 = cmd.getParameter(this, 0); + if (p0 != 0) this.instructionpointer = cmd.getParameter(this, 1); + else this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.FJMP) + { + const p0 = cmd.getParameter(this, 0); + if (p0 == 0) this.instructionpointer = cmd.getParameter(this, 1); + else this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.LT) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 < p1 ? 1 : 0; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.EQ) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 == p1 ? 1 : 0; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.ARB) + { + const p0 = cmd.getParameter(this, 0); + this.relative_base = this.relative_base+p0; + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else throw "Unknown Op: " + cmd.opcode + " @ " + this.instructionpointer; + } + + private incInstrPtr(cmd: Op) + { + this.instructionpointer += 1 + cmd.parametercount; + } + } + + enum StepResult { EXECUTED, HALTED, WAITING_FOR_IN } + + enum OpCode + { + ADD = 1, + MUL = 2, + IN = 3, + OUT = 4, + TJMP = 5, + FJMP = 6, + LT = 7, + EQ = 8, + ARB = 9, + HALT = 99, + } + + enum ParamMode + { + POSITION_MODE = 0, + IMMEDIATE_MODE = 1, + RELATIVE_MODE = 2, + } + + class Op + { + opcode: OpCode; + modes: ParamMode[]; + + name: string; + parametercount: number; + + constructor(v: number) + { + this.opcode = v%100; + v = Math.floor(v/100); + this.modes = []; + for(let i=0; i<4; i++) + { + this.modes.push(v%10); + v = Math.floor(v/10); + } + + if (this.opcode == OpCode.ADD) { this.name="ADD"; this.parametercount=3; } + else if (this.opcode == OpCode.MUL) { this.name="MUL"; this.parametercount=3; } + else if (this.opcode == OpCode.HALT) { this.name="HALT"; this.parametercount=0; } + else if (this.opcode == OpCode.IN) { this.name="IN"; this.parametercount=1; } + else if (this.opcode == OpCode.OUT) { this.name="OUT"; this.parametercount=1; } + else if (this.opcode == OpCode.TJMP) { this.name="TJMP"; this.parametercount=2; } + else if (this.opcode == OpCode.FJMP) { this.name="FJMP"; this.parametercount=2; } + else if (this.opcode == OpCode.LT) { this.name="LT"; this.parametercount=3; } + else if (this.opcode == OpCode.EQ) { this.name="EQ"; this.parametercount=3; } + else if (this.opcode == OpCode.ARB) { this.name="ARB"; this.parametercount=1; } + else throw "Unknown opcode: "+this.opcode; + } + + getParameter(proc: Interpreter, index: number): number + { + const prog = proc.program; + const ip = proc.instructionpointer; + + let p = prog.r(ip+1+index); + + if (this.modes[index] == ParamMode.POSITION_MODE) p = prog.r(p); + else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) p = p; + else if (this.modes[index] == ParamMode.RELATIVE_MODE) p = prog.r(proc.relative_base+p); + else throw "Unknown ParamMode: "+this.modes[index]; + + return p; + } + + setParameter(proc: Interpreter, index: number, value: number): void + { + const prog = proc.program; + const ip = proc.instructionpointer; + + let p = prog.r(ip+1+index); + + if (this.modes[index] == ParamMode.POSITION_MODE) prog.w(p, value); + else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) throw "Immediate mode not allowed in write"; + else if (this.modes[index] == ParamMode.RELATIVE_MODE) prog.w(proc.relative_base+p, value); + else throw "Unknown ParamMode: "+this.modes[index]; + } + } + + class InfMem + { + private data: { [_:number]:number } = {}; + + constructor(v: number[]) + { + for(let i=0; i p.trim().length > 0) + .map(p => parseLine(p)) + + AdventOfCode.setIntermedOutputSize("0.275vw"); + + let data = [...Array(10007).keys()]; + + for (const cmd of cmds) + { + if (cmd.type === CmdType.CUT && cmd.param >= 0) + { + data = [...data.slice(cmd.param), ...data.slice(0, cmd.param)]; + } + else if (cmd.type === CmdType.CUT && cmd.param < 0) + { + data = [...data.slice(data.length + cmd.param), ...data.slice(0, data.length + cmd.param)]; + } + else if (cmd.type === CmdType.DEAL) + { + let d2 = Array(data.length); + for(let i=0; i p.toString().padStart(5, ' ')).join(" ") + "\n"; + } + await AdventOfCode.outputIntermed(str); + + AdventOfCode.outputConsole(data.map(p => p.toString()).join(" ")); + } + + + AdventOfCode.output(DAY, PROBLEM, data.indexOf(2019).toString()); + } + + function parseLine(str: string): Cmd + { + const m0 = str.match(new RegExp('^cut (-?[0-9]+)$')); + if (m0 != null) return new Cmd(CmdType.CUT, parseInt(m0![1])); + + const m1 = str.match(new RegExp('^deal into new stack$')); + if (m1 != null) return new Cmd(CmdType.DEAL, 1); + + const m2 = str.match(new RegExp('^deal with increment ([0-9]+)$')); + if (m2 != null) return new Cmd(CmdType.DEAL, parseInt(m2![1])); + + throw str; + } + + enum CmdType { CUT, DEAL } + + class Cmd + { + type: CmdType; + param: number; + + constructor(t:CmdType, n: number) { this.type=t; this.param=n; } + } +} diff --git a/www/statics/aoc/2019/22_solution-2.ts b/www/statics/aoc/2019/22_solution-2.ts new file mode 100644 index 0000000..ddc7b8f --- /dev/null +++ b/www/statics/aoc/2019/22_solution-2.ts @@ -0,0 +1,185 @@ +namespace AdventOfCode2019_22_2 +{ + const DAY = 22; + const PROBLEM = 2; + + + export async function run() + { + const CARDS = 119315717514047n; + const SHUFFLES = 101741582076661n; + + /* + NOTES + + lcg skipping + https://www.nayuki.io/page/fast-skipping-in-a-linear-congruential-generator + + cracking lcg + https://tailcall.net/blog/cracking-randomness-lcgs/ + */ + + let input = await AdventOfCode.getInput(DAY); + if (input == null) return; + + const cmds = input + .split(new RegExp('\r?\n')) + .filter(p => p.trim().length > 0) + .map(p => parseLine(p)); + + let states = []; + let card_index = 0n; + for (let i=0; i<10; i++) + { + states.push(card_index) + card_index = getSourceIndex(card_index, cmds, CARDS) + } + + AdventOfCode.outputConsole("[states]: " + states.join(" ; ")); + + const [modulus, multiplier, increment] = lcg_crack(states, CARDS); + + AdventOfCode.outputConsole("[modulus]: " + modulus); + AdventOfCode.outputConsole("[multiplier]: " + multiplier); + AdventOfCode.outputConsole("[increment]: " + increment); + + let r = lcg_skip(multiplier, increment, modulus, 2020n, SHUFFLES); + + AdventOfCode.output(DAY, PROBLEM, r.toString()); + } + + function lcg_skip(a: bigint, b: bigint, m: bigint, x:bigint, skip: bigint): bigint + { + //let ainv = reciprocal_mod(a, m); + + let a1 = a - 1n; + let ma = a1 * m; + + let y = (modpow(a, skip, ma) - 1n) / a1 * b + let z = modpow(a, skip, m) * x + + x = (y + z) % m + + return x; + } + + function modpow(base: bigint, exp: bigint, mod: bigint): bigint { + base = base % mod; + + let r = 1n; + while (exp > 0n) { + if (base === 0n) return 0n; + if (exp % 2n === 1n) r = (r * base) % mod; + exp /= 2n; + base = (base * base) % mod; + } + + return r; + } + + function reciprocal_mod(x: bigint, mod: bigint): bigint + { + let y = x; + x = mod; + let [a, b] = [0n, 1n]; + while (y !== 0n) + { + let _a = b; + let _b = (a-x) / (y * b); + a=_a; b=_b; + + let _x = y; + let _y = x%y; + x=_x;y=_y; + } + if (x === 1n) + return a % mod + else + throw ("Reciprocal does not exist") + } + + function getSourceIndex(idxDest: bigint, cmds: Cmd[], decklength: bigint) + { + let idx = idxDest; + for (let cmd of cmds.slice().reverse()) + { + //AdventOfCode.outputConsole("[idx]: " + idx); + if (cmd.type === CmdType.CUT) + { + idx = (idx + cmd.param + decklength) % decklength; + } + else if (cmd.type === CmdType.DEAL && cmd.param > 1n) + { + let f = false; + for(let i=0n; i parseInt(p.trim())); + + let intps = new Array(50); + for(let i=0; i<50; i++) intps[i] = new Interpreter(code, [i]); + for(let i=0; i<50; i++) intps[i].blocking_io = false; + + let counter = new Array(50*50).fill(0); + + const IDS = [ + '0','1','2','3','4','5','6','7','8','9', + 'A','B','C','D','E','F','G','H','I','J', + 'K','L','M','N','O','P','Q','R','S','T', + 'U','V','W','X','Y','Z','a','b','c','d', + 'e','f','g','h','i','j','k','l','m','n', + 'o','p','q','r','s','t','u','v','w','x', + 'y','z' + ]; + + for(;;) + { + for(let i=0; i<50; i++) + { + intps[i].singleStep(); + if (intps[i].output.length === 3) + { + let d = intps[i].output[0]; + let x = intps[i].output[1]; + let y = intps[i].output[2]; + + intps[i].output = []; + + if (d === 255) + { + AdventOfCode.output(DAY, PROBLEM, y.toString()); + + return; + } + + intps[d].inputqueue.push(x); + intps[d].inputqueue.push(y); + + AdventOfCode.outputConsole(`[${i}] --[${x}|${y}]--> [${d}]`) + + counter[i*50+d]++; + } + } + + if (AdventOfCode.Config.immediateOutputEnabled) + { + let str = " "; + for (let dst = 0; dst < 50; dst++) str += IDS[dst] + " "; + str += "\n"; + for (let src = 0; src < 50; src++) + { + str += IDS[src] + " "; + for (let dst = 0; dst < 50; dst++) + { + const v = counter[src*50+dst]; + + if (v === 0) str += "." + " "; + else if (v %2 === 1) str += "#" + " "; + else if (v %2 === 0) str += "X" + " "; + } + + str += "\n"; + } + + await AdventOfCode.outputIntermed(str); + } + } + } + + class Interpreter + { + program: InfMem; + inputqueue: number[]; + instructionpointer: number; + output: number[]; + relative_base: number; + blocking_io: boolean; + + is_halted: boolean = false; + + constructor(prog: number[], input: number[]) + { + this.program = new InfMem(prog); + this.inputqueue = input; + this.instructionpointer = 0; + this.output = []; + this.relative_base = 0; + this.blocking_io = true; + } + + fullRun() : number[] + { + while(!this.is_halted) + { + const r = this.singleStep(); + + if (r === StepResult.EXECUTED) continue; + if (r === StepResult.HALTED) return this.output; + if (r === StepResult.WAITING_FOR_IN) throw "not enough input"; + + throw "unknown output of singleStep"; + } + + return this.output; + } + + autoRun() : StepResult + { + while(!this.is_halted) + { + const r = this.singleStep(); + + if (r === StepResult.EXECUTED) continue; + if (r === StepResult.HALTED) return StepResult.HALTED; + if (r === StepResult.WAITING_FOR_IN) return StepResult.WAITING_FOR_IN; + + throw "unknown output of singleStep"; + } + + return StepResult.HALTED; + } + + singleStep() : StepResult + { + const cmd = new Op(this.program.r(this.instructionpointer)); + + if (cmd.opcode == OpCode.ADD) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 + p1; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.MUL) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 * p1; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.HALT) + { + this.is_halted = true; + return StepResult.HALTED; + } + else if (cmd.opcode == OpCode.IN) + { + if (this.inputqueue.length == 0) + { + if (this.blocking_io) return StepResult.WAITING_FOR_IN; + + cmd.setParameter(this, 0, -1); + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + + const pv = this.inputqueue[0]; + cmd.setParameter(this, 0, pv); + this.inputqueue = this.inputqueue.slice(1); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.OUT) + { + const p0 = cmd.getParameter(this, 0); + this.output.push(p0); + //AdventOfCode.outputConsole("# " + p0); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.TJMP) + { + const p0 = cmd.getParameter(this, 0); + if (p0 != 0) this.instructionpointer = cmd.getParameter(this, 1); + else this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.FJMP) + { + const p0 = cmd.getParameter(this, 0); + if (p0 == 0) this.instructionpointer = cmd.getParameter(this, 1); + else this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.LT) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 < p1 ? 1 : 0; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.EQ) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 == p1 ? 1 : 0; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.ARB) + { + const p0 = cmd.getParameter(this, 0); + this.relative_base = this.relative_base+p0; + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else throw "Unknown Op: " + cmd.opcode + " @ " + this.instructionpointer; + } + + private incInstrPtr(cmd: Op) + { + this.instructionpointer += 1 + cmd.parametercount; + } + } + + enum StepResult { EXECUTED, HALTED, WAITING_FOR_IN } + + enum OpCode + { + ADD = 1, + MUL = 2, + IN = 3, + OUT = 4, + TJMP = 5, + FJMP = 6, + LT = 7, + EQ = 8, + ARB = 9, + HALT = 99, + } + + enum ParamMode + { + POSITION_MODE = 0, + IMMEDIATE_MODE = 1, + RELATIVE_MODE = 2, + } + + class Op + { + opcode: OpCode; + modes: ParamMode[]; + + name: string; + parametercount: number; + + constructor(v: number) + { + this.opcode = v%100; + v = Math.floor(v/100); + this.modes = []; + for(let i=0; i<4; i++) + { + this.modes.push(v%10); + v = Math.floor(v/10); + } + + if (this.opcode == OpCode.ADD) { this.name="ADD"; this.parametercount=3; } + else if (this.opcode == OpCode.MUL) { this.name="MUL"; this.parametercount=3; } + else if (this.opcode == OpCode.HALT) { this.name="HALT"; this.parametercount=0; } + else if (this.opcode == OpCode.IN) { this.name="IN"; this.parametercount=1; } + else if (this.opcode == OpCode.OUT) { this.name="OUT"; this.parametercount=1; } + else if (this.opcode == OpCode.TJMP) { this.name="TJMP"; this.parametercount=2; } + else if (this.opcode == OpCode.FJMP) { this.name="FJMP"; this.parametercount=2; } + else if (this.opcode == OpCode.LT) { this.name="LT"; this.parametercount=3; } + else if (this.opcode == OpCode.EQ) { this.name="EQ"; this.parametercount=3; } + else if (this.opcode == OpCode.ARB) { this.name="ARB"; this.parametercount=1; } + else throw "Unknown opcode: "+this.opcode; + } + + getParameter(proc: Interpreter, index: number): number + { + const prog = proc.program; + const ip = proc.instructionpointer; + + let p = prog.r(ip+1+index); + + if (this.modes[index] == ParamMode.POSITION_MODE) p = prog.r(p); + else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) p = p; + else if (this.modes[index] == ParamMode.RELATIVE_MODE) p = prog.r(proc.relative_base+p); + else throw "Unknown ParamMode: "+this.modes[index]; + + return p; + } + + setParameter(proc: Interpreter, index: number, value: number): void + { + const prog = proc.program; + const ip = proc.instructionpointer; + + let p = prog.r(ip+1+index); + + if (this.modes[index] == ParamMode.POSITION_MODE) prog.w(p, value); + else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) throw "Immediate mode not allowed in write"; + else if (this.modes[index] == ParamMode.RELATIVE_MODE) prog.w(proc.relative_base+p, value); + else throw "Unknown ParamMode: "+this.modes[index]; + } + } + + class InfMem + { + private data: { [_:number]:number } = {}; + + constructor(v: number[]) + { + for(let i=0; i parseInt(p.trim())); + + let intps = new Array(50); + for(let i=0; i<50; i++) intps[i] = new Interpreter(code, [i]); + for(let i=0; i<50; i++) intps[i].blocking_io = false; + + let counter = new Array(50*50).fill(0); + + const IDS = [ + '0','1','2','3','4','5','6','7','8','9', + 'A','B','C','D','E','F','G','H','I','J', + 'K','L','M','N','O','P','Q','R','S','T', + 'U','V','W','X','Y','Z','a','b','c','d', + 'e','f','g','h','i','j','k','l','m','n', + 'o','p','q','r','s','t','u','v','w','x', + 'y','z' + ]; + + let last_nat_y: number = -1; + + let NAT: [number, number] = [0, 0]; + + for(;;) + { + let issend = false; + for(let i=0; i<50; i++) + { + intps[i].singleStep(); + if (intps[i].output.length === 3) + { + let d = intps[i].output[0]; + let x = intps[i].output[1]; + let y = intps[i].output[2]; + + intps[i].output = []; + + issend = true; + + if (d === 255) + { + NAT = [x, y]; + + AdventOfCode.outputConsole(`[${i}] --[${x}|${y}]--> NAT`); + } + else + { + intps[d].inputqueue.push(x); + intps[d].inputqueue.push(y); + + AdventOfCode.outputConsole(`[${i}] --[${x}|${y}]--> [${d}]`); + + counter[i*50+d]++; + } + + } + } + + if (issend && AdventOfCode.Config.immediateOutputEnabled) + { + let str = " "; + for (let dst = 0; dst < 50; dst++) str += IDS[dst % 10] + " "; + str += "\n"; + for (let src = 0; src < 50; src++) + { + str += "[" + (intps[src].last_io_success ? "X":".") + "] " + IDS[src % 10] + " "; + for (let dst = 0; dst < 50; dst++) + { + const v = counter[src*50+dst]; + + if (v === 0) str += "." + " "; + else if (v %2 === 1) str += "#" + " "; + else if (v %2 === 0) str += "X" + " "; + } + + str += "\n"; + } + + await AdventOfCode.outputIntermed(str); + } + + if (intps.every(p => !p.last_io_success)) + { + intps[0].inputqueue.push(NAT[0]); + intps[0].inputqueue.push(NAT[1]); + intps[0].last_io_success = true; + + AdventOfCode.outputConsole(`NAT --[${NAT[0]}|${NAT[1]}]--> [0]`); + + if (last_nat_y === NAT[1]) + { + AdventOfCode.output(DAY, PROBLEM, last_nat_y.toString()); + return; + } + + last_nat_y = NAT[1]; + } + } + + } + + class Interpreter + { + program: InfMem; + inputqueue: number[]; + instructionpointer: number; + output: number[]; + relative_base: number; + blocking_io: boolean; + + is_halted: boolean = false; + last_io_success: boolean = true; + + constructor(prog: number[], input: number[]) + { + this.program = new InfMem(prog); + this.inputqueue = input; + this.instructionpointer = 0; + this.output = []; + this.relative_base = 0; + this.blocking_io = true; + } + + fullRun() : number[] + { + while(!this.is_halted) + { + const r = this.singleStep(); + + if (r === StepResult.EXECUTED) continue; + if (r === StepResult.HALTED) return this.output; + if (r === StepResult.WAITING_FOR_IN) throw "not enough input"; + + throw "unknown output of singleStep"; + } + + return this.output; + } + + autoRun() : StepResult + { + while(!this.is_halted) + { + const r = this.singleStep(); + + if (r === StepResult.EXECUTED) continue; + if (r === StepResult.HALTED) return StepResult.HALTED; + if (r === StepResult.WAITING_FOR_IN) return StepResult.WAITING_FOR_IN; + + throw "unknown output of singleStep"; + } + + return StepResult.HALTED; + } + + singleStep() : StepResult + { + const cmd = new Op(this.program.r(this.instructionpointer)); + + if (cmd.opcode == OpCode.ADD) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 + p1; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.MUL) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 * p1; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.HALT) + { + this.is_halted = true; + return StepResult.HALTED; + } + else if (cmd.opcode == OpCode.IN) + { + if (this.inputqueue.length == 0) + { + if (this.blocking_io) return StepResult.WAITING_FOR_IN; + + cmd.setParameter(this, 0, -1); + this.incInstrPtr(cmd); + this.last_io_success = false; + return StepResult.EXECUTED; + } + + const pv = this.inputqueue[0]; + cmd.setParameter(this, 0, pv); + this.inputqueue = this.inputqueue.slice(1); + + this.incInstrPtr(cmd); + this.last_io_success = true; + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.OUT) + { + const p0 = cmd.getParameter(this, 0); + this.output.push(p0); + //AdventOfCode.outputConsole("# " + p0); + + this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.TJMP) + { + const p0 = cmd.getParameter(this, 0); + if (p0 != 0) this.instructionpointer = cmd.getParameter(this, 1); + else this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.FJMP) + { + const p0 = cmd.getParameter(this, 0); + if (p0 == 0) this.instructionpointer = cmd.getParameter(this, 1); + else this.incInstrPtr(cmd); + + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.LT) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 < p1 ? 1 : 0; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.EQ) + { + const p0 = cmd.getParameter(this, 0); + const p1 = cmd.getParameter(this, 1); + const pv = p0 == p1 ? 1 : 0; + cmd.setParameter(this, 2, pv); + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else if (cmd.opcode == OpCode.ARB) + { + const p0 = cmd.getParameter(this, 0); + this.relative_base = this.relative_base+p0; + + this.incInstrPtr(cmd); + return StepResult.EXECUTED; + } + else throw "Unknown Op: " + cmd.opcode + " @ " + this.instructionpointer; + } + + private incInstrPtr(cmd: Op) + { + this.instructionpointer += 1 + cmd.parametercount; + } + } + + enum StepResult { EXECUTED, HALTED, WAITING_FOR_IN } + + enum OpCode + { + ADD = 1, + MUL = 2, + IN = 3, + OUT = 4, + TJMP = 5, + FJMP = 6, + LT = 7, + EQ = 8, + ARB = 9, + HALT = 99, + } + + enum ParamMode + { + POSITION_MODE = 0, + IMMEDIATE_MODE = 1, + RELATIVE_MODE = 2, + } + + class Op + { + opcode: OpCode; + modes: ParamMode[]; + + name: string; + parametercount: number; + + constructor(v: number) + { + this.opcode = v%100; + v = Math.floor(v/100); + this.modes = []; + for(let i=0; i<4; i++) + { + this.modes.push(v%10); + v = Math.floor(v/10); + } + + if (this.opcode == OpCode.ADD) { this.name="ADD"; this.parametercount=3; } + else if (this.opcode == OpCode.MUL) { this.name="MUL"; this.parametercount=3; } + else if (this.opcode == OpCode.HALT) { this.name="HALT"; this.parametercount=0; } + else if (this.opcode == OpCode.IN) { this.name="IN"; this.parametercount=1; } + else if (this.opcode == OpCode.OUT) { this.name="OUT"; this.parametercount=1; } + else if (this.opcode == OpCode.TJMP) { this.name="TJMP"; this.parametercount=2; } + else if (this.opcode == OpCode.FJMP) { this.name="FJMP"; this.parametercount=2; } + else if (this.opcode == OpCode.LT) { this.name="LT"; this.parametercount=3; } + else if (this.opcode == OpCode.EQ) { this.name="EQ"; this.parametercount=3; } + else if (this.opcode == OpCode.ARB) { this.name="ARB"; this.parametercount=1; } + else throw "Unknown opcode: "+this.opcode; + } + + getParameter(proc: Interpreter, index: number): number + { + const prog = proc.program; + const ip = proc.instructionpointer; + + let p = prog.r(ip+1+index); + + if (this.modes[index] == ParamMode.POSITION_MODE) p = prog.r(p); + else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) p = p; + else if (this.modes[index] == ParamMode.RELATIVE_MODE) p = prog.r(proc.relative_base+p); + else throw "Unknown ParamMode: "+this.modes[index]; + + return p; + } + + setParameter(proc: Interpreter, index: number, value: number): void + { + const prog = proc.program; + const ip = proc.instructionpointer; + + let p = prog.r(ip+1+index); + + if (this.modes[index] == ParamMode.POSITION_MODE) prog.w(p, value); + else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) throw "Immediate mode not allowed in write"; + else if (this.modes[index] == ParamMode.RELATIVE_MODE) prog.w(proc.relative_base+p, value); + else throw "Unknown ParamMode: "+this.modes[index]; + } + } + + class InfMem + { + private data: { [_:number]:number } = {}; + + constructor(v: number[]) + { + for(let i=0; i p.trim().length > 0) + .map(p => p.trim().split('').map(q => q==='#')); + + await AdventOfCode.outputIntermed(tostr(grid)); + + let hist: {[_:number]:boolean} = {}; + hist[biodiv(grid)] = true; + + for(;;) + { + grid = step(grid); + let bd = biodiv(grid); + if (bd in hist) + { + AdventOfCode.output(DAY, PROBLEM, bd.toString()); + return; + } + hist[bd] = true; + + await AdventOfCode.outputIntermed(tostr(grid)); + } + } + + function tostr(grid: boolean[][]): string + { + return grid.map(p => p.map(q => q ? "#" : ".").join("") ).join("\n"); + } + + function biodiv(grid: boolean[][]): number + { + let n = 0; + for(let y=0; y<5; y++) + for(let x=0; x<5; x++) + { + n += grid[y][x] ? Math.pow(2, y*5+x) : 0; + } + return n; + } + + function step(grid: boolean[][]): boolean[][] + { + let g2 = [ + [false, false, false, false, false], + [false, false, false, false, false], + [false, false, false, false, false], + [false, false, false, false, false], + [false, false, false, false, false] + ]; + + for(let y=0; y<5; y++) + for(let x=0; x<5; x++) + { + let adjac = 0; + if (x>0 && grid[y][x-1]) adjac++; + if (y>0 && grid[y-1][x]) adjac++; + if (x<4 && grid[y][x+1]) adjac++; + if (y<4 && grid[y+1][x]) adjac++; + + if (grid[y][x] && adjac !== 1) g2[y][x] = false; // A bug dies (becoming an empty space) unless there is exactly one bug adjacent to it. + else if (!grid[y][x] && (adjac === 1 || adjac === 2)) g2[y][x] = true; // An empty space becomes infested with a bug if exactly one or two bugs are adjacent to it. + else g2[y][x] = grid[y][x]; // Otherwise, a bug or empty space remains the same. + } + return g2; + } +} diff --git a/www/statics/aoc/2019/24_solution-2.ts b/www/statics/aoc/2019/24_solution-2.ts new file mode 100644 index 0000000..700ce67 --- /dev/null +++ b/www/statics/aoc/2019/24_solution-2.ts @@ -0,0 +1,162 @@ +namespace AdventOfCode2019_24_2 +{ + const DAY = 24; + const PROBLEM = 2; + + export async function run() + { + let input = await AdventOfCode.getInput(DAY); + if (input == null) return; + + AdventOfCode.setIntermedOutputSize("0.70vw"); + + const grid0 = input + .split(new RegExp('\r?\n')) + .filter(p => p.trim().length > 0) + .map(p => p.trim().split('').map(q => q==='#')); + + let world: {[_:number]:boolean} = {}; + for(let y=0; y<5; y++) for(let x=0; x<5; x++) world[id(x, y, 0)] = grid0[y][x]; + + let minD = -1; + let maxD = +1; + + await AdventOfCode.outputIntermed(tostr2(world, minD, maxD)); + await AdventOfCode.sleepIfIntermed(500); + + for(let i=0; i<200; i++) + { + [minD, maxD] = step(world, minD, maxD); + + await AdventOfCode.outputIntermed(tostr2(world, minD, maxD)); + await AdventOfCode.sleepIfIntermed(50); + } + + let count = Object.values(world).filter(p => p).length; + + AdventOfCode.output(DAY, PROBLEM, count.toString()); + } + + function tostr1(world: {[_:number]:boolean}, minD: number, maxD: number): string + { + let str = ""; + + for(let d=minD; d<=maxD; d++) + { + str += "Depth "+d+":\n"; + for(let y=0; y<5; y++) + { + for(let x=0; x<5; x++) + { + str += (world[id(x, y, d)] === true) ? "#" : "."; + } + str += "\n"; + } + str += "\n"; + } + return str; + } + + function tostr2(world: {[_:number]:boolean}, minD: number, maxD: number): string + { + let str = ""; + + for (let y = 0; y<11*6; y++) + { + for (let x = 0; x<19*6; x++) + { + let mx = x%6; + let my = y%6; + if (mx===5 || my === 5) str += " " + else if (mx===2 && my === 2) str += "?" + else + { + let md = Math.floor(y/6) * 19 + Math.floor(x/6) - Math.floor((19*11)/2); + + str += (world[id(mx, my, md)] === true) ? "#" : "."; + } + } + str += "\n"; + } + return str; + } + + function id(x: number, y: number, depth: number): number + { + return depth*100 + x*10 + y; + } + + function step(world: {[_:number]:boolean}, minD: number, maxD: number): [number, number] + { + let newminD = minD; + let newmaxD = minD; + + let diff: [number, boolean, number][] = []; + + for(let d=minD; d<=maxD; d++) + for(let y=0; y<5; y++) + for(let x=0; x<5; x++) + { + if (x === 2 && y === 2) continue; + + let me_id = id(x, y, d); + let me = (world[me_id] === true); + + let adjac = 0; + if (x>0 && world[id(x-1, y, d)] === true) adjac++; + if (y>0 && world[id(x, y-1, d)] === true) adjac++; + if (x<4 && world[id(x+1, y, d)] === true) adjac++; + if (y<4 && world[id(x, y+1, d)] === true) adjac++; + + if (x === 0 && world[id(1, 2, d-1)] === true) adjac++; // WEST OUTER + if (y === 0 && world[id(2, 1, d-1)] === true) adjac++; // NORTH OUTER + if (x === 4 && world[id(3, 2, d-1)] === true) adjac++; // EAST OUTER + if (y === 4 && world[id(2, 3, d-1)] === true) adjac++; // SOUTH OUTER + + if (x === 2 && y === 1) // NORTH INNER + { + if (world[id(0, 0, d+1)] === true) adjac++; + if (world[id(1, 0, d+1)] === true) adjac++; + if (world[id(2, 0, d+1)] === true) adjac++; + if (world[id(3, 0, d+1)] === true) adjac++; + if (world[id(4, 0, d+1)] === true) adjac++; + } + if (x === 3 && y === 2) // EAST INNER + { + if (world[id(4, 0, d+1)] === true) adjac++; + if (world[id(4, 1, d+1)] === true) adjac++; + if (world[id(4, 2, d+1)] === true) adjac++; + if (world[id(4, 3, d+1)] === true) adjac++; + if (world[id(4, 4, d+1)] === true) adjac++; + } + if (x === 2 && y === 3) // SOUTH INNER + { + if (world[id(0, 4, d+1)] === true) adjac++; + if (world[id(1, 4, d+1)] === true) adjac++; + if (world[id(2, 4, d+1)] === true) adjac++; + if (world[id(3, 4, d+1)] === true) adjac++; + if (world[id(4, 4, d+1)] === true) adjac++; + } + if (x === 1 && y === 2) // WEST INNER + { + if (world[id(0, 0, d+1)] === true) adjac++; + if (world[id(0, 1, d+1)] === true) adjac++; + if (world[id(0, 2, d+1)] === true) adjac++; + if (world[id(0, 3, d+1)] === true) adjac++; + if (world[id(0, 4, d+1)] === true) adjac++; + } + + if (me && adjac !== 1) diff.push([me_id, false, d]); // A bug dies (becoming an empty space) unless there is exactly one bug adjacent to it. + else if (!me && (adjac === 1 || adjac === 2)) diff.push([me_id, true, d]); // An empty space becomes infested with a bug if exactly one or two bugs are adjacent to it. + } + + for (const [id, val, depth] of diff) + { + world[id] = val; + if (depth <= newminD) newminD = depth-1; + if (depth >= newmaxD) newmaxD = depth+1; + } + + return [newminD, newmaxD]; + } +} diff --git a/www/statics/aoc/__all.php b/www/statics/aoc/__all.php index 1e82388..2f50b79 100644 --- a/www/statics/aoc/__all.php +++ b/www/statics/aoc/__all.php @@ -47,6 +47,14 @@ return ['day' => 13, 'parts' => 2, 'title' => 'Care Package', 'language' => 'ts', 'solutions' => ['326', '15988'] ], ['day' => 14, 'parts' => 2, 'title' => 'Space Stoichiometry', 'language' => 'ts', 'solutions' => ['168046', '6972986'] ], ['day' => 15, 'parts' => 2, 'title' => 'Oxygen System', 'language' => 'ts', 'solutions' => ['244', '278'] ], - ['day' => 16, 'parts' => 2, 'title' => 'Flawed Frequency Transmission', 'language' => 'ts', 'solutions' => ['69549155', '83253465'] ], + ['day' => 16, 'parts' => 2, 'title' => 'Flawed Frequency Transmission', 'language' => 'ts', 'solutions' => ['69549155', '83253465'] ], + ['day' => 17, 'parts' => 2, 'title' => 'Set and Forget', 'language' => 'ts', 'solutions' => ['3448', '762405'] ], + ['day' => 18, 'parts' => 2, 'title' => 'Many-Worlds Interpretation', 'language' => 'ts', 'solutions' => ['7430', '1864'] ], + ['day' => 19, 'parts' => 2, 'title' => 'Tractor Beam', 'language' => 'ts', 'solutions' => ['181', '4240964'] ], + ['day' => 20, 'parts' => 2, 'title' => 'Donut Maze', 'language' => 'ts', 'solutions' => ['602', '6986'] ], + ['day' => 21, 'parts' => 2, 'title' => 'Springdroid Adventure', 'language' => 'ts', 'solutions' => ['19361023', '1141457530'] ], + ['day' => 22, 'parts' => 2, 'title' => 'Slam Shuffle', 'language' => 'ts', 'solutions' => ['1252', '46116012647793'] ], + ['day' => 23, 'parts' => 2, 'title' => 'Category Six', 'language' => 'ts', 'solutions' => ['27061', '19406'] ], + ['day' => 24, 'parts' => 2, 'title' => 'Planet of Discord', 'language' => 'ts', 'solutions' => ['25719471', '1916'] ], ], ]; \ No newline at end of file