1
0

aoc 10-16

This commit is contained in:
Mike Schwörer 2019-12-16 14:47:03 +01:00
parent 1fa48259cf
commit f169fb40cc
40 changed files with 4382 additions and 12 deletions

View File

@ -18,7 +18,7 @@ namespace AdventOfCode2019_02_1
const arg1 = automata[automata[i+1]];
const arg2 = automata[automata[i+2]];
const dest = automata[i+3];
console.log("["+dest+"] <- "+arg1+" {"+op+"} "+arg2)
AdventOfCode.outputConsole("["+dest+"] <- "+arg1+" {"+op+"} "+arg2)
if (op==1) automata[dest] = arg1 + arg2;
else if (op==2) automata[dest] = arg1 * arg2;
else break;

View File

@ -58,7 +58,7 @@ namespace AdventOfCode2019_04_1
if (!(d0==d1 || d1==d2 || d2==d3 || d3==d4 || d4==d5)) continue;
rcount++;
console.log(v5);
AdventOfCode.outputConsole(v5);
}
}
}

View File

@ -58,7 +58,7 @@ namespace AdventOfCode2019_04_2
if (!eq(d0, d1, d2, d3, d4, d5)) continue;
rcount++;
console.log(v5);
AdventOfCode.outputConsole(v5);
}
}
}

View File

@ -65,7 +65,7 @@ namespace AdventOfCode2019_05_1
{
const p0 = cmd.getParameter(this, 0);
this.output.push(p0);
console.log("# " + p0);
AdventOfCode.outputConsole("# " + p0);
}
else throw "Unknown Op: " + cmd.opcode + " @ " + this.instructionpointer;

View File

@ -67,7 +67,7 @@ namespace AdventOfCode2019_05_2
{
const p0 = cmd.getParameter(this, 0);
this.output.push(p0);
console.log("# " + p0);
AdventOfCode.outputConsole("# " + p0);
}
else if (cmd.opcode == OpCode.TJMP)
{

View File

@ -39,7 +39,7 @@ namespace AdventOfCode2019_07_1
runner5.run();
outputs.push(runner5.output[0]);
console.log([a1,a2,a3,a4,a5].toString() + " --> " + runner1.output[0] + " |> " + runner2.output[0] + " |> " + runner3.output[0] + " |> " + runner4.output[0] + " |> " + runner5.output[0]);
AdventOfCode.outputConsole([a1,a2,a3,a4,a5].toString() + " --> " + runner1.output[0] + " |> " + runner2.output[0] + " |> " + runner3.output[0] + " |> " + runner4.output[0] + " |> " + runner5.output[0]);
}
const max = outputs.sort((a, b) => a - b).reverse()[0];
@ -99,7 +99,7 @@ namespace AdventOfCode2019_07_1
{
const p0 = cmd.getParameter(this, 0);
this.output.push(p0);
//console.log("# " + p0);
//AdventOfCode.outputConsole("# " + p0);
}
else if (cmd.opcode == OpCode.TJMP)
{

View File

@ -26,7 +26,7 @@ namespace AdventOfCode2019_07_2
const r = evalConfiguration(code, a1,a2,a3,a4,a5)
outputs.push(r);
console.log([a1,a2,a3,a4,a5].toString() + " --> " + r);
AdventOfCode.outputConsole([a1,a2,a3,a4,a5].toString() + " --> " + r);
}
const max = outputs.sort((a, b) => a - b).reverse()[0];

View File

@ -22,7 +22,7 @@ namespace AdventOfCode2019_08_1
const _zc = layers[i].filter(p => p==0).length;
const _vv = layers[i].filter(p => p==1).length * layers[i].filter(p => p==2).length;
console.log("["+i+"] => "+_zc+" ("+_vv+")");
AdventOfCode.outputConsole("["+i+"] => "+_zc+" ("+_vv+")");
if (_zc < zc) { zc=_zc; vv = _vv; }
}

View File

@ -36,7 +36,7 @@ namespace AdventOfCode2019_08_2
if (r[i]===2) str += ".";
}
console.log(str);
await AdventOfCode.outputIntermed(str);
AdventOfCode.output(DAY, PROBLEM, "CJZLP"); // OCR -.-
}

View File

