1
0

aoc 17-24

This commit is contained in:
Mike Schwörer 2019-12-24 13:34:32 +01:00
parent f169fb40cc
commit 33588460d3
34 changed files with 6030 additions and 2 deletions

View File

@ -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));

View File

@ -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?

View File

@ -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

View File

@ -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<v.length;i++) this.data[i]=v[i];
}
r(pos: number): number
{
if (!(pos in this.data)) this.data[pos] = 0;
return this.data[pos];
}
w(pos: number, val: number): number
{
return this.data[pos] = val;
}
}
}

View File

@ -0,0 +1,686 @@
namespace AdventOfCode2019_17_2
{
const DAY = 17;
const PROBLEM = 2;
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 [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<number[]>
{
let allsplits: { [pos:number]: [ number, RoboCommand[], number[] ][] } = {};
for(let i=0; i<fullpath.length; i++) allsplits[i] = [];
let splitmap: { [pos:number]: [ RoboCommand[], number[] ] } = {};
let splitid = 10000;
for(let splitlen = 10; splitlen>0;splitlen--)
{
let localfounds: Set<number> = new Set<number>();
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<this.used_segments.length; i++)
{
if (this.used_segments[i][0] === seg[0])
{
this.used_segments[i][1]++;
added = true;
break;
}
}
if (!added)
{
if (this.used_segments.length === 3) continue;
this.used_segments.push([seg[0], 1]);
}
// ======== add to path ========
this.path.push(seg[0]);
// ======== inc pos ========
this.position += seg[1].length;
// ======== recursion ========
this.run();
// ======== dec pos ========
this.position -= seg[1].length;
// ======== rem from path ========
this.path.pop();
// ======== rem from used_segments ========
let rem_us = false;
for(let i=0; i<this.used_segments.length; i++)
{
if (this.used_segments[i][0] === seg[0])
{
this.used_segments[i][1]--;
if (this.used_segments[i][1] === 0) rem_us = true;
break;
}
}
if (rem_us)
{
this.used_segments = this.used_segments.filter(p => 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<len; i++)
{
if (!(path[start1+i].eq(path[start2+i]))) return false;
}
return true;
}
function getFullPath(map: {[_:number]: number}, maxx: number, maxy: number)
{
let robox = -1;
let roboy = -1;
let robod = Direction.North;
for(let yy=0;yy<=maxy;yy++)
for(let xx=0;xx<=maxx;xx++)
{
let id = (yy*1_0000 + xx);
if (map[id] === 94) { robox=xx; roboy=yy; robod=Direction.North; break; }
if (map[id] === 62) { robox=xx; roboy=yy; robod=Direction.East; break; }
if (map[id] === 60) { robox=xx; roboy=yy; robod=Direction.West; break; }
if (map[id] === 118) { robox=xx; roboy=yy; robod=Direction.South; break; }
}
let result: RoboCommand[] = [];
let pos: [number, number] = [robox, roboy];
let dir = robod;
let moves = 0;
for(;;)
{
if (isScaffolding(map, moveDir(pos, dir)))
{
moves++;
pos = moveDir(pos, dir);
}
else if (isScaffolding(map, moveDir(pos, turnLeft(dir))))
{
if (moves > 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<v.length;i++) this.data[i]=v[i];
}
r(pos: number): number
{
if (!(pos in this.data)) this.data[pos] = 0;
return this.data[pos];
}
w(pos: number, val: number): number
{
return this.data[pos] = val;
}
}
}

View File

@ -0,0 +1,246 @@
--- Day 18: Many-Worlds Interpretation ---
As you approach Neptune, a planetary security system detects you and activates a giant tractor beam on Triton! You have no choice but to land.
A scan of the local area reveals only one interesting feature: a massive underground vault. You generate a map of the tunnels (your puzzle input). The tunnels are too narrow to move diagonally.
Only one entrance (marked @) is present among the open passages (marked .) and stone walls (#), but you also detect an assortment of keys (shown as lowercase letters) and doors (shown as uppercase letters). Keys of a given letter open the door of the same letter: a opens A, b opens B, and so on. You aren't sure which key you need to disable the tractor beam, so you'll need to collect all of them.
For example, suppose you have the following map:
#########
#b.A.@.a#
#########
Starting from the entrance (@), you can only access a large door (A) and a key (a). Moving toward the door doesn't help you, but you can move 2 steps to collect the key, unlocking A in the process:
#########
#b.....@#
#########
Then, you can move 6 steps to collect the only other key, b:
#########
#@......#
#########
So, collecting every key took a total of 8 steps.
Here is a larger example:
########################
#f.D.E.e.C.b.A.@.a.B.c.#
######################.#
#d.....................#
########################
The only reasonable move is to take key a and unlock door A:
########################
#f.D.E.e.C.b.....@.B.c.#
######################.#
#d.....................#
########################
Then, do the same with key b:
########################
#f.D.E.e.C.@.........c.#
######################.#
#d.....................#
########################
...and the same with key c:
########################
#f.D.E.e.............@.#
######################.#
#d.....................#
########################
Now, you have a choice between keys d and e. While key e is closer, collecting it now would be slower in the long run than collecting key d first, so that's the best choice:
########################
#f...E.e...............#
######################.#
#@.....................#
########################
Finally, collect key e to unlock door E, then collect key f, taking a grand total of 86 steps.
Here are a few more examples:
########################
#...............b.C.D.f#
#.######################
#.....@.a.B.c.d.A.e.F.g#
########################
Shortest path is 132 steps: b, a, c, d, f, e, g
#################
#i.G..c...e..H.p#
########.########
#j.A..b...f..D.o#
########@########
#k.E..a...g..B.n#
########.########
#l.F..d...h..C.m#
#################
Shortest paths are 136 steps;
one is: a, f, b, j, g, n, h, d, l, o, e, p, c, i, k, m
########################
#@..............ac.GI.b#
###d#e#f################
###A#B#C################
###g#h#i################
########################
Shortest paths are 81 steps; one is: a, c, f, i, d, g, b, e, h
How many steps is the shortest path that collects all of the keys?
--- Part Two ---
You arrive at the vault only to discover that there is not one vault, but four - each with its own entrance.
On your map, find the area in the middle that looks like this:
...
.@.
...
Update your map to instead use the correct data:
@#@
###
@#@
This change will split your map into four separate sections, each with its own entrance:
####### #######
#a.#Cd# #a.#Cd#
##...## ##@#@##
##.@.## --> #######
##...## ##@#@##
#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?

View File

@ -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..#.....#.....#.......#.........#.........#
#################################################################################

View File

@ -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<boolean>(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<boolean>(26).fill(false), 0, 0, ""]);
let best_result: [number, string]|null = null;
let seen = new Set<string>();
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<string, [number, boolean[]]> = new Map<string, [number, boolean[]]>(); // [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<this.height;y++)
for(let x=0; x<this.width;x++)
{
const v = String.fromCharCode(this.get([x,y]));
if (v === "#") continue;
if (v === ".") continue;
if (v === "@") { this.start = [x, y]; continue; }
if (v.charCodeAt(0) >= "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.length; i++)
{
sum += this.reachability.get(path.charAt(i-1)+">"+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<this.height;y++)
{
for(let x=0; x<this.width;x++)
{
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);
}
async dumpWithFill(visited_map: { [_: number]: any; })
{
let str = "";
for(let y=0; y<this.height;y++)
{
for(let x=0; x<this.width;x++)
{
const i = (y*10000000 + x);
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);
}
async dumpWithFillAndNext(visited_map: { [_: number]: any; }, next: [number, number, any, any][])
{
let str = "";
for(let y=0; y<this.height;y++)
{
for(let x=0; x<this.width;x++)
{
const i = (y*10000000 + x);
if (next.findIndex(p => 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);
}
}
}

View File

@ -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<string, number>();
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<grid.length; y++)
for(let x=0; x<grid[y].length; x++)
{
if (grid[y][x] === needle) return [x, y];
}
throw "";
}
class State
{
Positions: PSet;
OwnedKeys: number;
Steps: number = 0;
constructor(s: PSet, ok: number, ss: number) { this.Positions=s; this.OwnedKeys=ok; this.Steps = ss; }
strPositionsOwnedKeys()
{
return "" +
this.Positions[0][0]+";" +
this.Positions[0][1]+";" +
this.Positions[1][0]+";" +
this.Positions[1][1]+";" +
this.Positions[2][0]+";" +
this.Positions[2][1]+";" +
this.Positions[3][0]+";" +
this.Positions[0][1]+";" +
this.OwnedKeys+";";
}
}
function ReachableKeys(map: number[][], start: P, currentKeys: string): ReachableKey[]
{
let list: ReachableKey[] = [];
let visited: Set<number> = new Set<number>();
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;
}
}

View File

@ -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.)

View File

@ -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

View File

@ -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<v.length;i++) this.data[i]=v[i];
}
r(pos: number): number
{
if (!(pos in this.data)) this.data[pos] = 0;
return this.data[pos];
}
w(pos: number, val: number): number
{
return this.data[pos] = val;
}
}
}

