Skip to content

Commit 64467f1

Browse files
committed
Added 2017 day 22
1 parent 6e73429 commit 64467f1

3 files changed

Lines changed: 200 additions & 0 deletions

File tree

2017/22/code.mjs

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import { delay, Console, Vector2D, PixelMap } from "../../utility.mjs";
2+
3+
const weakenedColorIndex = 1;
4+
const weakenedColor = "#999999";
5+
const infectedColorIndex = 2;
6+
const infectedColor = "#ff0000";
7+
const flaggedColorIndex = 3;
8+
const flaggedColor = "#00aa00";
9+
10+
export default class {
11+
/**
12+
* @param {Console} solConsole Solution console.
13+
* @param {HTMLElement} visContainer Visualization container.
14+
*/
15+
constructor(solConsole, visContainer) {
16+
this.isSolving = false;
17+
this.isStopping = false;
18+
this.solConsole = typeof solConsole !== "undefined" ? solConsole : new Console();
19+
this.visContainer = visContainer;
20+
}
21+
22+
/**
23+
* Parses the puzzle input.
24+
* @param {string} input Puzzle input.
25+
* @returns {Vector2D[]} Infected nodes.
26+
*/
27+
parse(input) {
28+
let consoleLine = this.solConsole.addLine("Parsing...");
29+
30+
let letters = {};
31+
let map = input.split(/\r?\n/).filter(e => e != "").map((line, index, lines) => {
32+
if (line.length != lines[0].length)
33+
throw new Error(`Invalid length of line ${index + 1}`);
34+
if (!/^[#\.]+$/.test(line))
35+
throw new Error(`Invalid data in line ${index + 1}`);
36+
return line.split("");
37+
});
38+
39+
let mapWidth = map[0].length, mapHeight = map.length;
40+
41+
if (mapWidth % 2 == 0 || mapHeight % 2 == 0)
42+
throw new Error("Invalid map size");
43+
44+
let infectedNodes = [];
45+
for (let x = 0; x < mapHeight; x++) {
46+
for (let y = 0; y < mapWidth; y++) {
47+
if (map[y][x] != ".")
48+
infectedNodes.push(new Vector2D(x - Math.floor(mapWidth / 2), y - Math.floor(mapHeight / 2)));
49+
}
50+
}
51+
52+
consoleLine.innerHTML += " done.";
53+
return infectedNodes;
54+
}
55+
56+
/**
57+
* Finds the number of infection bursts.
58+
* @param {number} part Puzzle part.
59+
* @param {string} input Puzzle input.
60+
* @param {boolean} visualization Enable visualization.
61+
* @returns {number} Number of infection bursts.
62+
*/
63+
async solve(part, input, visualization) {
64+
try {
65+
this.isSolving = true;
66+
67+
let infectedNodes = this.parse(input);
68+
let numberOfSteps = part == 1 ? 10000 : 10000000;
69+
70+
let directions = [new Vector2D(0, -1), new Vector2D(1, 0), new Vector2D(0, 1), new Vector2D(-1, 0)];
71+
let position = new Vector2D(0, 0), directionIndex = 0;
72+
// Nodes: 1 - weakened, 2 - infected, 3 - flagged
73+
let nodeMap = infectedNodes.reduce((acc, e) => acc.set(e.y * numberOfSteps * 2 + e.x, 2), new Map());
74+
let numberOfInfectionBursts = 0;
75+
76+
let minCoordinates = new Vector2D(Math.min(0, ...infectedNodes.map(e => e.x)), Math.min(0, ...infectedNodes.map(e => e.y)));
77+
let maxCoordinates = new Vector2D(Math.max(0, ...infectedNodes.map(e => e.x)), Math.max(0, ...infectedNodes.map(e => e.y)));
78+
infectedNodes.forEach(e => minCoordinates)
79+
for (let i = 0; i < numberOfSteps; i++) {
80+
let nodeMapKey = position.y * numberOfSteps * 2 + position.x;
81+
let nodeValue = nodeMap.get(nodeMapKey);
82+
if (nodeValue == undefined) {
83+
if (part == 1) {
84+
nodeMap.set(nodeMapKey, 2);
85+
numberOfInfectionBursts++;
86+
}
87+
else
88+
nodeMap.set(nodeMapKey, 1);
89+
directionIndex = (directionIndex + 3) % directions.length;
90+
}
91+
else if (nodeValue == 1) {
92+
nodeMap.set(nodeMapKey, 2);
93+
numberOfInfectionBursts++;
94+
}
95+
else if (nodeValue == 2) {
96+
if (part == 1)
97+
nodeMap.delete(nodeMapKey);
98+
else
99+
nodeMap.set(nodeMapKey, 3);
100+
directionIndex = (directionIndex + 1) % directions.length;
101+
}
102+
else if (nodeValue == 3) {
103+
nodeMap.delete(nodeMapKey);
104+
directionIndex = (directionIndex + 2) % directions.length;
105+
}
106+
position.add(directions[directionIndex]);
107+
108+
if (visualization) {
109+
minCoordinates.x = Math.min(minCoordinates.x, position.x);
110+
minCoordinates.y = Math.min(minCoordinates.y, position.y);
111+
maxCoordinates.x = Math.max(maxCoordinates.x, position.x);
112+
maxCoordinates.y = Math.max(maxCoordinates.y, position.y);
113+
}
114+
}
115+
116+
if (visualization) {
117+
let numberOfInfectionBursts = 0;
118+
let mapWidth = maxCoordinates.x - minCoordinates.x + 1, mapHeight = maxCoordinates.y - minCoordinates.y + 1;
119+
this.solConsole.addLine(`Number of steps: ${numberOfSteps}.`);
120+
let solConsoleLine1 = this.solConsole.addLine();
121+
let solConsoleLine2 = this.solConsole.addLine();
122+
let pixelMap = new PixelMap(mapWidth, mapHeight);
123+
this.visContainer.append(pixelMap.container);
124+
pixelMap.palette[weakenedColorIndex] = weakenedColor;
125+
pixelMap.palette[infectedColorIndex] = infectedColor;
126+
pixelMap.palette[flaggedColorIndex] = flaggedColor;
127+
for (let infectedNode of infectedNodes)
128+
pixelMap.drawPixel(infectedNode.x - minCoordinates.x, infectedNode.y - minCoordinates.y, infectedColorIndex);
129+
let position = new Vector2D(-minCoordinates.x, -minCoordinates.y);
130+
directionIndex = 0;
131+
132+
for (let i = 0; i < numberOfSteps; i++) {
133+
if (this.isStopping)
134+
return;
135+
136+
let x = position.x, y = position.y;
137+
let pixel = pixelMap.image[y][x];
138+
if (pixel == 0) {
139+
if (part == 1) {
140+
pixelMap.drawPixel(x, y, infectedColorIndex);
141+
numberOfInfectionBursts++;
142+
}
143+
else
144+
pixelMap.drawPixel(x, y, weakenedColorIndex);
145+
directionIndex = (directionIndex + 3) % directions.length;
146+
}
147+
else if (pixel == weakenedColorIndex) {
148+
pixelMap.drawPixel(x, y, infectedColorIndex);
149+
numberOfInfectionBursts++;
150+
}
151+
else if (pixel == infectedColorIndex) {
152+
pixelMap.drawPixel(x, y, part == 1 ? 0 : flaggedColorIndex);
153+
directionIndex = (directionIndex + 1) % directions.length;
154+
}
155+
else if (pixel == flaggedColorIndex) {
156+
pixelMap.drawPixel(x, y, 0);
157+
directionIndex = (directionIndex + 2) % directions.length;
158+
}
159+
position.add(directions[directionIndex]);
160+
161+
if (visualization) {
162+
minCoordinates.x = Math.min(minCoordinates.x, position.x);
163+
minCoordinates.y = Math.min(minCoordinates.y, position.y);
164+
maxCoordinates.x = Math.max(maxCoordinates.x, position.x);
165+
maxCoordinates.y = Math.max(maxCoordinates.y, position.y);
166+
}
167+
168+
if ((i + 1) % (numberOfSteps / 1000) == 0) {
169+
solConsoleLine1.innerHTML = `Step: ${i + 1}.`;
170+
solConsoleLine2.innerHTML = `Infection bursts: ${numberOfInfectionBursts}.`;
171+
await delay(1);
172+
}
173+
}
174+
}
175+
176+
return numberOfInfectionBursts;
177+
}
178+
179+
finally {
180+
this.isSolving = false;
181+
}
182+
}
183+
184+
/**
185+
* Stops solving the puzzle.
186+
*/
187+
async stopSolving() {
188+
this.isStopping = true;
189+
while (this.isSolving)
190+
await(delay(10));
191+
this.isStopping = false;
192+
}
193+
}

2017/22/testInput.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
..#
2+
#..
3+
...

tree.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,10 @@ export const years = [
482482
{
483483
name: "Day 21: Fractal Art", path: "./2017/21", taskUrl: "https://adventofcode.com/2017/day/21",
484484
answers: {part1: 12}
485+
},
486+
{
487+
name: "Day 22: Sporifica Virus", path: "./2017/22", taskUrl: "https://adventofcode.com/2017/day/22",
488+
answers: {part1: 5587, part2: 2511944}
485489
}
486490
]
487491
},

0 commit comments

Comments
 (0)