@ -70,7 +70,7 @@ namespace AdventOfCode2019_09_1
{
const p0 = cmd.getParameter(this, 0);
this.output.push(p0);
console.log("# " + p0);
AdventOfCode.outputConsole("# " + p0);
}
else if (cmd.opcode == OpCode.TJMP)
{

View File

@ -70,7 +70,7 @@ namespace AdventOfCode2019_09_2
{
const p0 = cmd.getParameter(this, 0);
this.output.push(p0);
console.log("# " + p0);
AdventOfCode.outputConsole("# " + p0);
}
else if (cmd.opcode == OpCode.TJMP)
{

View File

@ -0,0 +1,170 @@
--- Day 10: Monitoring Station ---
You fly into the asteroid belt and reach the Ceres monitoring station. The Elves here have an emergency: they're having trouble tracking all of the asteroids and can't be sure they're safe.
The Elves would like to build a new monitoring station in a nearby area of space; they hand you a map of all of the asteroids in that region (your puzzle input).
The map indicates whether each position is empty (.) or contains an asteroid (#). The asteroids are much smaller than they appear on the map, and every asteroid is exactly in the center of its marked position. The asteroids can be described with X,Y coordinates where X is the distance from the left edge and Y is the distance from the top edge (so the top-left corner is 0,0 and the position immediately to its right is 1,0).
Your job is to figure out which asteroid would be the best place to build a new monitoring station. A monitoring station can detect any asteroid to which it has direct line of sight - that is, there cannot be another asteroid exactly between them. This line of sight can be at any angle, not just lines aligned to the grid or diagonally. The best location is the asteroid that can detect the largest number of other asteroids.
For example, consider the following map:
.#..#
.....
#####
....#
...##
The best location for a new monitoring station on this map is the highlighted asteroid at 3,4 because it can detect 8 asteroids, more than any other location. (The only asteroid it cannot detect is the one at 1,0; its view of this asteroid is blocked by the asteroid at 2,2.) All other asteroids are worse locations; they can detect 7 or fewer other asteroids. Here is the number of other asteroids a monitoring station on each asteroid could detect:
.7..7
.....
67775
....7
...87
Here is an asteroid (#) and some examples of the ways its line of sight might be blocked. If there were another asteroid at the location of a capital letter, the locations marked with the corresponding lowercase letter would be blocked and could not be detected:
#.........
...A......
...B..a...
.EDCG....a
..F.c.b...
.....c....
..efd.c.gb
.......c..
....f...c.
...e..d..c
Here are some larger examples:
Best is 5,8 with 33 other asteroids detected:
......#.#.
#..#.#....
..#######.
.#.#.###..
.#..#.....
..#....#.#
#..#....#.
.##.#..###
##...#..#.
.#....####
Best is 1,2 with 35 other asteroids detected:
#.#...#.#.
.###....#.
.#....#...
##.#.#.#.#
....#.#.#.
.##..###.#
..#...##..
..##....##
......#...
.####.###.
Best is 6,3 with 41 other asteroids detected:
.#..#..###
####.###.#
....###.#.
..###.##.#
##.##.#.#.
....###..#
..#.#..#.#
#..#.#.###
.##...##.#
.....#.#..
Best is 11,13 with 210 other asteroids detected:
.#..##.###...#######
##.############..##.
.#.######.########.#
.###.#######.####.#.
#####.##.#.##.###.##
..#####..#.#########
####################
#.####....###.#.#.##
##.#################
#####.##.###..####..
..######..##.#######
####.##.####...##..#
.#####..#.######.###
##...#.##########...
#.##########.#######
.####.#.###.###.#.##
....##.##.###..#####
.#.#.###########.###
#.#.#.#####.####.###
###.##.####.##.#..##
Find the best location for a new monitoring station. How many other asteroids can be detected from that location?
--- Part Two ---
Once you give them the coordinates, the Elves quickly deploy an Instant Monitoring Station to the location and discover the worst: there are simply too many asteroids.
The only solution is complete vaporization by giant laser.
Fortunately, in addition to an asteroid scanner, the new monitoring station also comes equipped with a giant rotating laser perfect for vaporizing asteroids. The laser starts by pointing up and always rotates clockwise, vaporizing any asteroid it hits.
If multiple asteroids are exactly in line with the station, the laser only has enough power to vaporize one of them before continuing its rotation. In other words, the same asteroids that can be detected can be vaporized, but if vaporizing one asteroid makes another one detectable, the newly-detected asteroid won't be vaporized until the laser has returned to the same position by rotating a full 360 degrees.
For example, consider the following map, where the asteroid with the new monitoring station (and laser) is marked X:
.#....#####...#..
##...##.#####..##
##...#...#.#####.
..#.....X...###..
..#.#.....#....##
The first nine asteroids to get vaporized, in order, would be:
.#....###24...#..
##...##.13#67..9#
##...#...5.8####.
..#.....X...###..
..#.#.....#....##
Note that some asteroids (the ones behind the asteroids marked 1, 5, and 7) won't have a chance to be vaporized until the next full rotation. The laser continues rotating; the next nine to be vaporized are:
.#....###.....#..
##...##...#.....#
##...#......1234.
..#.....X...5##..
..#.9.....8....76
The next nine to be vaporized are then:
.8....###.....#..
56...9#...#.....#
34...7...........
..2.....X....##..
..1..............
Finally, the laser completes its first full rotation (1 through 3), a second rotation (4 through 8), and vaporizes the last asteroid (9) partway through its third rotation:
......234.....6..
......1...5.....7
.................
........X....89..
.................
In the large example above (the one with the best monitoring station location at 11,13):
The 1st asteroid to be vaporized is at 11,12.
The 2nd asteroid to be vaporized is at 12,1.
The 3rd asteroid to be vaporized is at 12,2.
The 10th asteroid to be vaporized is at 12,8.
The 20th asteroid to be vaporized is at 16,0.
The 50th asteroid to be vaporized is at 16,9.
The 100th asteroid to be vaporized is at 10,16.
The 199th asteroid to be vaporized is at 9,6.
The 200th asteroid to be vaporized is at 8,2.
The 201st asteroid to be vaporized is at 10,9.
The 299th and final asteroid to be vaporized is at 11,1.
The Elves are placing bets on which will be the 200th asteroid to be vaporized. Win the bet by determining which asteroid that will be; what do you get if you multiply its X coordinate by 100 and then add its Y coordinate? (For example, 8,2 becomes 802.)

View File

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

View File

@ -0,0 +1,59 @@
namespace AdventOfCode2019_10_1
{
const DAY = 10;
const PROBLEM = 1;
export async function run()
{
let input = await AdventOfCode.getInput(DAY);
if (input == null) return;
input = input.trim();
let data: { [_:number]:[number,number,boolean] } = {};
const width = input.split(new RegExp('\r?\n'))[0].length;
const height = input.split(new RegExp('\r?\n')).length;
let x=0;
let y=0;
for(let i=0; i<input.length;i++)
{
if (input[i]=="\r") continue;
if (input[i]=="\n") { x=0;y++; continue; }
data[y*10000+x] = [x, y, input[i]==="#"];
x++;
}
let ac_max = -1;
for(let cy=0; cy<height;cy++)
for(let cx=0; cx<width;cx++)
{
if (!data[cy*10000+cx][2]) continue;
// same [dx,dy]/gcd === hiding each other
const count = Object.entries(data)
.filter(p => p[1][2])
.map(p => [cx-p[1][0], cy-p[1][1]])
.filter(p => p[0] != 0 || p[1] != 0)
.map(p => { const div = gcd(p[0], p[1]); return [ p[0]/div, p[1]/div ] })
.map(p => p[0] * 10000 + p[1])
.sort((a, b) => a - b).filter((v,i,s) => s.indexOf(v)===i) // unique
.length;
AdventOfCode.outputConsole(`${((count>ac_max)?"+":" ")}[${cx}|${cy}] := ${count}`);
if (count>ac_max) ac_max=count;
}
AdventOfCode.output(DAY, PROBLEM, ac_max.toString());
}
function gcd(x: number, y: number)
{
x = Math.abs(x);
y = Math.abs(y);
while(y) { var t = y; y = x % y; x = t; }
return x;
}
}

View File

@ -0,0 +1,95 @@
namespace AdventOfCode2019_10_2
{
const DAY = 10;
const PROBLEM = 2;
export async function run()
{
let input = await AdventOfCode.getInput(DAY);
if (input == null) return;
input = input.trim();
let data: { [_:number]:[number,number,boolean] } = {};
const width = input.split(new RegExp('\r?\n'))[0].length;
const height = input.split(new RegExp('\r?\n')).length;
let x=0;
let y=0;
for(let i=0; i<input.length;i++)
{
if (input[i]=="\r") continue;
if (input[i]=="\n") { x=0;y++; continue; }
data[y*10000+x] = [x, y, input[i]==="#"];
x++;
}
let ac_max = -1;
let ac_x = -1;
let ac_y = -1;
for(let cy=0; cy<height;cy++)
for(let cx=0; cx<width;cx++)
{
if (!data[cy*10000+cx][2]) continue;
// same [dx,dy]/gcd === hiding each other
const count = Object.entries(data)
.filter(p => p[1][2])
.map(p => [cx-p[1][0], cy-p[1][1]])
.filter(p => p[0] != 0 || p[1] != 0)
.map(p => { const div = gcd(p[0], p[1]); return [ p[0]/div, p[1]/div ] })
.map(p => p[0] * 10000 + p[1])
.sort((a, b) => a - b).filter((v,i,s) => s.indexOf(v)===i) // unique
.length;
if (count>ac_max) { ac_max=count; ac_x=cx; ac_y=cy; }
}
AdventOfCode.outputConsole(`[${ac_x}|${ac_y}] := ${ac_max}`);
//-------------------------
let asteroids = Object.entries(data)
.filter(p => p[1][2])
.map(p => [ p[1][0]-ac_x, p[1][1]-ac_y ])
.filter(p => p[0] != 0 || p[1] != 0) // rel_x | rel_y | norm_x | norm_y | dist | angle | processed | x | y | order
.map(p => { const div = gcd(p[0], p[1]); return [ p[0], p[1], p[0]/div, p[1]/div, div, (Math.atan2(p[0], -p[1])+Math.PI*2)%(Math.PI*2), 1, ac_x+p[0], ac_y+p[1] ] })
.map(p => { const div = gcd(p[0], p[1]); return [ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[5]*10000+p[4] ] })
.sort((a, b) => a[9] - b[9])
let ldx = -1;
let ldy = -1;
let idx=1;
for(let i=0; i<asteroids.length; i++)
{
if (asteroids[i][6] === 0) continue;
if (ldx==asteroids[i][2] && ldy==asteroids[i][3]) continue;
AdventOfCode.outputConsole(`${idx} => [${asteroids[i][7]}|${asteroids[i][8]}] /[${asteroids[i][0]}|${asteroids[i][1]}] @ ${asteroids[i][5]}:${asteroids[i][4]}`);
ldx = asteroids[i][2];
ldy = asteroids[i][3];
asteroids[i][6] = 0;
if (idx == 200)
{
AdventOfCode.output(DAY, PROBLEM, (asteroids[i][7]*100 + asteroids[i][8]).toString());
return;
}
idx++;
}
AdventOfCode.output(DAY, PROBLEM, "ERR");
}
function gcd(x: number, y: number)
{
x = Math.abs(x);
y = Math.abs(y);
while(y) { var t = y; y = x % y; x = t; }
return x;
}
}

View File

@ -0,0 +1,72 @@
--- Day 11: Space Police ---
On the way to Jupiter, you're pulled over by the Space Police.
"Attention, unmarked spacecraft! You are in violation of Space Law! All spacecraft must have a clearly visible registration identifier! You have 24 hours to comply or be sent to Space Jail!"
Not wanting to be sent to Space Jail, you radio back to the Elves on Earth for help. Although it takes almost three hours for their reply signal to reach you, they send instructions for how to power up the emergency hull painting robot and even provide a small Intcode program (your puzzle input) that will cause it to paint your ship appropriately.
There's just one problem: you don't have an emergency hull painting robot.
You'll need to build a new emergency hull painting robot. The robot needs to be able to move around on the grid of square panels on the side of your ship, detect the color of its current panel, and paint its current panel black or white. (All of the panels are currently black.)
The Intcode program will serve as the brain of the robot. The program uses input instructions to access the robot's camera: provide 0 if the robot is over a black panel or 1 if the robot is over a white panel. Then, the program will output two values:
First, it will output a value indicating the color to paint the panel the robot is over: 0 means to paint the panel black, and 1 means to paint the panel white.
Second, it will output a value indicating the direction the robot should turn: 0 means it should turn left 90 degrees, and 1 means it should turn right 90 degrees.
After the robot turns, it should always move forward exactly one panel. The robot starts facing up.
The robot will continue running for a while like this and halt when it is finished drawing. Do not restart the Intcode computer inside the robot during this process.
For example, suppose the robot is about to start running. Drawing black panels as ., white panels as #, and the robot pointing the direction it is facing (< ^ > v), the initial state and region near the robot looks like this:
.....
.....
..^..
.....
.....
The panel under the robot (not visible here because a ^ is shown instead) is also black, and so any input instructions at this point should be provided 0. Suppose the robot eventually outputs 1 (paint white) and then 0 (turn left). After taking these actions and moving forward one panel, the region now looks like this:
.....
.....
.<#..
.....
.....
Input instructions should still be provided 0. Next, the robot might output 0 (paint black) and then 0 (turn left):
.....
.....
..#..
.v...
.....
After more outputs (1,0, 1,0):
.....
.....
..^..
.##..
.....
The robot is now back where it started, but because it is now on a white panel, input instructions should be provided 1. After several more outputs (0,1, 1,0, 1,0), the area looks like this:
.....
..<#.
...#.
.##..
.....
Before you deploy the robot, you should probably have an estimate of the area it will cover: specifically, you need to know the number of panels it paints at least once, regardless of color. In the example above, the robot painted 6 panels at least once. (It painted its starting panel twice, but that panel is still only counted once; it also never painted the panel it ended on.)
Build a new emergency hull painting robot and run the Intcode program on it. How many panels does it paint at least once?
--- Part Two ---
You're not sure what it's trying to paint, but it's definitely not a registration identifier. The Space Police are getting impatient.
Checking your external ship cameras again, you notice a white panel marked "emergency hull painting robot starting panel". The rest of the panels are still black, but it looks like the robot was expecting to start on a white panel, not a black one.
Based on the Space Law Space Brochure that the Space Police attached to one of your windows, a valid registration identifier is always eight capital letters. After starting the robot on a single white panel instead, what registration identifier does it paint on your hull?

View File

@ -0,0 +1 @@
3,8,1005,8,330,1106,0,11,0,0,0,104,1,104,0,3,8,102,-1,8,10,101,1,10,10,4,10,1008,8,0,10,4,10,102,1,8,29,2,9,4,10,1006,0,10,1,1103,17,10,3,8,102,-1,8,10,101,1,10,10,4,10,108,0,8,10,4,10,101,0,8,61,1006,0,21,1006,0,51,3,8,1002,8,-1,10,101,1,10,10,4,10,108,1,8,10,4,10,1001,8,0,89,1,102,19,10,1,1107,17,10,1006,0,18,3,8,1002,8,-1,10,1001,10,1,10,4,10,1008,8,1,10,4,10,1001,8,0,123,1,9,2,10,2,1105,10,10,2,103,9,10,2,1105,15,10,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,0,10,4,10,102,1,8,161,3,8,102,-1,8,10,101,1,10,10,4,10,108,1,8,10,4,10,101,0,8,182,3,8,1002,8,-1,10,101,1,10,10,4,10,1008,8,0,10,4,10,101,0,8,205,2,1102,6,10,1006,0,38,2,1007,20,10,2,1105,17,10,3,8,102,-1,8,10,1001,10,1,10,4,10,108,1,8,10,4,10,1001,8,0,241,3,8,102,-1,8,10,101,1,10,10,4,10,108,1,8,10,4,10,101,0,8,263,1006,0,93,2,5,2,10,2,6,7,10,3,8,102,-1,8,10,101,1,10,10,4,10,108,0,8,10,4,10,1001,8,0,296,1006,0,81,1006,0,68,1006,0,76,2,4,4,10,101,1,9,9,1007,9,1010,10,1005,10,15,99,109,652,104,0,104,1,21102,825594262284,1,1,21102,347,1,0,1105,1,451,21101,0,932855939852,1,21101,358,0,0,1106,0,451,3,10,104,0,104,1,3,10,104,0,104,0,3,10,104,0,104,1,3,10,104,0,104,1,3,10,104,0,104,0,3,10,104,0,104,1,21102,1,235152649255,1,21101,405,0,0,1105,1,451,21102,235350879235,1,1,21102,416,1,0,1106,0,451,3,10,104,0,104,0,3,10,104,0,104,0,21102,988757512972,1,1,21101,439,0,0,1106,0,451,21102,1,988669698828,1,21101,0,450,0,1106,0,451,99,109,2,22101,0,-1,1,21102,40,1,2,21102,1,482,3,21102,472,1,0,1106,0,515,109,-2,2105,1,0,0,1,0,0,1,109,2,3,10,204,-1,1001,477,478,493,4,0,1001,477,1,477,108,4,477,10,1006,10,509,1101,0,0,477,109,-2,2106,0,0,0,109,4,1202,-1,1,514,1207,-3,0,10,1006,10,532,21102,1,0,-3,21202,-3,1,1,21202,-2,1,2,21102,1,1,3,21102,1,551,0,1106,0,556,109,-4,2105,1,0,109,5,1207,-3,1,10,1006,10,579,2207,-4,-2,10,1006,10,579,22101,0,-4,-4,1105,1,647,21201,-4,0,1,21201,-3,-1,2,21202,-2,2,3,21102,598,1,0,1105,1,556,21202,1,1,-4,21101,0,1,-1,2207,-4,-2,10,1006,10,617,21102,1,0,-1,22202,-2,-1,-2,2107,0,-3,10,1006,10,639,21202,-1,1,1,21102,1,639,0,105,1,514,21202,-2,-1,-2,22201,-4,-2,-4,109,-5,2105,1,0

View File

@ -0,0 +1,327 @@
namespace AdventOfCode2019_11_1
{
const DAY = 11;
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()));
const rnr = new Interpreter(code, []);
let map: { [_:number]:number } = {};
let ctr = 0;
let x=1000;
let y=1000;
let d=0; //0=UP|1=RIGHT|2=DOWN|3=LEFT
for(;;)
{
ctr++;
//let str = "";
//for(let yy=990;yy<1010;yy++)
//{
// for(let xx=990;xx<1010;xx++)
// {
// if (x==xx && y== yy)
// {
// if (d==0)str += "^";
// if (d==1)str += ">";
// if (d==2)str += "v";
// if (d==3)str += "<";
// }
// else if ((((yy*10000+xx) in map) ? map[yy*10000+xx] : 0) == 0)
// {
// str += ".";
// }
// else
// {
// str += "#";
// }
// }
// str += "\n";
//}
//AdventOfCode.outputConsole(str);
//if (ctr>20) return;
const col = ((y*10000+x) in map) ? map[y*10000+x] : 0;
for(;;)
{
let rr = rnr.singleStep();
if (rnr.output.length == 2) break;
if (rnr.is_halted) break;
if (rr == StepResult.WAITING_FOR_IN) { rnr.inputqueue.push(col); }
}
if (rnr.is_halted) break;
const newcol = rnr.output[0];
const newdir = rnr.output[1];
AdventOfCode.outputConsole(`[${newcol}|${((newcol==0)?'.':'#')}] | [${newdir}|${((newdir==0)?'L':'R')}]`);
rnr.output = [];
map[y*10000+x] = newcol;
if (d==0 && newdir==0) { d=3;x--; }
else if (d==0 && newdir==1) { d=1;x++; }
else if (d==1 && newdir==0) { d=0;y++; }
else if (d==1 && newdir==1) { d=2;y--; }
else if (d==2 && newdir==0) { d=1;x++; }
else if (d==2 && newdir==1) { d=3;x--; }
else if (d==3 && newdir==0) { d=2;y--; }
else if (d==3 && newdir==1) { d=0;y++; }
}
AdventOfCode.output(DAY, PROBLEM, Object.keys(map).length.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;
}
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,332 @@
namespace AdventOfCode2019_11_2
{
const DAY = 11;
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()));
const rnr = new Interpreter(code, []);
let map: { [_:number]:number } = {};
let ctr = 0;
let minx = 1000;
let maxx = 1000;
let miny = 1000;
let maxy = 1000;
let x=1000;
let y=1000;
let d=0; //0=UP|1=RIGHT|2=DOWN|3=LEFT
map[y*10000+x] = 1;
for(;;)
{
ctr++;
const col = ((y*10000+x) in map) ? map[y*10000+x] : 0;
for(;;)
{
let rr = rnr.singleStep();
if (rnr.output.length == 2) break;
if (rnr.is_halted) break;
if (rr == StepResult.WAITING_FOR_IN) { rnr.inputqueue.push(col); }
}
if (rnr.is_halted) break;
const newcol = rnr.output[0];
const newdir = rnr.output[1];
//AdventOfCode.outputConsole(`[${newcol}|${((newcol==0)?'.':'#')}] | [${newdir}|${((newdir==0)?'L':'R')}]`);
rnr.output = [];
map[y*10000+x] = newcol;
if (d==0 && newdir==0) { d=3;x--; }
else if (d==0 && newdir==1) { d=1;x++; }
else if (d==1 && newdir==0) { d=0;y++; }
else if (d==1 && newdir==1) { d=2;y--; }
else if (d==2 && newdir==0) { d=1;x++; }
else if (d==2 && newdir==1) { d=3;x--; }
else if (d==3 && newdir==0) { d=2;y--; }
else if (d==3 && newdir==1) { d=0;y++; }
minx = Math.min(minx, x);
maxx = Math.max(maxx, x);
miny = Math.min(miny, y);
maxy = Math.max(maxy, y);
}
let str = "";
for(let yy=maxy;yy>=miny;yy--)
{
for(let xx=minx;xx<=maxx;xx++)
{
if ((((yy*10000+xx) in map) ? map[yy*10000+xx] : 0) == 0)
{
str += " ";
}
else
{
str += "\u2588";
}
}
str += "\n";
}
await AdventOfCode.outputIntermed(str);
AdventOfCode.output(DAY, PROBLEM, "LBJHEKLH");
}
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;
}
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,226 @@
--- Day 12: The N-Body Problem ---
The space near Jupiter is not a very safe place; you need to be careful of a big distracting red spot, extreme radiation, and a whole lot of moons swirling around. You decide to start by tracking the four largest moons: Io, Europa, Ganymede, and Callisto.
After a brief scan, you calculate the position of each moon (your puzzle input). You just need to simulate their motion so you can avoid them.
Each moon has a 3-dimensional position (x, y, and z) and a 3-dimensional velocity. The position of each moon is given in your scan; the x, y, and z velocity of each moon starts at 0.
Simulate the motion of the moons in time steps. Within each time step, first update the velocity of every moon by applying gravity. Then, once all moons' velocities have been updated, update the position of every moon by applying velocity. Time progresses by one step once all of the positions are updated.
To apply gravity, consider every pair of moons. On each axis (x, y, and z), the velocity of each moon changes by exactly +1 or -1 to pull the moons together. For example, if Ganymede has an x position of 3, and Callisto has a x position of 5, then Ganymede's x velocity changes by +1 (because 5 > 3) and Callisto's x velocity changes by -1 (because 3 < 5). However, if the positions on a given axis are the same, the velocity on that axis does not change for that pair of moons.
Once all gravity has been applied, apply velocity: simply add the velocity of each moon to its own position. For example, if Europa has a position of x=1, y=2, z=3 and a velocity of x=-2, y=0,z=3, then its new position would be x=-1, y=2, z=6. This process does not modify the velocity of any moon.
For example, suppose your scan reveals the following positions:
<x=-1, y=0, z=2>
<x=2, y=-10, z=-7>
<x=4, y=-8, z=8>
<x=3, y=5, z=-1>
Simulating the motion of these moons would produce the following:
After 0 steps:
pos=<x=-1, y= 0, z= 2>, vel=<x= 0, y= 0, z= 0>
pos=<x= 2, y=-10, z=-7>, vel=<x= 0, y= 0, z= 0>
pos=<x= 4, y= -8, z= 8>, vel=<x= 0, y= 0, z= 0>
pos=<x= 3, y= 5, z=-1>, vel=<x= 0, y= 0, z= 0>
After 1 step:
pos=<x= 2, y=-1, z= 1>, vel=<x= 3, y=-1, z=-1>
pos=<x= 3, y=-7, z=-4>, vel=<x= 1, y= 3, z= 3>
pos=<x= 1, y=-7, z= 5>, vel=<x=-3, y= 1, z=-3>
pos=<x= 2, y= 2, z= 0>, vel=<x=-1, y=-3, z= 1>
After 2 steps:
pos=<x= 5, y=-3, z=-1>, vel=<x= 3, y=-2, z=-2>
pos=<x= 1, y=-2, z= 2>, vel=<x=-2, y= 5, z= 6>
pos=<x= 1, y=-4, z=-1>, vel=<x= 0, y= 3, z=-6>
pos=<x= 1, y=-4, z= 2>, vel=<x=-1, y=-6, z= 2>
After 3 steps:
pos=<x= 5, y=-6, z=-1>, vel=<x= 0, y=-3, z= 0>
pos=<x= 0, y= 0, z= 6>, vel=<x=-1, y= 2, z= 4>
pos=<x= 2, y= 1, z=-5>, vel=<x= 1, y= 5, z=-4>
pos=<x= 1, y=-8, z= 2>, vel=<x= 0, y=-4, z= 0>
After 4 steps:
pos=<x= 2, y=-8, z= 0>, vel=<x=-3, y=-2, z= 1>
pos=<x= 2, y= 1, z= 7>, vel=<x= 2, y= 1, z= 1>
pos=<x= 2, y= 3, z=-6>, vel=<x= 0, y= 2, z=-1>
pos=<x= 2, y=-9, z= 1>, vel=<x= 1, y=-1, z=-1>
After 5 steps:
pos=<x=-1, y=-9, z= 2>, vel=<x=-3, y=-1, z= 2>
pos=<x= 4, y= 1, z= 5>, vel=<x= 2, y= 0, z=-2>
pos=<x= 2, y= 2, z=-4>, vel=<x= 0, y=-1, z= 2>
pos=<x= 3, y=-7, z=-1>, vel=<x= 1, y= 2, z=-2>
After 6 steps:
pos=<x=-1, y=-7, z= 3>, vel=<x= 0, y= 2, z= 1>
pos=<x= 3, y= 0, z= 0>, vel=<x=-1, y=-1, z=-5>
pos=<x= 3, y=-2, z= 1>, vel=<x= 1, y=-4, z= 5>
pos=<x= 3, y=-4, z=-2>, vel=<x= 0, y= 3, z=-1>
After 7 steps:
pos=<x= 2, y=-2, z= 1>, vel=<x= 3, y= 5, z=-2>
pos=<x= 1, y=-4, z=-4>, vel=<x=-2, y=-4, z=-4>
pos=<x= 3, y=-7, z= 5>, vel=<x= 0, y=-5, z= 4>
pos=<x= 2, y= 0, z= 0>, vel=<x=-1, y= 4, z= 2>
After 8 steps:
pos=<x= 5, y= 2, z=-2>, vel=<x= 3, y= 4, z=-3>
pos=<x= 2, y=-7, z=-5>, vel=<x= 1, y=-3, z=-1>
pos=<x= 0, y=-9, z= 6>, vel=<x=-3, y=-2, z= 1>
pos=<x= 1, y= 1, z= 3>, vel=<x=-1, y= 1, z= 3>
After 9 steps:
pos=<x= 5, y= 3, z=-4>, vel=<x= 0, y= 1, z=-2>
pos=<x= 2, y=-9, z=-3>, vel=<x= 0, y=-2, z= 2>
pos=<x= 0, y=-8, z= 4>, vel=<x= 0, y= 1, z=-2>
pos=<x= 1, y= 1, z= 5>, vel=<x= 0, y= 0, z= 2>
After 10 steps:
pos=<x= 2, y= 1, z=-3>, vel=<x=-3, y=-2, z= 1>
pos=<x= 1, y=-8, z= 0>, vel=<x=-1, y= 1, z= 3>
pos=<x= 3, y=-6, z= 1>, vel=<x= 3, y= 2, z=-3>
pos=<x= 2, y= 0, z= 4>, vel=<x= 1, y=-1, z=-1>
Then, it might help to calculate the total energy in the system. The total energy for a single moon is its potential energy multiplied by its kinetic energy. A moon's potential energy is the sum of the absolute values of its x, y, and z position coordinates. A moon's kinetic energy is the sum of the absolute values of its velocity coordinates. Below, each line shows the calculations for a moon's potential energy (pot), kinetic energy (kin), and total energy:
Energy after 10 steps:
pot: 2 + 1 + 3 = 6; kin: 3 + 2 + 1 = 6; total: 6 * 6 = 36
pot: 1 + 8 + 0 = 9; kin: 1 + 1 + 3 = 5; total: 9 * 5 = 45
pot: 3 + 6 + 1 = 10; kin: 3 + 2 + 3 = 8; total: 10 * 8 = 80
pot: 2 + 0 + 4 = 6; kin: 1 + 1 + 1 = 3; total: 6 * 3 = 18
Sum of total energy: 36 + 45 + 80 + 18 = 179
In the above example, adding together the total energy for all moons after 10 steps produces the total energy in the system, 179.
Here's a second example:
<x=-8, y=-10, z=0>
<x=5, y=5, z=10>
<x=2, y=-7, z=3>
<x=9, y=-8, z=-3>
Every ten steps of simulation for 100 steps produces:
After 0 steps:
pos=<x= -8, y=-10, z= 0>, vel=<x= 0, y= 0, z= 0>
pos=<x= 5, y= 5, z= 10>, vel=<x= 0, y= 0, z= 0>
pos=<x= 2, y= -7, z= 3>, vel=<x= 0, y= 0, z= 0>
pos=<x= 9, y= -8, z= -3>, vel=<x= 0, y= 0, z= 0>
After 10 steps:
pos=<x= -9, y=-10, z= 1>, vel=<x= -2, y= -2, z= -1>
pos=<x= 4, y= 10, z= 9>, vel=<x= -3, y= 7, z= -2>
pos=<x= 8, y=-10, z= -3>, vel=<x= 5, y= -1, z= -2>
pos=<x= 5, y=-10, z= 3>, vel=<x= 0, y= -4, z= 5>
After 20 steps:
pos=<x=-10, y= 3, z= -4>, vel=<x= -5, y= 2, z= 0>
pos=<x= 5, y=-25, z= 6>, vel=<x= 1, y= 1, z= -4>
pos=<x= 13, y= 1, z= 1>, vel=<x= 5, y= -2, z= 2>
pos=<x= 0, y= 1, z= 7>, vel=<x= -1, y= -1, z= 2>
After 30 steps:
pos=<x= 15, y= -6, z= -9>, vel=<x= -5, y= 4, z= 0>
pos=<x= -4, y=-11, z= 3>, vel=<x= -3, y=-10, z= 0>
pos=<x= 0, y= -1, z= 11>, vel=<x= 7, y= 4, z= 3>
pos=<x= -3, y= -2, z= 5>, vel=<x= 1, y= 2, z= -3>
After 40 steps:
pos=<x= 14, y=-12, z= -4>, vel=<x= 11, y= 3, z= 0>
pos=<x= -1, y= 18, z= 8>, vel=<x= -5, y= 2, z= 3>
pos=<x= -5, y=-14, z= 8>, vel=<x= 1, y= -2, z= 0>
pos=<x= 0, y=-12, z= -2>, vel=<x= -7, y= -3, z= -3>
After 50 steps:
pos=<x=-23, y= 4, z= 1>, vel=<x= -7, y= -1, z= 2>
pos=<x= 20, y=-31, z= 13>, vel=<x= 5, y= 3, z= 4>
pos=<x= -4, y= 6, z= 1>, vel=<x= -1, y= 1, z= -3>
pos=<x= 15, y= 1, z= -5>, vel=<x= 3, y= -3, z= -3>
After 60 steps:
pos=<x= 36, y=-10, z= 6>, vel=<x= 5, y= 0, z= 3>
pos=<x=-18, y= 10, z= 9>, vel=<x= -3, y= -7, z= 5>
pos=<x= 8, y=-12, z= -3>, vel=<x= -2, y= 1, z= -7>
pos=<x=-18, y= -8, z= -2>, vel=<x= 0, y= 6, z= -1>
After 70 steps:
pos=<x=-33, y= -6, z= 5>, vel=<x= -5, y= -4, z= 7>
pos=<x= 13, y= -9, z= 2>, vel=<x= -2, y= 11, z= 3>
pos=<x= 11, y= -8, z= 2>, vel=<x= 8, y= -6, z= -7>
pos=<x= 17, y= 3, z= 1>, vel=<x= -1, y= -1, z= -3>
After 80 steps:
pos=<x= 30, y= -8, z= 3>, vel=<x= 3, y= 3, z= 0>
pos=<x= -2, y= -4, z= 0>, vel=<x= 4, y=-13, z= 2>
pos=<x=-18, y= -7, z= 15>, vel=<x= -8, y= 2, z= -2>
pos=<x= -2, y= -1, z= -8>, vel=<x= 1, y= 8, z= 0>
After 90 steps:
pos=<x=-25, y= -1, z= 4>, vel=<x= 1, y= -3, z= 4>
pos=<x= 2, y= -9, z= 0>, vel=<x= -3, y= 13, z= -1>
pos=<x= 32, y= -8, z= 14>, vel=<x= 5, y= -4, z= 6>
pos=<x= -1, y= -2, z= -8>, vel=<x= -3, y= -6, z= -9>
After 100 steps:
pos=<x= 8, y=-12, z= -9>, vel=<x= -7, y= 3, z= 0>
pos=<x= 13, y= 16, z= -3>, vel=<x= 3, y=-11, z= -5>
pos=<x=-29, y=-11, z= -1>, vel=<x= -3, y= 7, z= 4>
pos=<x= 16, y=-13, z= 23>, vel=<x= 7, y= 1, z= 1>
Energy after 100 steps:
pot: 8 + 12 + 9 = 29; kin: 7 + 3 + 0 = 10; total: 29 * 10 = 290
pot: 13 + 16 + 3 = 32; kin: 3 + 11 + 5 = 19; total: 32 * 19 = 608
pot: 29 + 11 + 1 = 41; kin: 3 + 7 + 4 = 14; total: 41 * 14 = 574
pot: 16 + 13 + 23 = 52; kin: 7 + 1 + 1 = 9; total: 52 * 9 = 468
Sum of total energy: 290 + 608 + 574 + 468 = 1940
What is the total energy in the system after simulating the moons given in your scan for 1000 steps?
--- Part Two ---
All this drifting around in space makes you wonder about the nature of the universe. Does history really repeat itself? You're curious whether the moons will ever return to a previous state.
Determine the number of steps that must occur before all of the moons' positions and velocities exactly match a previous point in time.
For example, the first example above takes 2772 steps before they exactly match a previous point in time; it eventually returns to the initial state:
After 0 steps:
pos=<x= -1, y= 0, z= 2>, vel=<x= 0, y= 0, z= 0>
pos=<x= 2, y=-10, z= -7>, vel=<x= 0, y= 0, z= 0>
pos=<x= 4, y= -8, z= 8>, vel=<x= 0, y= 0, z= 0>
pos=<x= 3, y= 5, z= -1>, vel=<x= 0, y= 0, z= 0>
After 2770 steps:
pos=<x= 2, y= -1, z= 1>, vel=<x= -3, y= 2, z= 2>
pos=<x= 3, y= -7, z= -4>, vel=<x= 2, y= -5, z= -6>
pos=<x= 1, y= -7, z= 5>, vel=<x= 0, y= -3, z= 6>
pos=<x= 2, y= 2, z= 0>, vel=<x= 1, y= 6, z= -2>
After 2771 steps:
pos=<x= -1, y= 0, z= 2>, vel=<x= -3, y= 1, z= 1>
pos=<x= 2, y=-10, z= -7>, vel=<x= -1, y= -3, z= -3>
pos=<x= 4, y= -8, z= 8>, vel=<x= 3, y= -1, z= 3>
pos=<x= 3, y= 5, z= -1>, vel=<x= 1, y= 3, z= -1>
After 2772 steps:
pos=<x= -1, y= 0, z= 2>, vel=<x= 0, y= 0, z= 0>
pos=<x= 2, y=-10, z= -7>, vel=<x= 0, y= 0, z= 0>
pos=<x= 4, y= -8, z= 8>, vel=<x= 0, y= 0, z= 0>
pos=<x= 3, y= 5, z= -1>, vel=<x= 0, y= 0, z= 0>
Of course, the universe might last for a very long time before repeating. Here's a copy of the second example from above:
<x=-8, y=-10, z=0>
<x=5, y=5, z=10>
<x=2, y=-7, z=3>
<x=9, y=-8, z=-3>
This set of initial positions takes 4686774924 steps before it repeats a previous state! Clearly, you might need to find a more efficient way to simulate the universe.
How many steps does it take to reach the first state that exactly matches a previous state?