View File

@ -0,0 +1,375 @@
namespace AdventOfCode2019_19_2
{
const DAY = 19;
const PROBLEM = 2;
export async function run()
{
let input = await AdventOfCode.getInput(DAY);
if (input == null) return;
AdventOfCode.setIntermedOutputSize("0.4vw");
const code = input.trim().split(",").map(p => 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<v.length;i++) this.data[i]=v[i];
}
r(pos: number): number
{
if (!(pos in this.data)) this.data[pos] = 0;
return this.data[pos];
}
w(pos: number, val: number): number
{
return this.data[pos] = val;
}
}
}

View File

@ -0,0 +1,203 @@
--- Day 20: Donut Maze ---
You notice a strange pattern on the surface of Pluto and land nearby to get a closer look. Upon closer inspection, you realize you've come across one of the famous space-warping mazes of the long-lost Pluto civilization!
Because there isn't much space on Pluto, the civilization that used to live here thrived by inventing a method for folding spacetime. Although the technology is no longer understood, mazes like this one provide a small glimpse into the daily life of an ancient Pluto citizen.
This maze is shaped like a donut. Portals along the inner and outer edge of the donut can instantly teleport you from one side to the other. For example:
A
A
#######.#########
#######.........#
#######.#######.#
#######.#######.#
#######.#######.#
##### B ###.#
BC...## C ###.#
##.## ###.#
##...DE F ###.#
##### G ###.#
#########.#####.#
DE..#######...###.#
#.#########.###.#
FG..#########.....#
###########.#####
Z
Z
This map of the maze shows solid walls (#) and open passages (.). Every maze on Pluto has a start (the open tile next to AA) and an end (the open tile next to ZZ). Mazes on Pluto also have portals; this maze has three pairs of portals: BC, DE, and FG. When on an open tile next to one of these labels, a single step can take you to the other tile with the same label. (You can only walk on . tiles; labels and empty space are not traversable.)
One path through the maze doesn't require any portals. Starting at AA, you could go down 1, right 8, down 12, left 4, and down 1 to reach ZZ, a total of 26 steps.
However, there is a shorter path: You could walk from AA to the inner BC portal (4 steps), warp to the outer BC portal (1 step), walk to the inner DE (6 steps), warp to the outer DE (1 step), walk to the outer FG (4 steps), warp to the inner FG (1 step), and finally walk to ZZ (6 steps). In total, this is only 23 steps.
Here is a larger example:
A
A
#################.#############
#.#...#...................#.#.#
#.#.#.###.###.###.#########.#.#
#.#.#.......#...#.....#.#.#...#
#.#########.###.#####.#.#.###.#
#.............#.#.....#.......#
###.###########.###.#####.#.#.#
#.....# A C #.#.#.#
####### S P #####.#
#.#...# #......VT
#.#.#.# #.#####
#...#.# YN....#.#
#.###.# #####.#
DI....#.# #.....#
#####.# #.###.#
ZZ......# QG....#..AS
###.### #######
JO..#.#.# #.....#
#.#.#.# ###.#.#
#...#..DI BU....#..LF
#####.# #.#####
YN......# VT..#....QG
#.###.# #.###.#
#.#...# #.....#
###.### J L J #.#.###
#.....# O F P #.#...#
#.###.#####.#.#####.#####.###.#
#...#.#.#...#.....#.....#.#...#
#.#####.###.###.#.#.#########.#
#...#.#.....#...#.#.#.#.....#.#
#.###.#####.###.###.#.#.#######
#.#.........#...#.............#
#########.###.###.#############
B J C
U P P
Here, AA has no direct path to ZZ, but it does connect to AS and CP. By passing through AS, QG, BU, and JO, you can reach ZZ in 58 steps.
In your maze, how many steps does it take to get from the open tile marked AA to the open tile marked ZZ?
--- Part Two ---
Strangely, the exit isn't open when you reach it. Then, you remember: the ancient Plutonians were famous for building recursive spaces.
The marked connections in the maze aren't portals: they physically connect to a larger or smaller copy of the maze. Specifically, the labeled tiles around the inside edge actually connect to a smaller copy of the same maze, and the smaller copy's inner labeled tiles connect to yet a smaller copy, and so on.
When you enter the maze, you are at the outermost level; when at the outermost level, only the outer labels AA and ZZ function (as the start and end, respectively); all other outer labeled tiles are effectively walls. At any other level, AA and ZZ count as walls, but the other outer labeled tiles bring you one level outward.
Your goal is to find a path through the maze that brings you back to ZZ at the outermost level of the maze.
In the first example above, the shortest path is now the loop around the right side. If the starting level is 0, then taking the previously-shortest path would pass through BC (to level 1), DE (to level 2), and FG (back to level 1). Because this is not the outermost level, ZZ is a wall, and the only option is to go back around to BC, which would only send you even deeper into the recursive maze.
In the second example above, there is no path that brings you to ZZ at the outermost level.
Here is a more interesting example:
Z L X W C
Z P Q B K
###########.#.#.#.#######.###############
#...#.......#.#.......#.#.......#.#.#...#
###.#.#.#.#.#.#.#.###.#.#.#######.#.#.###
#.#...#.#.#...#.#.#...#...#...#.#.......#
#.###.#######.###.###.#.###.###.#.#######
#...#.......#.#...#...#.............#...#
#.#########.#######.#.#######.#######.###
#...#.# F R I Z #.#.#.#
#.###.# D E C H #.#.#.#
#.#...# #...#.#
#.###.# #.###.#
#.#....OA WB..#.#..ZH
#.###.# #.#.#.#
CJ......# #.....#
####### #######
#.#....CK #......IC
#.###.# #.###.#
#.....# #...#.#
###.### #.#.#.#
XF....#.# RF..#.#.#
#####.# #######
#......CJ NM..#...#
###.#.# #.###.#
RE....#.# #......RF
###.### X X L #.#.#.#
#.....# F Q P #.#.#.#
###.###########.###.#######.#########.###
#.....#...#.....#.......#...#.....#.#...#
#####.#.###.#######.#######.###.###.#.#.#
#.......#.......#.#.#.#.#...#...#...#.#.#
#####.###.#####.#.#.#.#.###.###.#.###.###
#.......#.....#.#...#...............#...#
#############.#.#.###.###################
A O F N
A A D M
One shortest path through the maze is the following:
Walk from AA to XF (16 steps)
Recurse into level 1 through XF (1 step)
Walk from XF to CK (10 steps)
Recurse into level 2 through CK (1 step)
Walk from CK to ZH (14 steps)
Recurse into level 3 through ZH (1 step)
Walk from ZH to WB (10 steps)
Recurse into level 4 through WB (1 step)
Walk from WB to IC (10 steps)
Recurse into level 5 through IC (1 step)
Walk from IC to RF (10 steps)
Recurse into level 6 through RF (1 step)
Walk from RF to NM (8 steps)
Recurse into level 7 through NM (1 step)
Walk from NM to LP (12 steps)
Recurse into level 8 through LP (1 step)
Walk from LP to FD (24 steps)
Recurse into level 9 through FD (1 step)
Walk from FD to XQ (8 steps)
Recurse into level 10 through XQ (1 step)
Walk from XQ to WB (4 steps)
Return to level 9 through WB (1 step)
Walk from WB to ZH (10 steps)
Return to level 8 through ZH (1 step)
Walk from ZH to CK (14 steps)
Return to level 7 through CK (1 step)
Walk from CK to XF (10 steps)
Return to level 6 through XF (1 step)
Walk from XF to OA (14 steps)
Return to level 5 through OA (1 step)
Walk from OA to CJ (8 steps)
Return to level 4 through CJ (1 step)
Walk from CJ to RE (8 steps)
Return to level 3 through RE (1 step)
Walk from RE to IC (4 steps)
Recurse into level 4 through IC (1 step)
Walk from IC to RF (10 steps)
Recurse into level 5 through RF (1 step)
Walk from RF to NM (8 steps)
Recurse into level 6 through NM (1 step)
Walk from NM to LP (12 steps)
Recurse into level 7 through LP (1 step)
Walk from LP to FD (24 steps)
Recurse into level 8 through FD (1 step)
Walk from FD to XQ (8 steps)
Recurse into level 9 through XQ (1 step)
Walk from XQ to WB (4 steps)
Return to level 8 through WB (1 step)
Walk from WB to ZH (10 steps)
Return to level 7 through ZH (1 step)
Walk from ZH to CK (14 steps)
Return to level 6 through CK (1 step)
Walk from CK to XF (10 steps)
Return to level 5 through XF (1 step)
Walk from XF to OA (14 steps)
Return to level 4 through OA (1 step)
Walk from OA to CJ (8 steps)
Return to level 3 through CJ (1 step)
Walk from CJ to RE (8 steps)
Return to level 2 through RE (1 step)
Walk from RE to XQ (14 steps)
Return to level 1 through XQ (1 step)
Walk from XQ to FD (8 steps)
Return to level 0 through FD (1 step)
Walk from FD to ZZ (18 steps)
This path takes a total of 396 steps to move from AA at the outermost layer to ZZ at the outermost layer.
In your maze, when accounting for recursion, how many steps does it take to get from the open tile marked AA to the open tile marked ZZ, both at the outermost layer?

View File

@ -0,0 +1,121 @@
I U O M N O S
X T X C D N A
#########################################.#####.#######.###.###########.#####.#######.#######################################
#.....#.......#.....#.....#.....#...#.#...#...#...#.....#.#...........#.....#.....#.............#.#...#...#...#.......#.....#
#####.#######.#####.#####.#.###.###.#.#.###.#.###.#.#.###.###.###.#####.#.#######.#####.#.#######.###.#.###.###.#######.#####
#.#.#...#...#.....#.........#...........#.#.#...#.#.#.....#...#.......#.#...#.#.....#.#.#...............#.........#.........#
#.#.###.###.###.#####.#######.#####.#.###.#.###.#.#.#########.#.#########.###.#.#####.###.###.#####.#.#####.###.#########.###
#.....#.........#.....#...#.#.....#.#.....#.#...#.#.#...#...#.#.....#...#.#.#.........#.....#.....#.#.........#.#...#...#...#
#####.#####.#.#.###.#####.#.#.#####.#.#.#.#.###.#.#.###.###.#####.#####.#.#.#####.#######.###.#.#.#######.###.#####.###.#.###
#...#.......#.#.......#.......#.#.#.#.#.#.#...#.#.#.......#.#.#.....#.........#.....#.....#.#.#.#.......#.#.........#.......#
#.###############.#####.#####.#.#.#######.###.#.#.###.#.###.#.#.#.###.###.#####.#######.#.#.###.#.###.#.#.###.#####.#####.#.#
#...#.#.#...#.......#.......#...#.....#...#...#.#.#...#.....#...#...#.#...#.......#...#.#.....#.#...#.#.#.#.#.#.....#.#.#.#.#
#.###.#.#.#######.#.#.#.#####.###.###.#.#.#.###.#.#.###.#.###.###########.#####.#####.###.#.#.#.#.#########.###.#####.#.#.###
#.#.......#...#...#.#.#...#...#.#.#.....#.#...#...#.#...#.#.......#.#.#.#.#.........#.....#.#.#.#...........#.#...#.#.#.#...#
#.###.###.#.#####.###.#.###.###.###.#####.###.#.#.#.#############.#.#.#.#.#######.#.###.#.#####.#.#.#####.#.#.#####.#.#.#.###
#.#...#...#.#.#.#...#.#...#.#.....#.#.....#...#.#.#...#.#.#...........#.....#.....#.#...#.....#.#.#.....#.#.....#...#.#.#.#.#
#.#######.#.#.#.#.#############.#####.###.#.#######.#.#.#.#.###.#.#####.#########.#####.#################.#######.#.#.#.#.#.#
#.....#.......#.#.#...#...#...#...#.#.#...#.....#...#.#.....#.#.#.#.......#.#.#...#.......#.#...#.#.....#.....#.#.#.....#...#
###.#########.#.###.#####.#.###.###.###.#.#.#######.###.###.#.#######.###.#.#.#.#.#.#####.#.#.###.#.#.#####.###.#######.#.###
#.#.....#.#...#...#...#.#.#...#...#...#.#.#.....#...#...#.......#...#.#.......#.#.#.#.........#.....#...#.#.#.#...#.........#
#.###.###.###.###.#.###.#.###.#.#####.###.#.#.###.#.#########.###.#.#####.#######.###.###.#####.#.#.#####.###.###.#######.###
#.........#.#...#.#.#...........#.#.......#.#...#.#.#...#.........#.#.....#.#.......#.#.#.#.#...#.#.#.....#.#...#.#.....#...#
###.#.#####.#.###.#.#####.#####.#.#######.#.###.###.#.#####.#.#.#########.#.#####.#####.#.#.#.#.#######.###.#.###.#.#.###.###
#.#.#.#...#.........#.#.#.#.#.#...#.#.....#.#.#.#.......#.#.#.#.#...........#.#.......#.....#.#.#.#...#...#...#.....#...#.#.#
#.#.#####.#.###.###.#.#.###.#.#.#.#.#####.#.#.#.###.#.###.#####.#####.#######.###.#######.###.#.#.#.###.#####.###.#.###.#.#.#
#...#.#.#.#.#.#.#.....#.........#.#...#...#...#.#...#.#...#.......#...#...#...........#.#.....#.#...#.#.#...#...#.#.#.......#
###.#.#.#.###.#####.#.#####.#.#.###.###.#.#.#.#####.#####.#.#.#.#.#.###.#.#.###.#.#.###.#.#.#####.###.#.###.#.#.#########.###
#...#.#...#...#.#.#.#.......#.#.........#.#.#.#...........#.#.#.#.#.#...#.#...#.#.#.#.#.#.#.#.............#.#.#.#.....#.#.#.#
###.#.#.###.###.#.###.#.#.#####.###.#####.#.#######.#############.#.#.###.###.#######.#.#.###.###.#.#######.#.#######.#.#.#.#
#.#.....#.#.#.#.#.....#.#.#.#...#.......#.#...#...........#...#...#...#...#...#...#...........#...#.#.#...#.....#...#.#.....#
#.#####.#.#.#.#.#########.#.#.#.#.#.###.###.#######.#######.###.###.###.#.###.#.#####.###.#.#########.#.###.#######.#.###.###
#...#.....#.#.#.#.#.#...#.#...#.#.#.#.#...#.......#.........#.....#.#...#.#.....#.#.#...#.#.#.#...#.....#.....#.#...#.#.....#
#.#######.#.#.#.#.#.#.###.#.#.#.#.###.###.#.#.#####.#.###.#####.#######.###.#.###.#.#.#.#.#.#.###.###.#####.#.#.#.###.#.#####
#...#.#...#...........#...#.#.#.#.#.......#.#...#...#.#...#.......#.......#.#...#.....#.#.#...#.......#...#.#.......#.....#.#
#.#.#.###.###.###.#############.#########.###.#########.#####.#######.#####.#######.#################.#.#######.#####.###.#.#
#.#.....#.#.#.#.#.....#.#.....#.# C S P R W B A #...#.#...#...#.#.....#...#...#.#
###.#.###.#.#.#.#######.###.###.# S A P D A E D #.###.###.#.###.#.###########.#.#
#...#.#...........#...#.....#...# #.#.......#.........#.#.#.....#.#
###.#####.#####.###.###.###.##### #.#####.#.#.#.#.#.###.#.###.#.#.#
#...#.#...#.........#.....#.#...# #.....#.#...#.#.#.#.#.......#...#
###.#.###.#.###########.#####.### #.#.#.#.###.###.#.#.#######.#.#.#
EY........#.#...#...#.#.......#.#.# #.#.#.#...#.#.#.#...#.......#.#.#
#.###.#.#.#.#.#.#.#.###.#####.#.# #.#.#####.###.###.#####.#.###.###
#.#...#...#.#...#................RB VN..#.......#.....#.......#.#......CS
#############.#########.#.####### #############.#.###.#######.###.#
#...#.#...#.....#.#.#...#.#.....# #.........#...#.#...#.#...#...#.#
#.###.###.###.###.#.###.#####.#.# ###.###.###.#.#######.#.#########
BE....#...#...#.#.......#.#.#...#.# ON..#.#...#...#.#...#...#.#.#...#.#
#.#####.###.#######.#####.###.#.# #.#.#.#####.#.#.#####.#.#.#.###.#
#.#.#.......#.........#.....#.#.# #.#.#.#...#.#.#.#.........#.#.#..MR
#.#.#.#####.#####.#.#.###.###.#.# #.#.#.#####.#.#.###.#.#####.#.#.#
#.....#...#.......#.#.........#..QG WP....#.......#.......#............VN
#######.###.###.###.############# ###.###########.#.#######.#######
ZM........#.....#...#.#...#.....#.# #.#...#...#...#.#.#.....#.#.....#
#.#.#########.#.#####.#######.#.# #.#####.#.###.#######.#.###.#.###
#.#.......#.#.#.....#.#.....#....ND UE........#...........#.#.#...#...#
###.#######.#########.###.#.#.### #.###.#.#.#####.###.#.#.###.#.###
#...#...#...#...#.......#.#.....# #.#.#.#.#...#...#...#.#.....#....GN
#.#####.###.#.###.###.#####.#.### #.#.###########.#.###.#####.#.#.#
#...................#.......#...# #.#.......#.#.#.#.......#...#.#.#
#####.#####.###.#.#####.######### ###.#.#.###.#.###.###############
#...#.....#.#...#.#...#.#.#.....# UT....#.#.#.#.#...#.#.#.......#....WA
#.#.###############.###.#.#.##### ###.#####.#.#.###.#.#.###.#.#.#.#
WP..#...#.....#.#.#.....#.#.......# #.....#.....#.#.....#.#...#...#.#
#.#.#.###.###.#.###.#.###.#.##### #.#.#.###.###.#########.###.#.###
#.#.#.....#.....#.#.#.....#.....# #.#.#.#.......#.#.#.....#...#.#.#
#######.#.#.#.###.#.#.#.#.####### #.#####.###.#.#.#.#####.###.#.#.#
#...#.#.#...#.......#.#.#........OX #.........#.#...........#...#.#.#
#.###.###########.###.###.#.###.# #.#######.#####.###########.###.#
#.......#.....#...#...#...#.#...# #.#.....#.#.#.#.#.......#...#....PP
#.###.#.###.#.################### ###.###.###.#.###.#.###.#####.###
NO..#...#.#...#.#.#.....#...#.....# ZR..#.#.#...#.....#.#.#.....#.....#
###.#####.###.#.#.#####.###.###.# #.#.#.#.#####.#####.###.#####.#.#
#...#...#...#.....#.#.........#..FK #.....#.....#.#...#.#...#...#.#.#
#.###.#.###.#####.#.###.#.#.#.#.# #.###.###.###.###.#.###.#.#.###.#
ZZ......#.......#.#.......#.#.#.#.# #.#...#.............#.....#.....#
#.###.###.#.###.###.############# ###.#####.###.#################.#
#.#...#.#.#.......#.#.#.........# #.....#.....#.#...............#.#
#######.#####.#.#.###.#.###.###.# #########.#####.###.#######.#####
RX..#.#.#.....#.#.#.#.#.....#.#....GN #.#...#...#.....#.....#.#........UE
#.#.#.#.#.#########.###.#.###.#.# #.#.#####.###.#.###.###.###.###.#
#.....#.#.....#.#.....#.#...#.#.# ZM....#...#.#...#.#.#.......#...#.#
#.#.#.#.#.#####.###.#####.#.#.#.# #.#.###.###.#.###.#.#.#####.###.#
#.#.#...#.................#.#.#.# #.#.........#...#...#...#.....#.#
#.###.#.#####.###.#.#####.#####.# M P I R E N M #.###.#.#.###.#######.#####.###.#
#.#...#.....#...#.#...#.#...#.#.# C G X X Y O R #...#.#.#.#...#.....#.....#...#.#
#####.###.###.#.#.#####.#.###.#######.#######.###########.#####.#####.#####.###########.#########.#######.#.#####.#.#.###.###
#.#.#...#.#...#.#.#.............#.....#.#.#...#...#.........#...#.......#.......#.#...#.#.......#.#.............#.#.#.#.#...#
#.#.#.#.#.###.###.###.###.###.###.#.#.#.#.###.#.#.###.#########.#######.#######.#.###.#.###.#.#####.###.#.###.#########.#####
#...#.#.#...#...#...#...#...#.#...#.#...#.#.....#.#...#.....#...#.#.#.#.....#.#.#.#.#.#.....#.#...#...#.#.#.............#...#
###.#.#######.#.#####.###.#.#####.#.###.#.#######.#.###.#.###.#.#.#.#.###.###.#.#.#.#.#.#######.#######.#####.###.#######.###
#.......#.....#.#.......#.#...#...#.#.....#...#.#.#.....#...#.#.#...........#.#...#...#...........#.#.#...#.....#...........#
#.#.#.#.#.#.#.#.#.#.#.#.###.#.###.#.#.###.###.#.#.#######.#####.###.###.#.###.#.###.#.#.#.###.#.#.#.#.#.###.#.###.###.###.###
#.#.#.#.#.#.#.#.#.#.#.#.#...#...#.#.#.#.#.#.#.........#...#.#...#.....#.#...#...#...#...#...#.#.#.#.#...#...#.#.....#.#.#...#
###.#.#####.#######.#.###.###.###.#####.#.#.#.#####.#####.#.###.#####.#.#.#####.#.#.###.#######.#.#.#.#######.#######.#.#####
#...#...#...#.#.#...#...#.#.#...#.#.......#...#.#...#.....#.....#.....#.#...#...#.#.#.........#.#.#...#.........#...........#
###.#####.###.#.#.#.#######.#.#########.#.#.###.#.#####.#.#.#.#####.#####.#####.#.#########.#########.#####.#.###.#####.###.#
#.....#.....#.....#.......#.....#.......#.#.....#...#...#.#.#.....#.#.#.#.#...#.#.......#...........#.#.#.#.#...#...#.....#.#
###.#.###.#######.###.###.###.###.###.###.#.#.###.###.###.#######.#.#.#.#####.#.#.#.#########.###.#.#.#.#.#####.#.#.#.#.#.#.#
#...#...#...#.#.....#.#.....#.#...#.#.#...#.#...#.#.#.#.#.#.#...#.#.....#.#...#.#.#.........#.#.#.#.#.........#.#.#.#.#.#.#.#
#.###.#.###.#.#.#.#.#####.#########.#####.#.#######.###.#.#.###.#.#.###.#.###.#.#####.#######.#.#######.#.#########.###.#.#.#
#.#...#.#...#...#.#.#.......#...#.#.......#.#.......#...#...#...#.#...#.#.#.......#...#.#.#.#...#.....#.#.#.#...#.....#.#.#.#
#.#.#.#.#####.#.#.#####.#####.###.#.#####.#.###.#######.#.#####.#.#####.#.###.#######.#.#.#.###.#.#####.###.#.#####.#.#######
#.#.#.#...#...#.#.#.......#.#.#.#.#.#.#.#.#.#...#.#.#...#...#.#.......#.....#...#...........#.#...#.#.....#...#...#.#...#.#.#
###.###.#.#.#########.#####.#.#.#.###.#.#.#.###.#.#.###.#.###.#.###.#.###.###.#####.###.#####.#####.#.#.###.#####.###.###.#.#
#.....#.#.#.#.........#.......#.#...#.....#.....#.....#...#.......#.#...#.#.......#...#.......#.....#.#...#...#.....#.#.#...#
#.#.###.#.#.#.###.#.#######.#.#.#.#####.#.#.###.###.#.#.#####.#.#####.###.###.#########.###.#.###.#####.###.###.#.#####.#.###
#.#...#.#.#.#.#.#.#...#.....#.........#.#.#.#.....#.#.#...#.#.#...#.#.#.#.#.......#.......#.#...#...#.#.........#...........#
#.#.###.#######.#.#.#####.#########.#####.#######.#.#.#.###.###.#.#.###.#.#####.#######.#########.###.#.###.#.#.###.#.#.###.#
#.#.#.......#...#.#.#.#...#...#.....#.#...#.......#.#.......#...#.#.........#...#...............#.#.#.#.#.#.#.#...#.#.#.#.#.#
#.#####.#######.#####.#######.#.###.#.###.#.#######.###########.#.#.###########.#####.###.###.###.#.#.###.###.#.#####.###.###
#.#.....#...............#.......#.....#...#.......#...#...#...#.#.#...#.......#.....#...#...#...#.......#...#.#.....#.#...#.#
#.#.###.#.#.#.#.#######.#.#.#.#######.###.#######.###.###.#.#.###.###.#####.#.#.#.###.###########.###.###.#####.#.#####.###.#
#.#.#...#.#.#.#.#.#.......#.#...#.........#.........#...#.#.#...#.#.#...#.#.#...#.#.......#.........#.........#.#.#...#...#.#
#.#.###.###.#####.#.#####.#.#####.###.#.###.#.#######.###.###.#.#.#.###.#.#.###.#####.#.###.#####.#.#.###.#####.#####.#.###.#
#.#.#.#.#...#...#...#.....#.#.....#...#.#.#.#...#...#...#.#...#...#.......#...#.....#.#.......#.#.#.#.#.....#.#.............#
#####.#.#.#.#.#.###.#.###.#####.#####.###.#.###.#.#.#.###.#.#######.###.#####.#######.#.#.#.###.#.#.###.#####.###.#.#.###.#.#
#.......#.#.#.#.....#.#.....#...#.......#.....#.#.#.......#.......#...#.#...........#.#.#.#.....#.#.#...........#.#.#...#.#.#
#####################################.#######.#########.#.#######.#.###########.#######.#####################################
Z A R A F Q R P
R D B A K G D G

View File

@ -0,0 +1,163 @@
namespace AdventOfCode2019_20_1
{
const DAY = 20;
const PROBLEM = 1;
type Point = [number, number];
export async function run()
{
let input = await AdventOfCode.getInput(DAY);
if (input == null) return;
AdventOfCode.setIntermedOutputSize("0.55vw");
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]: Point} = {};
let entry: Point = [0, 0];
let exit: Point = [0, 0];
for (let y=1; y<height-1; y++)
for (let x=1; x<width-1; x++)
{
let v = grid[y][x];
if (isUpperChar(v))
{
let key;
let pos: Point;
if (grid[y-1][x] === "." && isUpperChar(grid[y+1][x]))
{
key = v + grid[y+1][x];
pos = [x, y-1];
}
else if (grid[y][x+1] === "." && isUpperChar(grid[y][x-1]))
{
key = grid[y][x-1] + v;
pos = [x+1, y];
}
else if (grid[y+1][x] === "." && isUpperChar(grid[y-1][x]))
{
key = grid[y-1][x] + v;
pos = [x, y+1];
}
else if (grid[y][x-1] === "." && isUpperChar(grid[y][x+1]))
{
key = v + grid[y][x+1];
pos = [x-1, y];
}
else continue;
if (key === "AA")
{
entry = pos;
AdventOfCode.outputConsole("[AA] := " + point_to_str(pos));
}
else if (key === "ZZ")
{
exit = pos;
AdventOfCode.outputConsole("[ZZ] := " + point_to_str(pos));
}
else if (key in tunnels)
{
tunnels[key][1] = pos;
tunnelsList[id(tunnels[key][0])] = tunnels[key][1];
tunnelsList[id(tunnels[key][1])] = tunnels[key][0];
AdventOfCode.outputConsole("[PORTAL] := " + point_to_str(tunnels[key][0]) + " <--> " + point_to_str(tunnels[key][1]));
}
else
{
tunnels[key] = [pos, [-1, -1]];
}
}
}
for (let y=0; y<height; y++)
for (let x=0; x<width; x++)
{
let v = grid[y][x];
if (v === " ") v = "#";
if (isUpperChar(v)) v = "#";
grid[y][x] = v;
}
await AdventOfCode.outputIntermed(toStr(grid));
// ------------------
let distmap: {[id:number]: number} = {};
let dqueue: [number, Point][] = [];
dqueue.push([0, entry]);
while (dqueue.length>0)
{
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<height; y++)
{
for (let x=0; x<width; x++)
{
if (highlights.some( p => 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]}]`; }
}

View File

@ -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; y<height-1; y++)
for (let x=1; x<width-1; x++)
{
let v = grid[y][x];
if (isUpperChar(v))
{
let isinner = x>3 && y>3 && x<width-3 && y<height-3;
let key;
let pos: Point;
if (grid[y-1][x] === "." && isUpperChar(grid[y+1][x]))
{
key = v + grid[y+1][x];
pos = [x, y-1];
}
else if (grid[y][x+1] === "." && isUpperChar(grid[y][x-1]))
{
key = grid[y][x-1] + v;
pos = [x+1, y];
}
else if (grid[y+1][x] === "." && isUpperChar(grid[y-1][x]))
{
key = grid[y-1][x] + v;
pos = [x, y+1];
}
else if (grid[y][x-1] === "." && isUpperChar(grid[y][x+1]))
{
key = v + grid[y][x+1];
pos = [x-1, y];
}
else continue;
if (key === "AA")
{
entry = pos;
AdventOfCode.outputConsole("[AA] := " + point_to_str(pos));
}
else if (key === "ZZ")
{
exit = pos;
AdventOfCode.outputConsole("[ZZ] := " + point_to_str(pos));
}
else if (key in tunnels)
{
if (isinner) tunnels[key][0] = pos;
else tunnels[key][1] = pos;
tunnelsList[id(tunnels[key][0])] = [true, tunnels[key][1]]; // down
tunnelsList[id(tunnels[key][1])] = [false, tunnels[key][0]]; // up
AdventOfCode.outputConsole("[PORTAL] := " + point_to_str(tunnels[key][0]) + " --> " + 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<height; y++)
for (let x=0; x<width; x++)
{
let v = grid[y][x];
if (isUpperChar(v)) v = " ";
grid[y][x] = v;
}
await AdventOfCode.outputIntermed(toStr(grid));
// ------------------
let distmap: {[id:number]: number} = {};
let dqueue: [number, number, Point][] = []; //<len, depth, pos>
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<height; y++)
{
for (let x=0; x<width; x++)
{
if (highlights.some( p => 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]}]`; }
}

