Skip to content

Commit a85a851

Browse files
committed
Added 2017 day 16
1 parent 09345c0 commit a85a851

3 files changed

Lines changed: 158 additions & 0 deletions

File tree

2017/16/code.mjs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
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 {DanceMove[]} Dance moves.
19+
*/
20+
parse(input) {
21+
let consoleLine = this.solConsole.addLine("Parsing...");
22+
23+
let danceMoves = input.trim().split(",").map((line, index) => {
24+
let match;
25+
if ((match = line.match(/^s(\d+)$/)) != null)
26+
return new DanceMove("s", [parseInt(match[1])]);
27+
if ((match = line.match(/^x(\d+)\/(\d+)$/)) != null)
28+
return new DanceMove("x", [parseInt(match[1]), parseInt(match[2])]);
29+
if ((match = line.match(/^p([a-z])\/([a-z])$/)) != null)
30+
return new DanceMove("p", [match[1], match[2]]);
31+
throw new Error(`Invalid move ${index + 1}`);
32+
});
33+
34+
consoleLine.innerHTML += " done.";
35+
return danceMoves;
36+
}
37+
38+
39+
/**
40+
* Finds the order of the programs after the specified number of dances.
41+
* @param {number} part Puzzle part.
42+
* @param {string} input Puzzle input.
43+
* @param {boolean} visualization Enable visualization.
44+
* @returns {string} Order of the programs after the specified number of dances.
45+
*/
46+
async solve(part, input, visualization) {
47+
try {
48+
this.isSolving = true;
49+
50+
let danceMoves = this.parse(input);
51+
let programs = new Array(danceMoves.length > 10 ? 16 : 5).fill(null).map((e, i) => String.fromCharCode("a".charCodeAt(0) + i));
52+
let numberOfDances = part == 1 ? 1 : (danceMoves.length > 10 ? 1000000000 : 2);
53+
54+
let solConsoleLine;
55+
let visConsole = new Console();
56+
if (visualization) {
57+
solConsoleLine = this.solConsole.addLine();
58+
this.visContainer.append(visConsole.container);
59+
visConsole.addLine(programs.join(""));
60+
}
61+
62+
let programsDanceNumberMap = new Map();
63+
let cycleFound = false;
64+
for (let danceNumber = 1; danceNumber <= numberOfDances; danceNumber++) {
65+
for (let danceMoveIndex = 0; danceMoveIndex < danceMoves.length; danceMoveIndex++) {
66+
if (this.isStopping)
67+
return;
68+
69+
let danceMove = danceMoves[danceMoveIndex];
70+
if (danceMove.move == "s") {
71+
let n = danceMove.parameters[0] % programs.length;
72+
programs = [...programs.slice(programs.length - n), ...programs.slice(0, programs.length - n)];
73+
}
74+
else if (danceMove.move == "x") {
75+
let i1 = danceMove.parameters[0], i2 = danceMove.parameters[1];
76+
let p1 = programs[i1], p2 = programs[i2];
77+
programs[i1] = p2;
78+
programs[i2] = p1;
79+
}
80+
else if (danceMove.move == "p") {
81+
let p1 = danceMove.parameters[0], p2 = danceMove.parameters[1];
82+
let i1 = programs.indexOf(p1), i2 = programs.indexOf(p2);
83+
programs[i1] = p2;
84+
programs[i2] = p1;
85+
}
86+
87+
if (visualization && (danceMoveIndex == danceMoves.length - 1 || (danceMoveIndex + 1) % 1000 == 0)) {
88+
solConsoleLine.innerHTML = `Dance ${danceNumber}, move ${danceMoveIndex + 1}/${danceMoves.length}.`;
89+
visConsole.lines[0].innerHTML = programs.join("");
90+
await delay(1);
91+
}
92+
}
93+
94+
// After each dance check if the same program order has already been encountered before
95+
if (!cycleFound) {
96+
let programString = programs.join("");
97+
let previousDanceNumber = programsDanceNumberMap.get(programString);
98+
99+
if (previousDanceNumber != undefined) {
100+
if (visualization)
101+
this.solConsole.addLine(`Program order after dance ${danceNumber} is the same as after dance ${previousDanceNumber}.`);
102+
danceNumber = previousDanceNumber + Math.floor((numberOfDances - previousDanceNumber) / (danceNumber - previousDanceNumber)) * (danceNumber - previousDanceNumber);
103+
if (visualization) {
104+
this.solConsole.addLine(`It will also be the same after dance ${danceNumber}.`);
105+
if (danceNumber < numberOfDances)
106+
solConsoleLine = this.solConsole.addLine();
107+
}
108+
cycleFound = true;
109+
}
110+
programsDanceNumberMap.set(programString, danceNumber);
111+
}
112+
}
113+
114+
return programs.join("");
115+
}
116+
117+
finally {
118+
this.isSolving = false;
119+
}
120+
}
121+
122+
/**
123+
* Stops solving the puzzle.
124+
*/
125+
async stopSolving() {
126+
this.isStopping = true;
127+
while (this.isSolving)
128+
await(delay(10));
129+
this.isStopping = false;
130+
}
131+
}
132+
133+
/**
134+
* Puzzle dance move class.
135+
*/
136+
class DanceMove {
137+
/**
138+
* @param {string} move Move.
139+
* @param {number|string[]} parameters Parameters.
140+
*/
141+
constructor(move, parameters) {
142+
/**
143+
* Move.
144+
* @type {string}
145+
*/
146+
this.move = move;
147+
/**
148+
* Parameters.
149+
* @type {number|string[]}
150+
*/
151+
this.parameters = parameters;
152+
}
153+
}

2017/16/testInput.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
s1,x3/4,pe/b

tree.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,10 @@ export const years = [
458458
{
459459
name: "Day 15: Dueling Generators", path: "./2017/15", taskUrl: "https://adventofcode.com/2017/day/15",
460460
answers: {part1: 1, part2: 1}
461+
},
462+
{
463+
name: "Day 16: Permutation Promenade", path: "./2017/16", taskUrl: "https://adventofcode.com/2017/day/16",
464+
answers: {part1: "baedc", part2: "ceadb"}
461465
}
462466
]
463467
},

0 commit comments

Comments
 (0)