View File

@ -0,0 +1,4 @@
<x=14, y=9, z=14>
<x=9, y=11, z=6>
<x=-6, y=14, z=-4>
<x=4, y=-4, z=-3>

View File

@ -0,0 +1,96 @@
namespace AdventOfCode2019_12_1
{
const DAY = 12;
const PROBLEM = 1;
export async function run()
{
let input = await AdventOfCode.getInput(DAY);
if (input == null) return;
let moons = input
.trim()
.split(new RegExp('\r?\n'))
.map(p => p.replace(new RegExp('[<>\\sxyz=]', 'g'), ""))
.map(p => p.split(",").map(p => parseInt(p)))
.map(p => new Moon(p[0], p[1], p[2]));
for(let i=0; i<1000;i++)
{
output(i, moons);
step(moons);
}
output(1000, moons);
const energy = moons.map(m => m.getEnergy()).reduce((a,b) => a+b);
AdventOfCode.output(DAY, PROBLEM, energy.toString());
}
function output(i: number, moons: Moon[])
{
AdventOfCode.outputConsole("======== "+i+" ========");
for(let m of moons) AdventOfCode.outputConsole(m.toString());
AdventOfCode.outputConsole();
}
function step(moons: Moon[])
{
for(let i1=0; i1<moons.length; i1++)
for(let i2=i1+1; i2<moons.length; i2++)
{
if (moons[i1].x<moons[i2].x) { moons[i1].dx++; moons[i2].dx--; }
if (moons[i1].x>moons[i2].x) { moons[i1].dx--; moons[i2].dx++; }
if (moons[i1].y<moons[i2].y) { moons[i1].dy++; moons[i2].dy--; }
if (moons[i1].y>moons[i2].y) { moons[i1].dy--; moons[i2].dy++; }
if (moons[i1].z<moons[i2].z) { moons[i1].dz++; moons[i2].dz--; }
if (moons[i1].z>moons[i2].z) { moons[i1].dz--; moons[i2].dz++; }
}
for(let i=0; i<moons.length; i++)
{
moons[i].x += moons[i].dx;
moons[i].y += moons[i].dy;
moons[i].z += moons[i].dz;
}
}
class Moon
{
x: number;
y: number;
z: number;
dx: number = 0;
dy: number = 0;
dz: number = 0;
constructor(x: number, y: number, z: number)
{
this.x=x;
this.y=y;
this.z=z;
}
public toString(): string {
return `pos=<x=${this.x}, y=${this.y}, z=${this.z}>, vel=<x=${this.dx}, y=${this.dy}, z=${this.dz}> [pot=${this.getPotEnergy()}|kin=${this.getKinEnergy()}] => ${this.getEnergy()}`;
}
public getPotEnergy(): number {
return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z);
}
public getKinEnergy(): number {
return Math.abs(this.dx)+Math.abs(this.dy)+Math.abs(this.dz);
}
public getEnergy(): number {
return this.getPotEnergy() * this.getKinEnergy();
}
}
}