View File

@ -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?

File diff suppressed because one or more lines are too long

View File

@ -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<v.length;i++) this.data[i]=v[i];
}
r(pos: number): number
{
if (!(pos in this.data)) this.data[pos] = 0;
return this.data[pos];
}
w(pos: number, val: number): number
{
return this.data[pos] = val;
}
}
}

View File

@ -0,0 +1,318 @@
namespace AdventOfCode2019_21_2
{
const DAY = 21;
const PROBLEM = 2;
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
"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<v.length;i++) this.data[i]=v[i];
}
r(pos: number): number
{
if (!(pos in this.data)) this.data[pos] = 0;
return this.data[pos];
}
w(pos: number, val: number): number
{
return this.data[pos] = val;
}
}
}

View File

@ -0,0 +1,158 @@
--- Day 22: Slam Shuffle ---
There isn't much to do while you wait for the droids to repair your ship. At least you're drifting in the right direction. You decide to practice a new card shuffle you've been working on.
Digging through the ship's storage, you find a deck of space cards! Just like any deck of space cards, there are 10007 cards in the deck numbered 0 through 10006. The deck must be new - they're still in factory order, with 0 on the top, then 1, then 2, and so on, all the way through to 10006 on the bottom.
You've been practicing three different techniques that you use while shuffling. Suppose you have a deck of only 10 cards (numbered 0 through 9):
To deal into new stack, create a new stack of cards by dealing the top card of the deck onto the top of the new stack repeatedly until you run out of cards:
Top Bottom
0 1 2 3 4 5 6 7 8 9 Your deck
New stack
1 2 3 4 5 6 7 8 9 Your deck
0 New stack
2 3 4 5 6 7 8 9 Your deck
1 0 New stack
3 4 5 6 7 8 9 Your deck
2 1 0 New stack
Several steps later...
9 Your deck
8 7 6 5 4 3 2 1 0 New stack
Your deck
9 8 7 6 5 4 3 2 1 0 New stack
Finally, pick up the new stack you've just created and use it as the deck for the next technique.
To cut N cards, take the top N cards off the top of the deck and move them as a single unit to the bottom of the deck, retaining their order. For example, to cut 3:
Top Bottom
0 1 2 3 4 5 6 7 8 9 Your deck
3 4 5 6 7 8 9 Your deck
0 1 2 Cut cards
3 4 5 6 7 8 9 Your deck
0 1 2 Cut cards
3 4 5 6 7 8 9 0 1 2 Your deck
You've also been getting pretty good at a version of this technique where N is negative! In that case, cut (the absolute value of) N cards from the bottom of the deck onto the top. For example, to cut -4:
Top Bottom
0 1 2 3 4 5 6 7 8 9 Your deck
0 1 2 3 4 5 Your deck
6 7 8 9 Cut cards
0 1 2 3 4 5 Your deck
6 7 8 9 Cut cards
6 7 8 9 0 1 2 3 4 5 Your deck
To deal with increment N, start by clearing enough space on your table to lay out all of the cards individually in a long line. Deal the top card into the leftmost position. Then, move N positions to the right and deal the next card there. If you would move into a position past the end of the space on your table, wrap around and keep counting from the leftmost card again. Continue this process until you run out of cards.
For example, to deal with increment 3:
0 1 2 3 4 5 6 7 8 9 Your deck
. . . . . . . . . . Space on table
^ Current position
Deal the top card to the current position:
1 2 3 4 5 6 7 8 9 Your deck
0 . . . . . . . . . Space on table
^ Current position
Move the current position right 3:
1 2 3 4 5 6 7 8 9 Your deck
0 . . . . . . . . . Space on table
^ Current position
Deal the top card:
2 3 4 5 6 7 8 9 Your deck
0 . . 1 . . . . . . Space on table
^ Current position
Move right 3 and deal:
3 4 5 6 7 8 9 Your deck
0 . . 1 . . 2 . . . Space on table
^ Current position
Move right 3 and deal:
4 5 6 7 8 9 Your deck
0 . . 1 . . 2 . . 3 Space on table
^ Current position
Move right 3, wrapping around, and deal:
5 6 7 8 9 Your deck
0 . 4 1 . . 2 . . 3 Space on table
^ Current position
And so on:
0 7 4 1 8 5 2 9 6 3 Space on table
Positions on the table which already contain cards are still counted; they're not skipped. Of course, this technique is carefully designed so it will never put two cards in the same position or leave a position empty.
Finally, collect the cards on the table so that the leftmost card ends up at the top of your deck, the card to its right ends up just below the top card, and so on, until the rightmost card ends up at the bottom of the deck.
The complete shuffle process (your puzzle input) consists of applying many of these techniques. Here are some examples that combine techniques; they all start with a factory order deck of 10 cards:
deal with increment 7
deal into new stack
deal into new stack
Result: 0 3 6 9 2 5 8 1 4 7
cut 6
deal with increment 7
deal into new stack
Result: 3 0 7 4 1 8 5 2 9 6
deal with increment 7
deal with increment 9
cut -2
Result: 6 3 0 7 4 1 8 5 2 9
deal into new stack
cut -2
deal with increment 7
cut 8
cut -4
deal with increment 7
cut 3
deal with increment 9
deal with increment 3
cut -1
Result: 9 2 5 8 1 4 7 0 3 6
Positions within the deck count from 0 at the top, then 1 for the card immediately below the top card, and so on to the bottom. (That is, cards start in the position matching their number.)
After shuffling your factory order deck of 10007 cards, what is the position of card 2019?
--- Part Two ---
After a while, you realize your shuffling skill won't improve much more with merely a single deck of cards. You ask every 3D printer on the ship to make you some more cards while you check on the ship repairs. While reviewing the work the droids have finished so far, you think you see Halley's Comet fly past!
When you get back, you discover that the 3D printers have combined their power to create for you a single, giant, brand new, factory order deck of 119315717514047 space cards.
Finally, a deck of cards worthy of shuffling!
You decide to apply your complete shuffle process (your puzzle input) to the deck 101741582076661 times in a row.
You'll need to be careful, though - one wrong move with this many cards and you might overflow your entire ship!
After shuffling your new, giant, factory order deck that many times, what number is on the card that ends up in position 2020?

