Skip to content

Commit 6e73429

Browse files
committed
Added 2017 day 21
1 parent c92b158 commit 6e73429

File tree

3 files changed

+156
-0
lines changed

3 files changed

+156
-0
lines changed

2017/21/code.mjs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import { delay, Console } from "../../utility.mjs";
2+
3+
export default class {
4+
/**
5+
* @param {Console} solConsole Solution console.
6+
* @param {HTMLElement} visContainer Visualization container.
7+
*/
8+
constructor(solConsole, visContainer) {
9+
this.isSolving = false;
10+
this.isStopping = false;
11+
this.solConsole = typeof solConsole !== "undefined" ? solConsole : new Console();
12+
this.visContainer = visContainer;
13+
}
14+
15+
/**
16+
* Parses the puzzle input.
17+
* @param {string} input Puzzle input.
18+
* @returns {EnhancementRule[]} Enhancement rules.
19+
*/
20+
parse(input) {
21+
let consoleLine = this.solConsole.addLine("Parsing...");
22+
23+
let enhancementRules = input.split(/\r?\n/).map((line, index) => {
24+
let match = line.match(/^([\.\/#]+) => ([\.\/#]+)$/);
25+
if (match == null)
26+
throw new Error(`Invalid data in line ${index + 1}`);
27+
28+
let from = match[1].split("/").map(e => e.split("").map(e => e == "#" ? 1 : 0));
29+
let to = match[2].split("/").map(e => e.split("").map(e => e == "#" ? 1 : 0));
30+
31+
let flip = shape => shape.map((e1, y) => shape[0].map((e2, x) => shape[y][shape[0].length - 1 - x]));
32+
let rotate = shape => shape[0].map((e1, x) => shape.map((e2, y) => shape[shape.length - 1 - y][x]));
33+
34+
if ((from.length == 2 && from.every(e => e.length == 2) && to.length == 3 && to.every(e => e.length == 3))
35+
|| (from.length == 3 && from.every(e => e.length == 3) && to.length == 4 && to.every(e => e.length == 4))) {
36+
return new EnhancementRule([from, rotate(from), rotate(rotate(from)), rotate(rotate(rotate(from))),
37+
flip(from), rotate(flip(from)), rotate(rotate(flip(from))), rotate(rotate(rotate(flip(from))))], to)
38+
}
39+
else
40+
throw new Error(`Invalid data in line ${index + 1}`);
41+
});
42+
43+
consoleLine.innerHTML += " done.";
44+
return enhancementRules;
45+
}
46+
47+
/**
48+
* Finds the number of pixels that stay on after the specified number of iterations.
49+
* @param {number} part Puzzle part.
50+
* @param {string} input Puzzle input.
51+
* @param {boolean} visualization Enable visualization.
52+
* @returns {number} Number of pixels that stay on after the specified number of iterations.
53+
*/
54+
async solve(part, input, visualization) {
55+
try {
56+
this.isSolving = true;
57+
58+
let enhancementRules = this.parse(input);
59+
let image = [[0, 1, 0], [0, 0, 1], [1, 1, 1]];
60+
let numberOfIterations = enhancementRules.length < 5 ? 2 : (part == 1 ? 5 : 18);
61+
62+
let visConsole = new Console();
63+
if (visualization)
64+
this.visContainer.append(visConsole.container);
65+
66+
for (let i = 0; i < numberOfIterations; i++) {
67+
let imageSize = image.length;
68+
let fromBlockSize = imageSize % 2 == 0 ? 2 : 3;
69+
let toBlockSize = imageSize % 2 == 0 ? 3 : 4;
70+
let newImageSize = imageSize % 2 == 0 ? imageSize / 2 * 3 : imageSize / 3 * 4;
71+
let newImage = new Array(newImageSize).fill(null).map(e => e = new Array(newImageSize).fill(0));
72+
73+
for (let x = 0; x < imageSize / fromBlockSize; x++) {
74+
for (let y = 0; y < imageSize / fromBlockSize; y++) {
75+
for (let enhancementRuleIndex = 0, enhancementRuleApplied = false; enhancementRuleIndex < enhancementRules.length && !enhancementRuleApplied; enhancementRuleIndex++) {
76+
let enhancementRule = enhancementRules[enhancementRuleIndex]
77+
let to = enhancementRule.to;
78+
if (to.length == toBlockSize) {
79+
for (let fromIndex = 0; fromIndex < enhancementRule.from.length && !enhancementRuleApplied; fromIndex++) {
80+
let from = enhancementRule.from[fromIndex];
81+
if (from.length == fromBlockSize) {
82+
let match = true;
83+
for (let ix = 0; ix < fromBlockSize && match; ix++) {
84+
for (let iy = 0; iy < fromBlockSize && match; iy++) {
85+
if (from[iy][ix] != image[y * fromBlockSize + iy][x * fromBlockSize + ix])
86+
match = false;
87+
}
88+
}
89+
if (match) {
90+
for (let ix = 0; ix < toBlockSize; ix++) {
91+
for (let iy = 0; iy < toBlockSize; iy++)
92+
newImage[y * toBlockSize + iy][x * toBlockSize + ix] = to[iy][ix];
93+
}
94+
enhancementRuleApplied = true;
95+
}
96+
}
97+
}
98+
}
99+
}
100+
}
101+
}
102+
103+
image = newImage;
104+
105+
if (visualization) {
106+
let numberOfOnPixels = image.reduce((acc, e) => acc + e.reduce((acc, e) => acc + e, 0), 0);
107+
visConsole.addLine(`Iteration ${i + 1}: ${numberOfOnPixels} pixel${numberOfOnPixels == 1 ? "" : "s"} are on.`);
108+
}
109+
}
110+
111+
return image.reduce((acc, e) => acc + e.reduce((acc, e) => acc + e, 0), 0);
112+
}
113+
114+
finally {
115+
this.isSolving = false;
116+
}
117+
}
118+
119+
/**
120+
* Stops solving the puzzle.
121+
*/
122+
async stopSolving() {
123+
this.isStopping = true;
124+
while (this.isSolving)
125+
await(delay(10));
126+
this.isStopping = false;
127+
}
128+
}
129+
130+
/**
131+
* Puzzle particle class.
132+
*/
133+
class EnhancementRule {
134+
/**
135+
* @param {number[][][]} from From.
136+
* @param {number[][]} to To.
137+
*/
138+
constructor(from, to) {
139+
/**
140+
* From.
141+
* @type {number[][]}
142+
*/
143+
this.from = from;
144+
/**
145+
* To.
146+
* @type {number[][]}
147+
*/
148+
this.to = to;
149+
}
150+
}

2017/21/testInput.txt

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

tree.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,10 @@ export const years = [
478478
{
479479
name: "Day 20: Particle Swarm", path: "./2017/20", taskUrl: "https://adventofcode.com/2017/day/20",
480480
answers: {part1: 3, part2: 1}
481+
},
482+
{
483+
name: "Day 21: Fractal Art", path: "./2017/21", taskUrl: "https://adventofcode.com/2017/day/21",
484+
answers: {part1: 12}
481485
}
482486
]
483487
},

0 commit comments

Comments
 (0)