View File

@ -0,0 +1,154 @@
namespace AdventOfCode2019_12_2
{
const DAY = 12;
const PROBLEM = 2;
export async function run()
{
let input = await AdventOfCode.getInput(DAY);
if (input == null) return;
let moons = input
.trim()
.split(new RegExp('\r?\n'))
.map(p => p.replace(new RegExp('[<>\\sxyz=]', 'g'), ""))
.map(p => p.split(",").map(p => parseInt(p)))
.map(p => new Moon(p[0], p[1], p[2]));
let cycletimes = [];
for(let dim=0; dim<3; dim++)
{
let moons1d = moons.map(m => Moon1D.createFromMoon(m, dim));
const cycle = getCycleTime(moons1d);
AdventOfCode.outputConsole(`CycleTime Dim[${dim}] ==> ${cycle}`);
cycletimes.push(cycle);
}
const totalctime = lcm(cycletimes)
AdventOfCode.output(DAY, PROBLEM, totalctime.toString());
}
function lcm(v: number[]) // least common multiple
{
return v.map(p => BigInt(p)).reduce((a,b) => lcm2(a,b));
}
function lcm2(a: bigint, b: bigint): bigint // least common multiple
{
return (a*b)/gcd2(a,b);
}
function gcd(v: bigint[]): bigint
{
return v.reduce((a,b) => gcd2(a,b));
}
function gcd2(x: bigint, y: bigint): bigint
{
x = (x<0) ? (-x) : (x);
y = (y<0) ? (-y) : (y);
while(y) { var t = y; y = x % y; x = t; }
return x;
}
function getCycleTime(moons: Moon1D[]): number
{
const orig = moons.map(m => m.clone());
for(let time=0;; time++)
{
step(moons)
if (eq(orig, moons)) return time+1;
}
}
function eq(a: Moon1D[], b: Moon1D[]): boolean
{
for(let i=0; i<a.length; i++)
{
if (a[i].pos != b[i].pos) return false;
if (a[i].vel != b[i].vel) return false;
}
return true;
}
function step(moons: Moon1D[])
{
for(let i1=0; i1<moons.length; i1++)
for(let i2=i1+1; i2<moons.length; i2++)
{
if (moons[i1].pos<moons[i2].pos) { moons[i1].vel++; moons[i2].vel--; }
if (moons[i1].pos>moons[i2].pos) { moons[i1].vel--; moons[i2].vel++; }
}
for(let i=0; i<moons.length; i++)
{
moons[i].pos += moons[i].vel;
}
}
class Moon1D
{
pos: number;
vel: number;
constructor(p: number, v:number)
{
this.pos=p;
this.vel=v;
}
static createFromMoon(m: Moon, idx: number): Moon1D
{
if (idx == 0) { return new Moon1D(m.x, m.dx); }
if (idx == 1) { return new Moon1D(m.y, m.dy); }
if (idx == 2) { return new Moon1D(m.z, m.dz); }
throw "Invalid index";
}
public clone(): Moon1D
{
return new Moon1D(this.pos, this.vel);
}
}
class Moon
{
x: number;
y: number;
z: number;
dx: number = 0;
dy: number = 0;
dz: number = 0;
constructor(x: number, y: number, z: number)
{
this.x=x;
this.y=y;
this.z=z;
}
public toString(): string {
return `pos=<x=${this.x}, y=${this.y}, z=${this.z}>, vel=<x=${this.dx}, y=${this.dy}, z=${this.dz}> [pot=${this.getPotEnergy()}|kin=${this.getKinEnergy()}] => ${this.getEnergy()}`;
}
public getPotEnergy(): number {
return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z);
}
public getKinEnergy(): number {
return Math.abs(this.dx)+Math.abs(this.dy)+Math.abs(this.dz);
}
public getEnergy(): number {
return this.getPotEnergy() * this.getKinEnergy();
}
}
}

View File

@ -0,0 +1,31 @@
--- Day 13: Care Package ---
As you ponder the solitude of space and the ever-increasing three-hour roundtrip for messages between you and Earth, you notice that the Space Mail Indicator Light is blinking. To help keep you sane, the Elves have sent you a care package.
It's a new game for the ship's arcade cabinet! Unfortunately, the arcade is all the way on the other end of the ship. Surely, it won't be hard to build your own - the care package even comes with schematics.
The arcade cabinet runs Intcode software like the game the Elves sent (your puzzle input). It has a primitive screen capable of drawing square tiles on a grid. The software draws tiles to the screen with output instructions: every three output instructions specify the x position (distance from the left), y position (distance from the top), and tile id. The tile id is interpreted as follows:
0 is an empty tile. No game object appears in this tile.
1 is a wall tile. Walls are indestructible barriers.
2 is a block tile. Blocks can be broken by the ball.
3 is a horizontal paddle tile. The paddle is indestructible.
4 is a ball tile. The ball moves diagonally and bounces off objects.
For example, a sequence of output values like 1,2,3,6,5,4 would draw a horizontal paddle tile (1 tile from the left and 2 tiles from the top) and a ball tile (6 tiles from the left and 5 tiles from the top).
Start the game. How many block tiles are on the screen when the game exits?
--- Part Two ---
The game didn't run because you didn't put in any quarters. Unfortunately, you did not bring any quarters. Memory address 0 represents the number of quarters that have been inserted; set it to 2 to play for free.
The arcade cabinet has a joystick that can move left and right. The software reads the position of the joystick with input instructions:
If the joystick is in the neutral position, provide 0.
If the joystick is tilted to the left, provide -1.
If the joystick is tilted to the right, provide 1.
The arcade cabinet also has a segment display capable of showing a single number that represents the player's current score. When three output instructions specify X=-1, Y=0, the third output instruction is not a tile; the value instead specifies the new score to show in the segment display. For example, a sequence of output values like -1,0,12345 would show 12345 as the player's current score.
Beat the game by breaking all the blocks. What is your score after the last block is broken?

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,274 @@
namespace AdventOfCode2019_13_1
{
const DAY = 13;
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()));
const rnr = new Interpreter(code, []);
rnr.fullRun();
let tiles = [];
AdventOfCode.outputConsole(rnr.output);
for(let i=0; i<rnr.output.length; i+=3)
{
if (rnr.output[i+2] === 2) tiles.push( rnr.output[i+0]*100000 + rnr.output[i+1] );
}
let count = tiles.filter((v,i,s) => s.indexOf(v)===i).length;
AdventOfCode.output(DAY, PROBLEM, count.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;
}
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,343 @@
namespace AdventOfCode2019_13_2
{
const DAY = 13;
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()));
code[0] = 2;
const rnr = new Interpreter(code, []);
let world: { [_:number]:[number, number, number] } = {};
let score = 0;
let minx=0;
let maxx=0;
let miny=0;
let maxy=0;
for(;;)
{
const ssr = rnr.singleStep();
if (ssr === StepResult.HALTED) break;
if (rnr.output.length === 3)
{
const x = rnr.output[0];
const y = rnr.output[1];
const v = rnr.output[2];
rnr.output = [];
if (x===-1 && y === 0)
{
score = v;
}
else
{
world[y*10000+x]=[x,y,v];
minx = Math.min(minx, x);
maxx = Math.max(maxx, x);
miny = Math.min(miny, y);
maxy = Math.max(maxy, y);
}
}
if (ssr === StepResult.WAITING_FOR_IN)
{
const paddle = avgpos(world, 3);
const ball = avgpos(world, 4);
let str = "";
for(let yy=miny;yy<=maxy;yy++)
{
for(let xx=minx;xx<=maxx;xx++)
{
const vvv = ((yy*10000+xx) in world) ? world[yy*10000+xx][2] : 0;
if (vvv === 0) str += ".";
if (vvv === 1) str += "\u2588";
if (vvv === 2) str += "+";
if (vvv === 3) str += "-";
if (vvv === 4) str += "o";
}
str += "\n";
}
const ddd = Math.sign(ball-paddle);
await AdventOfCode.outputIntermed(str+"\n\n (("+score+"))")
rnr.inputqueue.push(ddd);
}
}
AdventOfCode.output(DAY, PROBLEM, score.toString());
}
function avgpos(world: { [_:number]:[number, number, number] }, search: number)
{
let xsum = 0;
let count = 0;
for(let v of Object.values(world))
{
if (v[2] !== search) continue;
xsum += v[0];
count++;
}
if (count === 0) throw (search + " not found");
return Math.round(xsum/count);
}
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;
}
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,103 @@
--- Day 14: Space Stoichiometry ---
As you approach the rings of Saturn, your ship's low fuel indicator turns on. There isn't any fuel here, but the rings have plenty of raw material. Perhaps your ship's Inter-Stellar Refinery Union brand nanofactory can turn these raw materials into fuel.
You ask the nanofactory to produce a list of the reactions it can perform that are relevant to this process (your puzzle input). Every reaction turns some quantities of specific input chemicals into some quantity of an output chemical. Almost every chemical is produced by exactly one reaction; the only exception, ORE, is the raw material input to the entire process and is not produced by a reaction.
You just need to know how much ORE you'll need to collect before you can produce one unit of FUEL.
Each reaction gives specific quantities for its inputs and output; reactions cannot be partially run, so only whole integer multiples of these quantities can be used. (It's okay to have leftover chemicals when you're done, though.) For example, the reaction 1 A, 2 B, 3 C => 2 D means that exactly 2 units of chemical D can be produced by consuming exactly 1 A, 2 B and 3 C. You can run the full reaction as many times as necessary; for example, you could produce 10 D by consuming 5 A, 10 B, and 15 C.
Suppose your nanofactory produces the following list of reactions:
10 ORE => 10 A
1 ORE => 1 B
7 A, 1 B => 1 C
7 A, 1 C => 1 D
7 A, 1 D => 1 E
7 A, 1 E => 1 FUEL
The first two reactions use only ORE as inputs; they indicate that you can produce as much of chemical A as you want (in increments of 10 units, each 10 costing 10 ORE) and as much of chemical B as you want (each costing 1 ORE). To produce 1 FUEL, a total of 31 ORE is required: 1 ORE to produce 1 B, then 30 more ORE to produce the 7 + 7 + 7 + 7 = 28 A (with 2 extra A wasted) required in the reactions to convert the B into C, C into D, D into E, and finally E into FUEL. (30 A is produced because its reaction requires that it is created in increments of 10.)
Or, suppose you have the following list of reactions:
9 ORE => 2 A
8 ORE => 3 B
7 ORE => 5 C
3 A, 4 B => 1 AB
5 B, 7 C => 1 BC
4 C, 1 A => 1 CA
2 AB, 3 BC, 4 CA => 1 FUEL
The above list of reactions requires 165 ORE to produce 1 FUEL:
Consume 45 ORE to produce 10 A.
Consume 64 ORE to produce 24 B.
Consume 56 ORE to produce 40 C.
Consume 6 A, 8 B to produce 2 AB.
Consume 15 B, 21 C to produce 3 BC.
Consume 16 C, 4 A to produce 4 CA.
Consume 2 AB, 3 BC, 4 CA to produce 1 FUEL.
Here are some larger examples:
13312 ORE for 1 FUEL:
157 ORE => 5 NZVS
165 ORE => 6 DCFZ
44 XJWVT, 5 KHKGT, 1 QDVJ, 29 NZVS, 9 GPVTF, 48 HKGWZ => 1 FUEL
12 HKGWZ, 1 GPVTF, 8 PSHF => 9 QDVJ
179 ORE => 7 PSHF
177 ORE => 5 HKGWZ
7 DCFZ, 7 PSHF => 2 XJWVT
165 ORE => 2 GPVTF
3 DCFZ, 7 NZVS, 5 HKGWZ, 10 PSHF => 8 KHKGT
180697 ORE for 1 FUEL:
2 VPVL, 7 FWMGM, 2 CXFTF, 11 MNCFX => 1 STKFG
17 NVRVD, 3 JNWZP => 8 VPVL
53 STKFG, 6 MNCFX, 46 VJHF, 81 HVMC, 68 CXFTF, 25 GNMV => 1 FUEL
22 VJHF, 37 MNCFX => 5 FWMGM
139 ORE => 4 NVRVD
144 ORE => 7 JNWZP
5 MNCFX, 7 RFSQX, 2 FWMGM, 2 VPVL, 19 CXFTF => 3 HVMC
5 VJHF, 7 MNCFX, 9 VPVL, 37 CXFTF => 6 GNMV
145 ORE => 6 MNCFX
1 NVRVD => 8 CXFTF
1 VJHF, 6 MNCFX => 4 RFSQX
176 ORE => 6 VJHF
2210736 ORE for 1 FUEL:
171 ORE => 8 CNZTR
7 ZLQW, 3 BMBT, 9 XCVML, 26 XMNCP, 1 WPTQ, 2 MZWV, 1 RJRHP => 4 PLWSL
114 ORE => 4 BHXH
14 VRPVC => 6 BMBT
6 BHXH, 18 KTJDG, 12 WPTQ, 7 PLWSL, 31 FHTLT, 37 ZDVW => 1 FUEL
6 WPTQ, 2 BMBT, 8 ZLQW, 18 KTJDG, 1 XMNCP, 6 MZWV, 1 RJRHP => 6 FHTLT
15 XDBXC, 2 LTCX, 1 VRPVC => 6 ZLQW
13 WPTQ, 10 LTCX, 3 RJRHP, 14 XMNCP, 2 MZWV, 1 ZLQW => 1 ZDVW
5 BMBT => 4 WPTQ
189 ORE => 9 KTJDG
1 MZWV, 17 XDBXC, 3 XCVML => 2 XMNCP
12 VRPVC, 27 CNZTR => 2 XDBXC
15 KTJDG, 12 BHXH => 5 XCVML
3 BHXH, 2 VRPVC => 7 MZWV
121 ORE => 7 VRPVC
7 XCVML => 6 RJRHP
5 BHXH, 4 VRPVC => 5 LTCX
Given the list of reactions in your puzzle input, what is the minimum amount of ORE required to produce exactly 1 FUEL?
--- Part Two ---
After collecting ORE for a while, you check your cargo hold: 1 trillion (1000000000000) units of ORE.
With that much ore, given the examples above:
The 13312 ORE-per-FUEL example could produce 82892753 FUEL.
The 180697 ORE-per-FUEL example could produce 5586022 FUEL.
The 2210736 ORE-per-FUEL example could produce 460664 FUEL.
Given 1 trillion ORE, what is the maximum amount of FUEL you can produce?