View File

@ -0,0 +1,100 @@
cut -4258
deal with increment 71
cut -6593
deal into new stack
deal with increment 54
cut -5397
deal into new stack
cut 1327
deal with increment 20
deal into new stack
deal with increment 45
cut -9986
deal into new stack
deal with increment 47
cut -3318
deal with increment 75
cut 542
deal with increment 48
cut 8670
deal with increment 13
deal into new stack
deal with increment 5
cut -8813
deal with increment 36
cut 3228
deal with increment 21
cut 5143
deal with increment 13
cut 7329
deal with increment 74
deal into new stack
deal with increment 4
cut 4178
deal with increment 29
cut -7664
deal with increment 17
cut 8216
deal with increment 22
cut -7497
deal with increment 10
cut -2813
deal into new stack
cut 8416
deal with increment 16
cut -4124
deal with increment 13
cut -8531
deal with increment 74
cut -9397
deal with increment 57
cut -1832
deal with increment 34
cut -2538
deal into new stack
cut 7837
deal with increment 57
cut 5257
deal with increment 2
cut -8241
deal with increment 26
deal into new stack
deal with increment 39
cut -659
deal with increment 58
cut 34
deal into new stack
deal with increment 46
cut 9168
deal with increment 35
cut 8530
deal into new stack
cut 297
deal into new stack
cut 1116
deal with increment 69
cut 5440
deal with increment 6
deal into new stack
cut 3811
deal with increment 7
deal into new stack
cut -8657
deal with increment 29
cut 8933
deal with increment 4
cut -6643
deal with increment 37
cut 1688
deal with increment 32
cut -554
deal with increment 69
deal into new stack
deal with increment 64
cut 4395
deal with increment 71
cut -9180
deal with increment 60
cut 6480
deal with increment 73
cut -7146

View File

@ -0,0 +1,76 @@
namespace AdventOfCode2019_22_1
{
const DAY = 22;
const PROBLEM = 1;
export async function run()
{
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))
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<data.length;i++) d2[(i*cmd.param) % data.length] = data[i];
data = (cmd.param === 1) ? d2.reverse() : d2;
}
else throw cmd;
let str = "";
let len = 50;
for (let i=0; i<(data.length / len)+1; i++)
{
str += data.slice(i*len, (i+1)*len).map(p => 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; }
}
}

View File

@ -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<cmd.param; i++)
{
if (((i * decklength + idx) % cmd.param) === 0n)
{
idx = (i * decklength + idx) / cmd.param;
f = true;
break;
}
}
if (!f) throw "--";
}
else if (cmd.type === CmdType.DEAL && cmd.param === 1n)
{
idx = decklength - idx - 1n;
}
else throw cmd;
}
return idx;
}
function lcg_crack(states: bigint[], modulus: bigint): [bigint, bigint, bigint]
{
const multiplier = (states[2] - states[1]) * modinv(states[1] - states[0], modulus) % modulus
return crack_unknown_increment(states, modulus, multiplier)
}
function crack_unknown_increment(states: bigint[], modulus: bigint, multiplier: bigint): [bigint, bigint, bigint]
{
const increment = (states[1] - states[0]*multiplier) % modulus;
return [modulus, multiplier, increment];
}
function egcd(a: bigint, b: bigint): [bigint, bigint, bigint]
{
if (a === 0n) return [b, 0n, 1n];
const [g, x, y] = egcd(b % a, a);
return [g, y - (b / a) * x, x];
}
function modinv(b: bigint, n: bigint): bigint
{
const [g, x, _] = egcd(b, n)
if (g === 1n) return x % n;
throw "rec";
}
function parseLine(str: string): Cmd
{BigInt
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: bigint;
constructor(t:CmdType, n: number) { this.type=t; this.param=BigInt(n); }
}
}