View File

@ -0,0 +1,55 @@
1 GZJM, 2 CQFGM, 20 SNPQ, 7 RVQG, 3 FBTV, 27 SQLH, 10 HFGCF, 3 ZQCH => 3 SZCN
4 FCDL, 6 NVPW, 21 GZJM, 1 FBTV, 1 NLSNB, 7 HFGCF, 3 SNPQ => 1 LRPK
15 FVHTD, 2 HBGFL => 4 BCVLZ
4 GFGS => 4 RVQG
5 BCVLZ, 4 LBQV => 7 TWSRV
6 DWKTF, 4 VCKL => 4 KDJV
16 WZJB => 4 RBGJQ
8 RBGJQ, 5 FCDL, 2 LWBQ => 1 MWSX
100 ORE => 7 WBRL
7 PGZGQ => 5 FVHTD
1 JCDML, 2 TWSRV => 9 JSQSB
3 WZJB, 1 NXNR => 6 XFPVS
7 JPCPK => 8 JCDML
11 LWBQ, 8 XFPVS => 9 PSPFR
2 TWSRV => 8 NVPW
2 LBQV => 1 PMJFD
2 LCZBD => 3 FBTV
1 WBQC, 1 ZPNKQ => 8 JPCPK
44 HFGCF, 41 PSPFR, 26 LMSCR, 14 MLMDC, 6 BWTHK, 3 PRKPC, 13 LRPK, 50 MWSX, 8 SZCN => 1 FUEL
1 XFPVS => 4 BJRSZ
1 GWBDR, 1 MBQC => 4 HZPRB
2 BJRSZ, 9 KDJV, 1 XFPVS => 8 SNVL
7 PMJFD, 30 SNVL, 1 BJRSZ => 2 JMTG
8 SNVL, 1 RBGJQ => 9 FCDL
2 HZPRB => 6 NLSNB
2 GRDG => 9 VCKL
1 FVHTD => 9 WZJB
130 ORE => 2 GRDG
3 WZJB, 1 GFGS, 1 NXNR => 9 SNPQ
9 VCKL => 5 WBQC
1 WBRL, 11 FPMPB => 7 PGZGQ
118 ORE => 3 LMSCR
3 SQLH, 1 PMJFD, 4 XJBL => 7 MLMDC
1 LMSCR, 10 GRDG => 2 TBDH
6 DWKTF => 2 SQLH
2 BJRSZ, 1 PGZGQ, 3 NXNR => 7 MBQC
5 PRKPC => 7 NXNR
9 SQLH => 5 LCZBD
1 FCDL => 9 CQFGM
5 PGZGQ, 1 TBDH => 8 HBGFL
15 JSQSB => 5 HFGCF
2 PGZGQ, 1 VCKL => 4 ZPNKQ
3 FBTV, 3 JMTG => 5 QLHKT
1 ZGZST, 2 LCZBD => 7 GFGS
2 RVQG => 4 ZQCH
1 ZPNKQ => 5 LBQV
3 LWBQ => 8 XJBL
1 LBQV, 9 JCDML => 3 GWBDR
8 VCKL, 6 FVHTD => 9 DWKTF
3 JCDML => 3 ZGZST
160 ORE => 5 FPMPB
3 SQLH, 22 LBQV, 5 BCVLZ => 6 PRKPC
1 WZJB => 2 GZJM
10 ZGZST => 2 LWBQ
5 TBDH, 19 NXNR, 9 QLHKT, 2 KDJV, 1 SQLH, 1 GWBDR, 6 HFGCF => 4 BWTHK

View File

@ -0,0 +1,117 @@
namespace AdventOfCode2019_14_1
{
const DAY = 14;
const PROBLEM = 1;
export async function run()
{
let input = await AdventOfCode.getInput(DAY);
if (input == null) return;
let recipes: { [_:string]:[number, [number, string][], string ] } = {};
for (let line of input.split(new RegExp('\r?\n')).filter(p => p.trim().length > 0))
{
const left = line.split(" => ")[0];
const right = line.split(" => ")[1];
const right_n = parseInt(right.split(" ")[0]);
const right_v = right.split(" ")[1];
recipes[right_v] = [ right_n, left.split(", ").map(p => [ parseInt(p.split(" ")[0]), p.split(" ")[1] ] ), line ];
}
// -------------
let missing : [number, string][] = [ [1, "FUEL"] ];
let surplus : [number, string][] = [];
while( missing.length>1 || missing[0][1] !== "ORE" )
{
missing = missing.filter(p => p[0]>0);
surplus = surplus.filter(p => p[0]>0);
AdventOfCode.outputConsole("missing: [" + missing.map(p => p[0]+"|"+p[1]).reduce((a,b)=>a+" "+b, "")+"]");
AdventOfCode.outputConsole("surplus: [" + surplus.map(p => p[0]+"|"+p[1]).reduce((a,b)=>a+" "+b, "")+"]");
let target = missing.pop();
if (target === undefined) throw "undef??";
if (target[1] === "ORE")
{
missing.push(target);
missing = missing.reverse();
target = missing.pop();
if (target === undefined) throw "undef??";
}
const recipe = recipes[target[1]];
if (target[0] > recipe[0])
{
missing.push([ target[0] - recipe[0], target[1] ]);
}
else if (target[0] < recipe[0])
{
let found=false;
for(let i=0; i<surplus.length; i++)
{
if (surplus[i][1] === target[1])
{
surplus[i][0] += (recipe[0] - target[0]);
found = true;
break;
}
}
if (!found) surplus.push([ (recipe[0] - target[0]), target[1] ]);
}
let incredentials = Object.assign([], recipe[1]);
AdventOfCode.outputConsole(recipe[2]);
for(let icrd of incredentials)
{
let incred_count = icrd[0];
const incred_name = icrd[1];
for(let i=0; i<surplus.length; i++)
{
if (surplus[i][1] === incred_name)
{
if (surplus[i][0] >= incred_count)
{
surplus[i][0] -= incred_count;
incred_count = 0;
}
else
{
incred_count -= surplus[i][0];
surplus[i][0] = 0;
}
}
}
if (incred_count > 0)
{
let found=false;
for(let i=0; i<missing.length; i++)
{
if (missing[i][1] === incred_name)
{
missing[i][0] += incred_count;
found = true;
break;
}
}
if (!found) missing.push([incred_count, incred_name]);
}
AdventOfCode.outputConsole("");
}
}
AdventOfCode.outputConsole(missing);
AdventOfCode.output(DAY, PROBLEM, missing[0][0].toString());
}
}

View File

@ -0,0 +1,154 @@
namespace AdventOfCode2019_14_2
{
const DAY = 14;
const PROBLEM = 2;
export async function run()
{
let input = await AdventOfCode.getInput(DAY);
if (input == null) return;
let recipes: { [_:string]:[number, [number, string][], string ] } = {};
for (let line of input.split(new RegExp('\r?\n')).filter(p => p.trim().length > 0))
{
const left = line.split(" => ")[0];
const right = line.split(" => ")[1];
const right_n = parseInt(right.split(" ")[0]);
const right_v = right.split(" ")[1];
recipes[right_v] = [ right_n, left.split(", ").map(p => [ parseInt(p.split(" ")[0]), p.split(" ")[1] ] ), line ];
}
// binary search
const target = 1_000_000_000_000;
let lower = 1;
let upper = 1_000_000_000_000;
AdventOfCode.outputConsole(`Range: [${lower} | ${upper}]`);
while(upper-lower>1)
{
const f = Math.floor((upper+lower) / 2);
const o = calcOreRequirement(recipes, f);
AdventOfCode.outputConsole(`${o} ORE => ${f} FUEL`);
if (o > target) {
upper = f;
} else if (o < target) {
lower = f;
} else {
upper = lower = f; // jackpot
}
AdventOfCode.outputConsole(`Range: [${lower} | ${upper}]`);
}
AdventOfCode.output(DAY, PROBLEM, lower.toString());
}
function calcOreRequirement(recipes: { [_:string]:[number, [number, string][], string ] }, fuelcount: number): number
{
let missing : [number, string][] = [ [fuelcount, "FUEL"] ];
let surplus : [number, string][] = [];
while( missing.length>1 || missing[0][1] !== "ORE" )
{
missing = missing.filter(p => p[0]>0);
surplus = surplus.filter(p => p[0]>0);
//AdventOfCode.outputConsole("missing: [" + missing.map(p => p[0]+"|"+p[1]).reduce((a,b)=>a+" "+b, "")+"]");
//AdventOfCode.outputConsole("surplus: [" + surplus.map(p => p[0]+"|"+p[1]).reduce((a,b)=>a+" "+b, "")+"]");
let target = missing.pop();
if (target === undefined) throw "undef??";
if (target[1] === "ORE")
{
missing.push(target);
missing = missing.reverse();
target = missing.pop();
if (target === undefined) throw "undef??";
}
const orig_recipe = recipes[target[1]];
const factor = Math.max(1, Math.ceil(target[0] / orig_recipe[0]));
let recipe: [number, [number, string][], string] =
[
factor*orig_recipe[0],
Object.assign([], orig_recipe[1]).map(p => [ factor*p[0], p[1] ] ),
(""+factor+"*("+orig_recipe[2]+")")
];
if (target[0] > recipe[0])
{
missing.push([ target[0] - recipe[0], target[1] ]);
}
else if (target[0] < recipe[0])
{
let found=false;
for(let i=0; i<surplus.length; i++)
{
if (surplus[i][1] === target[1])
{
surplus[i][0] += (recipe[0] - target[0]);
found = true;
break;
}
}
if (!found) surplus.push([ (recipe[0] - target[0]), target[1] ]);
}
let incredentials = Object.assign([], recipe[1]);
//AdventOfCode.outputConsole(recipe[2]);
for(let icrd of incredentials)
{
let incred_count = icrd[0];
const incred_name = icrd[1];
for(let i=0; i<surplus.length; i++)
{
if (surplus[i][1] === incred_name)
{
if (surplus[i][0] >= incred_count)
{
surplus[i][0] -= incred_count;
incred_count = 0;
}
else
{
incred_count -= surplus[i][0];
surplus[i][0] = 0;
}
}
}
if (incred_count > 0)
{
let found=false;
for(let i=0; i<missing.length; i++)
{
if (missing[i][1] === incred_name)
{
missing[i][0] += incred_count;
found = true;
break;
}
}
if (!found) missing.push([incred_count, incred_name]);
}
//AdventOfCode.outputConsole("");
}
}
return missing[0][0];
}
}

View File

@ -0,0 +1,126 @@
--- Day 15: Oxygen System ---
Out here in deep space, many things can go wrong. Fortunately, many of those things have indicator lights. Unfortunately, one of those lights is lit: the oxygen system for part of the ship has failed!
According to the readouts, the oxygen system must have failed days ago after a rupture in oxygen tank two; that section of the ship was automatically sealed once oxygen levels went dangerously low. A single remotely-operated repair droid is your only option for fixing the oxygen system.
The Elves' care package included an Intcode program (your puzzle input) that you can use to remotely control the repair droid. By running that program, you can direct the repair droid to the oxygen system and fix the problem.
The remote control program executes the following steps in a loop forever:
Accept a movement command via an input instruction.
Send the movement command to the repair droid.
Wait for the repair droid to finish the movement operation.
Report on the status of the repair droid via an output instruction.
Only four movement commands are understood: north (1), south (2), west (3), and east (4). Any other command is invalid. The movements differ in direction, but not in distance: in a long enough east-west hallway, a series of commands like 4,4,4,4,3,3,3,3 would leave the repair droid back where it started.
The repair droid can reply with any of the following status codes:
0: The repair droid hit a wall. Its position has not changed.
1: The repair droid has moved one step in the requested direction.
2: The repair droid has moved one step in the requested direction; its new position is the location of the oxygen system.
You don't know anything about the area around the repair droid, but you can figure it out by watching the status codes.
For example, we can draw the area using D for the droid, # for walls, . for locations the droid can traverse, and empty space for unexplored locations. Then, the initial state looks like this:
D
To make the droid go north, send it 1. If it replies with 0, you know that location is a wall and that the droid didn't move:
#
D
To move east, send 4; a reply of 1 means the movement was successful:
#
.D
Then, perhaps attempts to move north (1), south (2), and east (4) are all met with replies of 0:
##
.D#
#
Now, you know the repair droid is in a dead end. Backtrack with 3 (which you already know will get a reply of 1 because you already know that location is open):
##
D.#
#
Then, perhaps west (3) gets a reply of 0, south (2) gets a reply of 1, south again (2) gets a reply of 0, and then west (3) gets a reply of 2:
##
#..#
D.#
#
Now, because of the reply of 2, you know you've found the oxygen system! In this example, it was only 2 moves away from the repair droid's starting position.
What is the fewest number of movement commands required to move the repair droid from its starting position to the location of the oxygen system?
--- Part Two ---
You quickly repair the oxygen system; oxygen gradually fills the area.
Oxygen starts in the location containing the repaired oxygen system. It takes one minute for oxygen to spread to all open locations that are adjacent to a location that already contains oxygen. Diagonal locations are not adjacent.
In the example above, suppose you've used the droid to explore the area fully and have the following map (where locations that currently contain oxygen are marked O):
##
#..##
#.#..#
#.O.#
###
Initially, the only location which contains oxygen is the location of the repaired oxygen system. However, after one minute, the oxygen spreads to all open (.) locations that are adjacent to a location containing oxygen:
##
#..##
#.#..#
#OOO#
###
After a total of two minutes, the map looks like this:
##
#..##
#O#O.#
#OOO#
###
After a total of three minutes:
##
#O.##
#O#OO#
#OOO#
###
And finally, the whole region is full of oxygen after a total of four minutes:
##
#OO##
#O#OO#
#OOO#
###
So, in this example, all locations contain oxygen after 4 minutes.
Use the repair droid to get a complete map of the area. How many minutes will it take to fill with oxygen?

View File

@ -0,0 +1 @@
3,1033,1008,1033,1,1032,1005,1032,31,1008,1033,2,1032,1005,1032,58,1008,1033,3,1032,1005,1032,81,1008,1033,4,1032,1005,1032,104,99,101,0,1034,1039,102,1,1036,1041,1001,1035,-1,1040,1008,1038,0,1043,102,-1,1043,1032,1,1037,1032,1042,1106,0,124,101,0,1034,1039,101,0,1036,1041,1001,1035,1,1040,1008,1038,0,1043,1,1037,1038,1042,1105,1,124,1001,1034,-1,1039,1008,1036,0,1041,1002,1035,1,1040,1001,1038,0,1043,1001,1037,0,1042,1106,0,124,1001,1034,1,1039,1008,1036,0,1041,102,1,1035,1040,1002,1038,1,1043,102,1,1037,1042,1006,1039,217,1006,1040,217,1008,1039,40,1032,1005,1032,217,1008,1040,40,1032,1005,1032,217,1008,1039,9,1032,1006,1032,165,1008,1040,33,1032,1006,1032,165,1101,2,0,1044,1106,0,224,2,1041,1043,1032,1006,1032,179,1102,1,1,1044,1106,0,224,1,1041,1043,1032,1006,1032,217,1,1042,1043,1032,1001,1032,-1,1032,1002,1032,39,1032,1,1032,1039,1032,101,-1,1032,1032,101,252,1032,211,1007,0,53,1044,1106,0,224,1101,0,0,1044,1106,0,224,1006,1044,247,1001,1039,0,1034,101,0,1040,1035,102,1,1041,1036,101,0,1043,1038,101,0,1042,1037,4,1044,1105,1,0,64,32,22,30,60,6,82,30,34,77,7,15,17,32,43,96,51,43,27,74,39,14,10,70,32,20,59,35,98,3,81,47,40,23,65,16,1,82,35,35,44,76,93,55,6,40,65,15,62,62,67,7,72,21,92,85,54,71,42,84,80,30,64,88,50,90,16,34,63,20,88,24,64,86,11,64,20,44,23,63,11,26,10,84,75,13,93,39,16,67,2,91,97,22,86,40,69,11,40,58,93,22,82,30,24,40,58,26,75,70,52,20,27,95,57,23,69,9,30,82,87,70,42,32,90,67,36,92,41,97,72,24,3,36,60,96,5,62,13,74,27,21,60,58,90,17,49,27,70,29,59,48,72,30,35,11,21,60,99,35,37,71,9,84,3,22,74,20,48,70,19,58,65,22,14,72,15,7,31,77,61,5,31,60,24,80,33,58,49,78,80,37,79,66,37,83,4,21,50,65,96,23,67,89,44,17,58,60,41,96,96,39,27,62,84,18,74,38,56,9,72,70,32,62,95,6,87,51,96,36,4,3,79,21,21,31,66,93,13,10,77,43,52,68,66,47,42,55,57,23,60,45,63,3,86,96,29,70,81,31,3,48,38,91,34,69,85,18,95,93,96,85,15,38,80,35,17,98,92,14,57,60,25,46,63,60,16,58,25,48,73,59,40,6,72,46,91,39,22,63,79,58,67,84,33,52,78,52,26,21,61,49,78,77,5,95,75,20,56,30,43,67,75,33,84,10,14,60,21,98,14,31,81,97,49,64,19,69,44,3,68,2,66,20,69,48,81,96,22,56,22,25,27,60,59,36,10,45,81,39,46,97,54,49,42,78,89,26,93,55,14,96,48,48,96,57,51,82,94,23,46,64,20,10,56,19,63,41,77,17,26,68,47,37,97,84,6,93,26,99,1,11,84,12,79,74,34,85,25,48,92,69,68,44,59,35,99,33,88,75,29,12,87,79,37,74,24,98,4,68,1,85,43,31,60,2,82,16,51,65,97,4,82,42,52,82,56,58,24,33,60,22,65,29,43,75,10,72,34,97,70,11,36,89,26,69,84,26,50,17,42,83,44,63,1,84,77,22,89,46,72,79,93,22,94,34,79,48,68,55,3,73,91,30,79,37,76,19,24,61,41,98,32,12,6,57,16,44,55,43,63,55,98,11,68,17,50,67,26,86,19,60,14,56,30,59,11,9,41,26,59,39,56,49,48,82,3,83,64,69,48,65,89,42,78,33,25,91,92,50,91,8,64,73,92,16,96,28,40,27,67,22,69,95,7,12,70,56,49,81,22,68,67,40,48,92,43,14,86,60,49,39,74,58,42,43,54,37,2,84,25,41,22,22,65,16,6,67,62,22,25,88,52,76,88,40,20,75,84,24,4,39,99,51,72,73,14,15,81,39,70,15,26,15,32,34,83,33,73,95,14,55,91,65,81,44,3,89,49,80,5,38,56,42,76,68,14,4,76,71,98,65,62,31,6,96,23,77,82,4,59,91,29,14,91,42,80,35,2,58,99,27,40,64,43,86,74,17,58,68,24,71,51,73,35,74,63,50,17,24,5,83,71,12,62,33,19,31,84,41,25,71,75,41,43,55,85,22,11,88,71,49,33,55,50,63,52,64,23,25,42,85,47,49,30,65,42,95,61,15,86,7,61,62,32,79,62,82,13,84,30,69,21,70,22,99,95,71,5,71,11,69,39,85,79,89,41,94,82,86,46,83,96,80,48,74,63,56,8,58,38,66,12,61,33,88,30,92,27,57,0,0,21,21,1,10,1,0,0,0,0,0,0

View File