View File

@ -0,0 +1,29 @@
--- Day 23: Category Six ---
The droids have finished repairing as much of the ship as they can. Their report indicates that this was a Category 6 disaster - not because it was that bad, but because it destroyed the stockpile of Category 6 network cables as well as most of the ship's network infrastructure.
You'll need to rebuild the network from scratch.
The computers on the network are standard Intcode computers that communicate by sending packets to each other. There are 50 of them in total, each running a copy of the same Network Interface Controller (NIC) software (your puzzle input). The computers have network addresses 0 through 49; when each computer boots up, it will request its network address via a single input instruction. Be sure to give each computer a unique network address.
Once a computer has received its network address, it will begin doing work and communicating over the network by sending and receiving packets. All packets contain two values named X and Y. Packets sent to a computer are queued by the recipient and read in the order they are received.
To send a packet to another computer, the NIC will use three output instructions that provide the destination address of the packet followed by its X and Y values. For example, three output instructions that provide the values 10, 20, 30 would send a packet with X=20 and Y=30 to the computer with address 10.
To receive a packet from another computer, the NIC will use an input instruction. If the incoming packet queue is empty, provide -1. Otherwise, provide the X value of the next packet; the computer will then use a second input instruction to receive the Y value for the same packet. Once both values of the packet are read in this way, the packet is removed from the queue.
Note that these input and output instructions never block. Specifically, output instructions do not wait for the sent packet to be received - the computer might send multiple packets before receiving any. Similarly, input instructions do not wait for a packet to arrive - if no packet is waiting, input instructions should receive -1.
Boot up all 50 computers and attach them to your network. What is the Y value of the first packet sent to address 255?
--- Part Two ---
Packets sent to address 255 are handled by a device called a NAT (Not Always Transmitting). The NAT is responsible for managing power consumption of the network by blocking certain packets and watching for idle periods in the computers.
If a packet would be sent to address 255, the NAT receives it instead. The NAT remembers only the last packet it receives; that is, the data in each packet it receives overwrites the NAT's packet memory with the new packet's X and Y values.
The NAT also monitors all computers on the network. If all computers have empty incoming packet queues and are continuously trying to receive packets without sending packets, the network is considered idle.
Once the network is idle, the NAT sends only the last packet it received to address 0; this will cause the computers on the network to resume activity. In this way, the NAT can throttle power consumption of the network when the ship needs power in other areas.
Monitor packets released to the computer at address 0 by the NAT. What is the first Y value delivered by the NAT to the computer at address 0 twice in a row?

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,353 @@
namespace AdventOfCode2019_23_1
{
const DAY = 23;
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 intps = new Array<Interpreter>(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<number>(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<v.length;i++) this.data[i]=v[i];
}
r(pos: number): number
{
if (!(pos in this.data)) this.data[pos] = 0;
return this.data[pos];
}
w(pos: number, val: number): number
{
return this.data[pos] = val;
}
}
}