@ -0,0 +1,664 @@
namespace AdventOfCode2019_15_1
{
const DAY = 15;
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 wm = new WorldMap();
let rnr = new Interpreter(code, []);
let d = Direction.North;
await AdventOfCode.outputIntermed(wm.dump());
for(;;)
{
if (wm.isfree(move_pos(wm.position, turn_right(d))))
{
d = turn_right(d);
}
rnr.inputqueue.push(d);
rnr.autoRun();
if (rnr.is_halted) throw "halted";
if (rnr.output.length !== 1) throw "out";
let nx = wm.position[0];
let ny = wm.position[1];
if (d === Direction.North) ny--;
if (d === Direction.East) nx++;
if (d === Direction.South) ny++;
if (d === Direction.West) nx--;
if (rnr.output[0] === 0)
{
wm.set_wall(nx, ny);
d = turn_left(d);
}
else if (rnr.output[0] === 1)
{
wm.set_empty(nx, ny);
wm.position = [nx, ny];
}
else if (rnr.output[0] === 2)
{
wm.set_target(nx, ny);
wm.position = [nx, ny];
}
rnr.output = [];
await AdventOfCode.outputIntermed(wm.dump());
if (wm.unknowns.length === 0) break;
}
await AdventOfCode.outputIntermed(wm.dump());
const dist = await wm.calcDistance();
await AdventOfCode.outputIntermed(wm.dump3());
AdventOfCode.output(DAY, PROBLEM, dist.toString());
}
function turn_right(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 "d";
}
function turn_left(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 "d";
}
function move_pos(p: [number, number], d: Direction): [number, number]
{
let nx = p[0];
let ny = p[1];
if (d === Direction.North) ny--;
if (d === Direction.East) nx++;
if (d === Direction.South) ny++;476
if (d === Direction.West) nx--;
return [nx, ny];
}
enum Direction
{
North = 1,
South = 2,
West = 3,
East = 4,
}
enum Field
{
Unknown = 0,
Wall = 1,
Empty = 2,
POI = 3, // possible new way to go, but unknown
Target = 4,
Path = 5,
}
class WorldMap
{
world: { [_:number]: Field } = {};
unknowns: [number, number][] = [];
position: [number, number] = [0, 0];
minx: number = 0;
miny: number = 0;
maxx: number = 0;
maxy: number = 0;
target_pos: [number,number] = [NaN, NaN];
distance_map: { [_:number]: number } = {};
constructor()
{
this.set_empty(0, 0);
}
async calcDistance(): Promise<number>
{
this.position = [0,0];
let distance_map: { [_:number]: number } = {};
distance_map[0] = 0;
let updates: [number, number][] = [];
updates.unshift([-1,0]);
updates.unshift([+1,0]);
updates.unshift([0,+1]);
updates.unshift([0,-1]);
while(updates.length > 0)
{
const pos = updates.pop();
if (pos === undefined) throw "undef";
if (!this.isfree(pos)) continue;
const x = pos[0];
const y = pos[1];
const i = (y*10000000 + x);
const i_n = ((y-1)*10000000 + (x));
const i_e = ((y)*10000000 + (x+1));
const i_s = ((y+1)*10000000 + (x));
const i_w = ((y)*10000000 + (x-1));
const d_curr = (i in distance_map) ? distance_map[i] : Number.MAX_SAFE_INTEGER;
const d_n = (i_n in distance_map) ? distance_map[i_n] : Number.MAX_SAFE_INTEGER;
const d_e = (i_e in distance_map) ? distance_map[i_e] : Number.MAX_SAFE_INTEGER;
const d_s = (i_s in distance_map) ? distance_map[i_s] : Number.MAX_SAFE_INTEGER;
const d_w = (i_w in distance_map) ? distance_map[i_w] : Number.MAX_SAFE_INTEGER;
let d_new = Math.min(d_n, d_e, d_s, d_w);
if (d_new !== Number.MAX_SAFE_INTEGER) d_new++;
await AdventOfCode.outputIntermed(this.dump2(distance_map, updates));
if (d_curr <= d_new) continue;
distance_map[i] = d_new;
updates.unshift([x+1, y]);
updates.unshift([x-1, y]);
updates.unshift([x, y+1]);
updates.unshift([x, y-1]);
}
await AdventOfCode.outputIntermed(this.dump2(distance_map, updates));
let tpos = this.target_pos;
for(;;)
{
const x = tpos[0];
const y = tpos[1];
const i = (y*10000000 + x);
const d = distance_map[i];
const i_n = ((y-1)*10000000 + (x));
const i_e = ((y)*10000000 + (x+1));
const i_s = ((y+1)*10000000 + (x));
const i_w = ((y)*10000000 + (x-1));
const d_n = (i_n in distance_map) ? distance_map[i_n] : Number.MAX_SAFE_INTEGER;
const d_e = (i_e in distance_map) ? distance_map[i_e] : Number.MAX_SAFE_INTEGER;
const d_s = (i_s in distance_map) ? distance_map[i_s] : Number.MAX_SAFE_INTEGER;
const d_w = (i_w in distance_map) ? distance_map[i_w] : Number.MAX_SAFE_INTEGER;
if (d === 1) break;
if (d_n+1 === d)
{
tpos = [tpos[0], tpos[1]-1];
this.set(tpos[0], tpos[1], Field.Path);
}
else if (d_e+1 === d)
{
tpos = [tpos[0]+1, tpos[1]];
this.set(tpos[0], tpos[1], Field.Path);
}
else if (d_s+1 === d)
{
tpos = [tpos[0], tpos[1]+1];
this.set(tpos[0], tpos[1], Field.Path);
}
else if (d_w+1 === d)
{
tpos = [tpos[0]-1, tpos[1]];
this.set(tpos[0], tpos[1], Field.Path);
}
else throw "";
}
return distance_map[(this.target_pos[1]*10000000 + this.target_pos[0])];
}
dump2(distance_map: { [_:number]: number }, updates: [number, number][]): string
{
let str = "";
for(let yy=this.miny;yy<=this.maxy;yy++)
{
for(let xx=this.minx;xx<=this.maxx;xx++)
{
const i = (yy*10000000 + xx);
if (this.get(xx, yy) === Field.Wall)
{
str += "#";
}
else if (updates.filter(u => u[0] === xx && u[1] === yy).length > 0)
{
str += "@";
}
else if (i in distance_map)
{
str += "+";
}
else
{
str += ".";
}
}
str += "\n";
}
return str;
}
isfree(p: [number, number]): boolean
{
const f = this.get(p[0], p[1]);
return (f !== Field.Wall) && (f !== Field.Unknown);
}
get(x: number, y: number): Field
{
const i = (y*10000000 + x);
if (!(i in this.world)) return Field.Unknown;
return this.world[i];
}
set(x: number, y: number, f: Field)
{
const i = (y*10000000 + x);
if (this.world[i] === Field.POI) this.unknowns = this.unknowns.filter(p => p[0] !== x || p[1] !== y);
this.world[i] = f;
if (f === Field.POI) this.unknowns.push([x, y]);
if (f === Field.Target) this.target_pos = [x, y];
this.minx = Math.min(this.minx, x);
this.maxx = Math.max(this.maxx, x);
this.miny = Math.min(this.miny, y);
this.maxy = Math.max(this.maxy, y);
}
set_empty(x: number, y: number)
{
this.set(x, y, Field.Empty);
if (this.get(x-1, y) === Field.Unknown) this.set(x-1, y, Field.POI);
if (this.get(x+1, y) === Field.Unknown) this.set(x+1, y, Field.POI);
if (this.get(x, y-1) === Field.Unknown) this.set(x, y-1, Field.POI);
if (this.get(x, y+1) === Field.Unknown) this.set(x, y+1, Field.POI);
}
set_target(x: number, y: number)
{
this.set(x, y, Field.Target);
if (this.get(x-1, y) === Field.Unknown) this.set(x-1, y, Field.POI);
if (this.get(x+1, y) === Field.Unknown) this.set(x+1, y, Field.POI);
if (this.get(x, y-1) === Field.Unknown) this.set(x, y-1, Field.POI);
if (this.get(x, y+1) === Field.Unknown) this.set(x, y+1, Field.POI);
}
set_wall(x: number, y: number)
{
this.set(x, y, Field.Wall);
}
dump(): string
{
let str = "";
for(let yy=this.miny;yy<=this.maxy;yy++)
{
for(let xx=this.minx;xx<=this.maxx;xx++)
{
if (xx === this.position[0] && yy === this.position[1])
{
str += "X";
}
else if (xx === 0 && yy === 0)
{
str += "0";
}
else
{
switch(this.get(xx, yy))
{
case Field.Empty: str += "."; break;
case Field.POI: str += "?"; break;
case Field.Target: str += "@"; break;
case Field.Unknown: str += " "; break;
case Field.Wall: str += "#"; break;
case Field.Path: str += "="; break;
}
}
}
str += "\n";
}
return str;
}
dump3(): string
{
let str = "";
for(let yy=this.miny;yy<=this.maxy;yy++)
{
for(let xx=this.minx;xx<=this.maxx;xx++)
{
if (xx === 0 && yy === 0)
{
str += "X";
}
else
{
switch(this.get(xx, yy))
{
case Field.Empty: str += " "; break;
case Field.POI: str += " "; break;
case Field.Target: str += "@"; break;
case Field.Unknown: str += " "; break;
case Field.Wall: str += "#"; break;
case Field.Path: str += "."; break;
}
}
}
str += "\n";
}
return str;
}
}
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,734 @@
namespace AdventOfCode2019_15_2
{
const DAY = 15;
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 wm = new WorldMap();
let rnr = new Interpreter(code, []);
let d = Direction.North;
await AdventOfCode.outputIntermed(wm.dump());
for(;;)
{
if (wm.isfree(move_pos(wm.position, turn_right(d))))
{
d = turn_right(d);
}
rnr.inputqueue.push(d);
rnr.autoRun();
if (rnr.is_halted) throw "halted";
if (rnr.output.length !== 1) throw "out";
let nx = wm.position[0];
let ny = wm.position[1];
if (d === Direction.North) ny--;
if (d === Direction.East) nx++;
if (d === Direction.South) ny++;
if (d === Direction.West) nx--;
if (rnr.output[0] === 0)
{
wm.set_wall(nx, ny);
d = turn_left(d);
}
else if (rnr.output[0] === 1)
{
wm.set_empty(nx, ny);
wm.position = [nx, ny];
}
else if (rnr.output[0] === 2)
{
wm.set_target(nx, ny);
wm.position = [nx, ny];
}
rnr.output = [];
await AdventOfCode.outputIntermed(wm.dump());
if (wm.unknowns.length === 0) break;
}
await AdventOfCode.outputIntermed(wm.dump());
const time = await wm.calcFillTime();
AdventOfCode.output(DAY, PROBLEM, time.toString());
}
function turn_right(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 "d";
}
function turn_left(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 "d";
}
function move_pos(p: [number, number], d: Direction): [number, number]
{
let nx = p[0];
let ny = p[1];
if (d === Direction.North) ny--;
if (d === Direction.East) nx++;
if (d === Direction.South) ny++;476
if (d === Direction.West) nx--;
return [nx, ny];
}
enum Direction
{
North = 1,
South = 2,
West = 3,
East = 4,
}
enum Field
{
Unknown = 0,
Wall = 1,
Empty = 2,
POI = 3, // possible new way to go, but unknown
Target = 4,
Path = 5,
}
class WorldMap
{
world: { [_:number]: Field } = {};
unknowns: [number, number][] = [];
position: [number, number] = [0, 0];
minx: number = 0;
miny: number = 0;
maxx: number = 0;
maxy: number = 0;
target_pos: [number,number] = [NaN, NaN];
distance_map: { [_:number]: number } = {};
constructor()
{
this.set_empty(0, 0);
}
async calcFillTime(): Promise<number>
{
let oxy_map: { [_:number]: boolean } = {};
let next: [number, number][] = [];
next.push([this.target_pos[0], this.target_pos[1]]);
let counter = -1;
for(;;)
{
let ls = Object.assign([], next);
next = [];
let updates = 0;
for(let pos of ls)
{
const x = pos[0];
const y = pos[1];
const i = (y*10000000 + x);
if (i in oxy_map) continue;
oxy_map[i] = true;
updates++;
if (this.isfree([x-1, y])) next.push([x-1, y]);
if (this.isfree([x+1, y])) next.push([x+1, y]);
if (this.isfree([x, y-1])) next.push([x, y-1]);
if (this.isfree([x, y+1])) next.push([x, y+1]);
}
await AdventOfCode.outputIntermed(this.dump4(oxy_map));
if (updates === 0) return counter;
counter++;
}
}
async calcDistance(): Promise<number>
{
this.position = [0,0];
let distance_map: { [_:number]: number } = {};
distance_map[0] = 0;
let updates: [number, number][] = [];
updates.unshift([-1,0]);
updates.unshift([+1,0]);
updates.unshift([0,+1]);
updates.unshift([0,-1]);
while(updates.length > 0)
{
const pos = updates.pop();
if (pos === undefined) throw "undef";
if (!this.isfree(pos)) continue;
const x = pos[0];
const y = pos[1];
const i = (y*10000000 + x);
const i_n = ((y-1)*10000000 + (x));
const i_e = ((y)*10000000 + (x+1));
const i_s = ((y+1)*10000000 + (x));
const i_w = ((y)*10000000 + (x-1));
const d_curr = (i in distance_map) ? distance_map[i] : Number.MAX_SAFE_INTEGER;
const d_n = (i_n in distance_map) ? distance_map[i_n] : Number.MAX_SAFE_INTEGER;
const d_e = (i_e in distance_map) ? distance_map[i_e] : Number.MAX_SAFE_INTEGER;
const d_s = (i_s in distance_map) ? distance_map[i_s] : Number.MAX_SAFE_INTEGER;
const d_w = (i_w in distance_map) ? distance_map[i_w] : Number.MAX_SAFE_INTEGER;
let d_new = Math.min(d_n, d_e, d_s, d_w);
if (d_new !== Number.MAX_SAFE_INTEGER) d_new++;
await AdventOfCode.outputIntermed(this.dump2(distance_map, updates));
if (d_curr <= d_new) continue;
distance_map[i] = d_new;
updates.unshift([x+1, y]);
updates.unshift([x-1, y]);
updates.unshift([x, y+1]);
updates.unshift([x, y-1]);
}
await AdventOfCode.outputIntermed(this.dump2(distance_map, updates));
let tpos = this.target_pos;
for(;;)
{
const x = tpos[0];
const y = tpos[1];
const i = (y*10000000 + x);
const d = distance_map[i];
const i_n = ((y-1)*10000000 + (x));
const i_e = ((y)*10000000 + (x+1));
const i_s = ((y+1)*10000000 + (x));
const i_w = ((y)*10000000 + (x-1));
const d_n = (i_n in distance_map) ? distance_map[i_n] : Number.MAX_SAFE_INTEGER;
const d_e = (i_e in distance_map) ? distance_map[i_e] : Number.MAX_SAFE_INTEGER;
const d_s = (i_s in distance_map) ? distance_map[i_s] : Number.MAX_SAFE_INTEGER;
const d_w = (i_w in distance_map) ? distance_map[i_w] : Number.MAX_SAFE_INTEGER;
if (d === 1) break;
if (d_n+1 === d)
{
tpos = [tpos[0], tpos[1]-1];
this.set(tpos[0], tpos[1], Field.Path);
}
else if (d_e+1 === d)
{
tpos = [tpos[0]+1, tpos[1]];
this.set(tpos[0], tpos[1], Field.Path);
}
else if (d_s+1 === d)
{
tpos = [tpos[0], tpos[1]+1];
this.set(tpos[0], tpos[1], Field.Path);
}
else if (d_w+1 === d)
{
tpos = [tpos[0]-1, tpos[1]];
this.set(tpos[0], tpos[1], Field.Path);
}
else throw "";
}
return distance_map[(this.target_pos[1]*10000000 + this.target_pos[0])];
}
isfree(p: [number, number]): boolean
{
const f = this.get(p[0], p[1]);
return (f !== Field.Wall) && (f !== Field.Unknown);
}
get(x: number, y: number): Field
{
const i = (y*10000000 + x);
if (!(i in this.world)) return Field.Unknown;
return this.world[i];
}
set(x: number, y: number, f: Field)
{
const i = (y*10000000 + x);
if (this.world[i] === Field.POI) this.unknowns = this.unknowns.filter(p => p[0] !== x || p[1] !== y);
this.world[i] = f;
if (f === Field.POI) this.unknowns.push([x, y]);
if (f === Field.Target) this.target_pos = [x, y];
this.minx = Math.min(this.minx, x);
this.maxx = Math.max(this.maxx, x);
this.miny = Math.min(this.miny, y);
this.maxy = Math.max(this.maxy, y);
}
set_empty(x: number, y: number)
{
this.set(x, y, Field.Empty);
if (this.get(x-1, y) === Field.Unknown) this.set(x-1, y, Field.POI);
if (this.get(x+1, y) === Field.Unknown) this.set(x+1, y, Field.POI);
if (this.get(x, y-1) === Field.Unknown) this.set(x, y-1, Field.POI);
if (this.get(x, y+1) === Field.Unknown) this.set(x, y+1, Field.POI);
}
set_target(x: number, y: number)
{
this.set(x, y, Field.Target);
if (this.get(x-1, y) === Field.Unknown) this.set(x-1, y, Field.POI);
if (this.get(x+1, y) === Field.Unknown) this.set(x+1, y, Field.POI);
if (this.get(x, y-1) === Field.Unknown) this.set(x, y-1, Field.POI);
if (this.get(x, y+1) === Field.Unknown) this.set(x, y+1, Field.POI);
}
set_wall(x: number, y: number)
{
this.set(x, y, Field.Wall);
}
dump(): string
{
let str = "";
for(let yy=this.miny;yy<=this.maxy;yy++)
{
for(let xx=this.minx;xx<=this.maxx;xx++)
{
if (xx === this.position[0] && yy === this.position[1])
{
str += "X";
}
else if (xx === 0 && yy === 0)
{
str += "0";
}
else
{
switch(this.get(xx, yy))
{
case Field.Empty: str += "."; break;
case Field.POI: str += "?"; break;
case Field.Target: str += "@"; break;
case Field.Unknown: str += " "; break;
case Field.Wall: str += "#"; break;
case Field.Path: str += "="; break;
}
}
}
str += "\n";
}
return str;
}
dump2(distance_map: { [_:number]: number }, updates: [number, number][]): string
{
let str = "";
for(let yy=this.miny;yy<=this.maxy;yy++)
{
for(let xx=this.minx;xx<=this.maxx;xx++)
{
const i = (yy*10000000 + xx);
if (this.get(xx, yy) === Field.Wall)
{
str += "#";
}
else if (updates.filter(u => u[0] === xx && u[1] === yy).length > 0)
{
str += "@";
}
else if (i in distance_map)
{
str += "+";
}
else
{
str += ".";
}
}
str += "\n";
}
return str;
}
dump3(): string
{
let str = "";
for(let yy=this.miny;yy<=this.maxy;yy++)
{
for(let xx=this.minx;xx<=this.maxx;xx++)
{
if (xx === 0 && yy === 0)
{
str += "X";
}
else
{
switch(this.get(xx, yy))
{
case Field.Empty: str += " "; break;
case Field.POI: str += " "; break;
case Field.Target: str += "@"; break;
case Field.Unknown: str += " "; break;
case Field.Wall: str += "#"; break;
case Field.Path: str += "."; break;
}
}
}
str += "\n";
}
return str;
}
dump4(oxy_map: { [_:number]: boolean }): string
{
let str = "";
for(let yy=this.miny;yy<=this.maxy;yy++)
{
for(let xx=this.minx;xx<=this.maxx;xx++)
{
const i = (yy*10000000 + xx);
if (this.get(xx, yy) === Field.Wall)
{
str += "#";
}
else if (xx === this.target_pos[0] && yy === this.target_pos[1])
{
str += "X";
}
else if (i in oxy_map)
{
str += "@";
}
else
{
str += " ";
}
}
str += "\n";
}
return str;
}
}
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,87 @@
--- Day 16: Flawed Frequency Transmission ---
You're 3/4ths of the way through the gas giants. Not only do roundtrip signals to Earth take five hours, but the signal quality is quite bad as well. You can clean up the signal with the Flawed Frequency Transmission algorithm, or FFT.
As input, FFT takes a list of numbers. In the signal you received (your puzzle input), each number is a single digit: data like 15243 represents the sequence 1, 5, 2, 4, 3.
FFT operates in repeated phases. In each phase, a new list is constructed with the same length as the input list. This new list is also used as the input for the next phase.
Each element in the new list is built by multiplying every value in the input list by a value in a repeating pattern and then adding up the results. So, if the input list were 9, 8, 7, 6, 5 and the pattern for a given element were 1, 2, 3, the result would be 9*1 + 8*2 + 7*3 + 6*1 + 5*2 (with each input element on the left and each value in the repeating pattern on the right of each multiplication). Then, only the ones digit is kept: 38 becomes 8, -17 becomes 7, and so on.
While each element in the output array uses all of the same input array elements, the actual repeating pattern to use depends on which output element is being calculated. The base pattern is 0, 1, 0, -1. Then, repeat each value in the pattern a number of times equal to the position in the output list being considered. Repeat once for the first element, twice for the second element, three times for the third element, and so on. So, if the third element of the output list is being calculated, repeating the values would produce: 0, 0, 0, 1, 1, 1, 0, 0, 0, -1, -1, -1.
When applying the pattern, skip the very first value exactly once. (In other words, offset the whole pattern left by one.) So, for the second element of the output list, the actual pattern used would be: 0, 1, 1, 0, 0, -1, -1, 0, 0, 1, 1, 0, 0, -1, -1, ....
After using this process to calculate each element of the output list, the phase is complete, and the output list of this phase is used as the new input list for the next phase, if any.
Given the input signal 12345678, below are four phases of FFT. Within each phase, each output digit is calculated on a single line with the result at the far right; each multiplication operation shows the input digit on the left and the pattern value on the right:
Input signal: 12345678
1*1 + 2*0 + 3*-1 + 4*0 + 5*1 + 6*0 + 7*-1 + 8*0 = 4
1*0 + 2*1 + 3*1 + 4*0 + 5*0 + 6*-1 + 7*-1 + 8*0 = 8
1*0 + 2*0 + 3*1 + 4*1 + 5*1 + 6*0 + 7*0 + 8*0 = 2
1*0 + 2*0 + 3*0 + 4*1 + 5*1 + 6*1 + 7*1 + 8*0 = 2
1*0 + 2*0 + 3*0 + 4*0 + 5*1 + 6*1 + 7*1 + 8*1 = 6
1*0 + 2*0 + 3*0 + 4*0 + 5*0 + 6*1 + 7*1 + 8*1 = 1
1*0 + 2*0 + 3*0 + 4*0 + 5*0 + 6*0 + 7*1 + 8*1 = 5
1*0 + 2*0 + 3*0 + 4*0 + 5*0 + 6*0 + 7*0 + 8*1 = 8
After 1 phase: 48226158
4*1 + 8*0 + 2*-1 + 2*0 + 6*1 + 1*0 + 5*-1 + 8*0 = 3
4*0 + 8*1 + 2*1 + 2*0 + 6*0 + 1*-1 + 5*-1 + 8*0 = 4
4*0 + 8*0 + 2*1 + 2*1 + 6*1 + 1*0 + 5*0 + 8*0 = 0
4*0 + 8*0 + 2*0 + 2*1 + 6*1 + 1*1 + 5*1 + 8*0 = 4
4*0 + 8*0 + 2*0 + 2*0 + 6*1 + 1*1 + 5*1 + 8*1 = 0
4*0 + 8*0 + 2*0 + 2*0 + 6*0 + 1*1 + 5*1 + 8*1 = 4
4*0 + 8*0 + 2*0 + 2*0 + 6*0 + 1*0 + 5*1 + 8*1 = 3
4*0 + 8*0 + 2*0 + 2*0 + 6*0 + 1*0 + 5*0 + 8*1 = 8
After 2 phases: 34040438
3*1 + 4*0 + 0*-1 + 4*0 + 0*1 + 4*0 + 3*-1 + 8*0 = 0
3*0 + 4*1 + 0*1 + 4*0 + 0*0 + 4*-1 + 3*-1 + 8*0 = 3
3*0 + 4*0 + 0*1 + 4*1 + 0*1 + 4*0 + 3*0 + 8*0 = 4
3*0 + 4*0 + 0*0 + 4*1 + 0*1 + 4*1 + 3*1 + 8*0 = 1
3*0 + 4*0 + 0*0 + 4*0 + 0*1 + 4*1 + 3*1 + 8*1 = 5
3*0 + 4*0 + 0*0 + 4*0 + 0*0 + 4*1 + 3*1 + 8*1 = 5
3*0 + 4*0 + 0*0 + 4*0 + 0*0 + 4*0 + 3*1 + 8*1 = 1
3*0 + 4*0 + 0*0 + 4*0 + 0*0 + 4*0 + 3*0 + 8*1 = 8
After 3 phases: 03415518
0*1 + 3*0 + 4*-1 + 1*0 + 5*1 + 5*0 + 1*-1 + 8*0 = 0
0*0 + 3*1 + 4*1 + 1*0 + 5*0 + 5*-1 + 1*-1 + 8*0 = 1
0*0 + 3*0 + 4*1 + 1*1 + 5*1 + 5*0 + 1*0 + 8*0 = 0
0*0 + 3*0 + 4*0 + 1*1 + 5*1 + 5*1 + 1*1 + 8*0 = 2
0*0 + 3*0 + 4*0 + 1*0 + 5*1 + 5*1 + 1*1 + 8*1 = 9
0*0 + 3*0 + 4*0 + 1*0 + 5*0 + 5*1 + 1*1 + 8*1 = 4
0*0 + 3*0 + 4*0 + 1*0 + 5*0 + 5*0 + 1*1 + 8*1 = 9
0*0 + 3*0 + 4*0 + 1*0 + 5*0 + 5*0 + 1*0 + 8*1 = 8
After 4 phases: 01029498
Here are the first eight digits of the final output list after 100 phases for some larger inputs:
80871224585914546619083218645595 becomes 24176176.
19617804207202209144916044189917 becomes 73745418.
69317163492948606335995924319873 becomes 52432133.
After 100 phases of FFT, what are the first eight digits in the final output list?
--- Part Two ---
Now that your FFT is working, you can decode the real signal.
The real signal is your puzzle input repeated 10000 times. Treat this new signal as a single input list. Patterns are still calculated as before, and 100 phases of FFT are still applied.
The first seven digits of your initial input signal also represent the message offset. The message offset is the location of the eight-digit message in the final output list. Specifically, the message offset indicates the number of digits to skip before reading the eight-digit message. For example, if the first seven digits of your initial input signal were 1234567, the eight-digit message would be the eight digits after skipping 1,234,567 digits of the final output list. Or, if the message offset were 7 and your final output list were 98765432109876543210, the eight-digit message would be 21098765. (Of course, your real message offset will be a seven-digit number, not a one-digit number like 7.)
Here is the eight-digit message in the final output list after 100 phases. The message offset given in each input has been highlighted. (Note that the inputs given below are repeated 10000 times to find the actual starting input lists.)
03036732577212944063491565474664 becomes 84462026.
02935109699940807407585447034323 becomes 78725270.
03081770884921959731165446850517 becomes 53553731.
After repeating your input signal 10000 times and running 100 phases of FFT, what is the eight-digit message embedded in the final output list?

View File

@ -0,0 +1 @@
59756772370948995765943195844952640015210703313486295362653878290009098923609769261473534009395188480864325959786470084762607666312503091505466258796062230652769633818282653497853018108281567627899722548602257463608530331299936274116326038606007040084159138769832784921878333830514041948066594667152593945159170816779820264758715101494739244533095696039336070510975612190417391067896410262310835830006544632083421447385542256916141256383813360662952845638955872442636455511906111157861890394133454959320174572270568292972621253460895625862616228998147301670850340831993043617316938748361984714845874270986989103792418940945322846146634931990046966552

View File

@ -0,0 +1,47 @@
namespace AdventOfCode2019_16_1
{
const DAY = 16;
const PROBLEM = 1;
export async function run()
{
let input = await AdventOfCode.getInput(DAY);
if (input == null) return;
const message = input.trim().split("").map(p => parseInt(p));
let step = message;
for (let i=0; i < 100; i++) step = FFT(step);
AdventOfCode.output(DAY, PROBLEM, step.slice(0, 8).map(p=>p.toString()).reduce((a,b)=>a+b));
}
function FFT(msg: number[])
{
let result = [];
for (let i=0; i < msg.length; i++)
{
let sum = 0;
let str = "";
for (let i2=0; i2 < msg.length; i2++)
{
let factor = [0, 1, 0, -1][Math.floor(((i2+1) % ((i+1)*4)) / (i+1))];
str += msg[i2]+"*"+factor+" + ";
sum += (msg[i2]*factor);
}
str += " = " + sum;
sum = Math.abs(sum) % 10;
str += " = " + sum;
result.push(sum);
AdventOfCode.outputConsole(str);
}
AdventOfCode.outputConsole("[" + result.map(p=>p.toString()).reduce((a,b)=>a+","+b)+"]");
return result;
}
}

View File

@ -0,0 +1,46 @@
namespace AdventOfCode2019_16_2
{
const DAY = 16;
const PROBLEM = 2;
export async function run()
{
let input = await AdventOfCode.getInput(DAY);
if (input == null) return;
const message = input.trim().split("").map(p => parseInt(p));
let offset = parseInt(message.slice(0,7).map(p=>p.toString()).reduce((a,b)=>a+b));
let off_offset = Math.floor(offset / message.length) * message.length;
let step = [];
for(let i=off_offset/message.length; i<10_000; i++) for(const d of message) step.push(d);
step = step.slice(offset - off_offset);
for (let i=0; i < 100; i++)
{
step = StupidBigFFT(step);
AdventOfCode.outputConsole(`[${i}] ` + step.slice(0, 8).map(p=>p.toString()).reduce((a,b)=>a+b));
await AdventOfCode.sleep(0);
}
AdventOfCode.output(DAY, PROBLEM, step.slice(0, 8).map(p=>p.toString()).reduce((a,b)=>a+b));
}
function StupidBigFFT(msg: number[])
{
let result = new Array(msg.length);
let sum = 0;
for (let i=msg.length-1; i >= 0; i--)
{
sum = (sum + msg[i]) % 10;
result[i] = sum;
}
return result;
}
}

View File

@ -41,5 +41,12 @@ return
['day' => 7, 'parts' => 2, 'title' => 'Amplification Circuit', 'language' => 'ts', 'solutions' => ['844468', '4215746'] ],
['day' => 8, 'parts' => 2, 'title' => 'Space Image Format', 'language' => 'ts', 'solutions' => ['2286', 'CJZLP'] ],
['day' => 9, 'parts' => 2, 'title' => 'Sensor Boost', 'language' => 'ts', 'solutions' => ['2738720997', '50894'] ],
['day' => 10, 'parts' => 2, 'title' => 'Monitoring Station', 'language' => 'ts', 'solutions' => ['344', '2732'] ],
['day' => 11, 'parts' => 2, 'title' => 'Space Police', 'language' => 'ts', 'solutions' => ['2021', 'LBJHEKLH'] ],
['day' => 12, 'parts' => 2, 'title' => 'The N-Body Problem', 'language' => 'ts', 'solutions' => ['9999', '282399002133976'] ],
['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'] ],
],
];