View File

@ -0,0 +1,384 @@
namespace AdventOfCode2019_23_2
{
const DAY = 23;
const PROBLEM = 2;
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 intps = new Array<Interpreter>(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<number>(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<v.length;i++) this.data[i]=v[i];
}
r(pos: number): number
{
if (!(pos in this.data)) this.data[pos] = 0;
return this.data[pos];
}
w(pos: number, val: number): number
{
return this.data[pos] = val;
}
}
}

View File

@ -0,0 +1,227 @@
--- Day 24: Planet of Discord ---
You land on Eris, your last stop before reaching Santa. As soon as you do, your sensors start picking up strange life forms moving around: Eris is infested with bugs! With an over 24-hour roundtrip for messages between you and Earth, you'll have to deal with this problem on your own.
Eris isn't a very large place; a scan of the entire area fits into a 5x5 grid (your puzzle input). The scan shows bugs (#) and empty spaces (.).
Each minute, The bugs live and die based on the number of bugs in the four adjacent tiles:
A bug dies (becoming an empty space) unless there is exactly one bug adjacent to it.
An empty space becomes infested with a bug if exactly one or two bugs are adjacent to it.
Otherwise, a bug or empty space remains the same. (Tiles on the edges of the grid have fewer than four adjacent tiles; the missing tiles count as empty space.) This process happens in every location simultaneously; that is, within the same minute, the number of adjacent bugs is counted for every tile first, and then the tiles are updated.
Here are the first few minutes of an example scenario:
Initial state:
....#
#..#.
#..##
..#..
#....
After 1 minute:
#..#.
####.
###.#
##.##
.##..
After 2 minutes:
#####
....#
....#
...#.
#.###
After 3 minutes:
#....
####.
...##
#.##.
.##.#
After 4 minutes:
####.
....#
##..#
.....
##...
To understand the nature of the bugs, watch for the first time a layout of bugs and empty spaces matches any previous layout. In the example above, the first layout to appear twice is:
.....
.....
.....
#....
.#...
To calculate the biodiversity rating for this layout, consider each tile left-to-right in the top row, then left-to-right in the second row, and so on. Each of these tiles is worth biodiversity points equal to increasing powers of two: 1, 2, 4, 8, 16, 32, and so on. Add up the biodiversity points for tiles with bugs; in this example, the 16th tile (32768 points) and 22nd tile (2097152 points) have bugs, a total biodiversity rating of 2129920.
What is the biodiversity rating for the first layout that appears twice?
--- Part Two ---
After careful analysis, one thing is certain: you have no idea where all these bugs are coming from.
Then, you remember: Eris is an old Plutonian settlement! Clearly, the bugs are coming from recursively-folded space.
This 5x5 grid is only one level in an infinite number of recursion levels. The tile in the middle of the grid is actually another 5x5 grid, the grid in your scan is contained as the middle tile of a larger 5x5 grid, and so on. Two levels of grids look like this:
| | | |
| | | |
| | | |
-----+-----+---------+-----+-----
| | | |
| | | |
| | | |
-----+-----+---------+-----+-----
| | | | | | | |
| |-+-+-+-+-| |
| | | | | | | |
| |-+-+-+-+-| |
| | | |?| | | |
| |-+-+-+-+-| |
| | | | | | | |
| |-+-+-+-+-| |
| | | | | | | |
-----+-----+---------+-----+-----
| | | |
| | | |
| | | |
-----+-----+---------+-----+-----
| | | |
| | | |
| | | |
(To save space, some of the tiles are not drawn to scale.) Remember, this is only a small part of the infinitely recursive grid; there is a 5x5 grid that contains this diagram, and a 5x5 grid that contains that one, and so on. Also, the ? in the diagram contains another 5x5 grid, which itself contains another 5x5 grid, and so on.
The scan you took (your puzzle input) shows where the bugs are on a single level of this structure. The middle tile of your scan is empty to accommodate the recursive grids within it. Initially, no other levels contain bugs.
Tiles still count as adjacent if they are directly up, down, left, or right of a given tile. Some tiles have adjacent tiles at a recursion level above or below its own level. For example:
| | | |
1 | 2 | 3 | 4 | 5
| | | |
-----+-----+---------+-----+-----
| | | |
6 | 7 | 8 | 9 | 10
| | | |
-----+-----+---------+-----+-----
| |A|B|C|D|E| |
| |-+-+-+-+-| |
| |F|G|H|I|J| |
| |-+-+-+-+-| |
11 | 12 |K|L|?|N|O| 14 | 15
| |-+-+-+-+-| |
| |P|Q|R|S|T| |
| |-+-+-+-+-| |
| |U|V|W|X|Y| |
-----+-----+---------+-----+-----
| | | |
16 | 17 | 18 | 19 | 20
| | | |
-----+-----+---------+-----+-----
| | | |
21 | 22 | 23 | 24 | 25
| | | |
Tile 19 has four adjacent tiles: 14, 18, 20, and 24.
Tile G has four adjacent tiles: B, F, H, and L.
Tile D has four adjacent tiles: 8, C, E, and I.
Tile E has four adjacent tiles: 8, D, 14, and J.
Tile 14 has eight adjacent tiles: 9, E, J, O, T, Y, 15, and 19.
Tile N has eight adjacent tiles: I, O, S, and five tiles within the sub-grid marked ?.
The rules about bugs living and dying are the same as before.
For example, consider the same initial state as above:
....#
#..#.
#.?##
..#..
#....
The center tile is drawn as ? to indicate the next recursive grid. Call this level 0; the grid within this one is level 1, and the grid that contains this one is level -1. Then, after ten minutes, the grid at each level would look like this:
Depth -5:
..#..
.#.#.
..?.#
.#.#.
..#..
Depth -4:
...#.
...##
..?..
...##
...#.
Depth -3:
#.#..
.#...
..?..
.#...
#.#..
Depth -2:
.#.##
....#
..?.#
...##
.###.
Depth -1:
#..##
...##
..?..
...#.
.####
Depth 0:
.#...
.#.##
.#?..
.....
.....
Depth 1:
.##..
#..##
..?.#
##.##
#####
Depth 2:
###..
##.#.
#.?..
.#.##
#.#..
Depth 3:
..###
.....
#.?..
#....
#...#
Depth 4:
.###.
#..#.
#.?..
##.#.
.....
Depth 5:
####.
#..#.
#.?#.
####.
.....
In this example, after 10 minutes, a total of 99 bugs are present.
Starting with your scan, how many bugs are present after 200 minutes?

View File

@ -0,0 +1,5 @@
#.#..
.#.#.
#...#
.#..#
##.#.

View File

@ -0,0 +1,79 @@
namespace AdventOfCode2019_24_1
{
const DAY = 24;
const PROBLEM = 1;
export async function run()
{
let input = await AdventOfCode.getInput(DAY);
if (input == null) return;
AdventOfCode.setIntermedOutputSize("5.00vw");
let grid = input
.split(new RegExp('\r?\n'))
.filter(p => 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;
}
}

View File

@ -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];
}
}

View File

@ -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'] ],
],
];