Skip to content

Commit 8daa743

Browse files
committed
Added 2017 day 18
1 parent 7324af2 commit 8daa743

File tree

4 files changed

+187
-1
lines changed

4 files changed

+187
-1
lines changed

2017/17/code.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export default class {
3434
* @param {number} part Puzzle part.
3535
* @param {string} input Puzzle input.
3636
* @param {boolean} visualization Enable visualization.
37-
* @returns {string} Value after 2017 in the buffer after 2017th insert (part 1) or the value after 0 in the buffer after 50000000th insert (part 2).
37+
* @returns {number} Value after 2017 in the buffer after 2017th insert (part 1) or the value after 0 in the buffer after 50000000th insert (part 2).
3838
*/
3939
async solve(part, input, visualization) {
4040
try {

2017/18/code.mjs

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
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 {Instruction[]} Instructions.
19+
*/
20+
parse(input) {
21+
let consoleLine = this.solConsole.addLine("Parsing...");
22+
23+
let instructions = input.trim().split(/\r?\n/).map((line, index) => {
24+
let match;
25+
if ((match = line.match(/^(snd) ([a-z]|-?\d+)$/)) != null)
26+
return new Instruction(match[1], [isNaN(match[2]) ? match[2] : parseInt(match[2])]);
27+
if ((match = line.match(/^(rcv) ([a-z])$/)) != null)
28+
return new Instruction(match[1], [isNaN(match[2]) ? match[2] : parseInt(match[2])]);
29+
if ((match = line.match(/^(set|add|mul|mod) ([a-z]) ([a-z]|-?\d+)$/)) != null)
30+
return new Instruction(match[1], [isNaN(match[2]) ? match[2] : parseInt(match[2]), isNaN(match[3]) ? match[3] : parseInt(match[3])]);
31+
if ((match = line.match(/^(jgz) ([a-z]|-?\d+) ([a-z]|-?\d+)$/)) != null)
32+
return new Instruction(match[1], [isNaN(match[2]) ? match[2] : parseInt(match[2]), isNaN(match[3]) ? match[3] : parseInt(match[3])]);
33+
else
34+
throw new Error(`Invalid instruction ${index + 1} (${line})`);
35+
});
36+
37+
consoleLine.innerHTML += " done.";
38+
return instructions;
39+
}
40+
41+
/**
42+
* Finds the value of the recovered frequency (part 1) or the number of times program 1 sends a value (part 2).
43+
* @param {number} part Puzzle part.
44+
* @param {string} input Puzzle input.
45+
* @param {boolean} visualization Enable visualization.
46+
* @returns {number} Value of the recovered frequency (part 1) or the number of times program 1 sends a value (part 2).
47+
*/
48+
async solve(part, input, visualization) {
49+
try {
50+
this.isSolving = true;
51+
52+
let instructions = this.parse(input);
53+
54+
let visConsole = new Console();
55+
if (visualization)
56+
this.visContainer.append(visConsole.container);
57+
58+
let registerNames = new Set();
59+
instructions.forEach(e => {
60+
if (typeof e.operands[0] == "string")
61+
registerNames.add(e.operands[0]);
62+
if (typeof e.operands[1] == "string")
63+
registerNames.add(e.operands[1]);
64+
});
65+
registerNames = [...registerNames].sort();
66+
let numberOfPrograms = part == 1 ? 1 : 2;
67+
let memory = [];
68+
let messageQueues = [];
69+
for (let p = 0; p < numberOfPrograms; p++) {
70+
memory.push({});
71+
registerNames.forEach(e => memory[p][e] = 0);
72+
memory[p]["p"] = p;
73+
messageQueues.push([]);
74+
}
75+
76+
let soundFrequency;
77+
let numberOfMessages = messageQueues.map(e => 0);
78+
79+
for (let i = new Array(numberOfPrograms).fill(0); ; i = i.map(e => e + 1)) {
80+
if (part == 1 && i[0] >= instructions.length)
81+
throw new Error("RCV instruction is not executed");
82+
if (part == 2 && i.every((e, p) => e >= instructions.length || (instructions[e].opcode == "rcv" && messageQueues[p].length == 0))) {
83+
if (visualization) {
84+
if (i[0] >= instructions.length)
85+
visConsole.addLine(`Program 0 has reached the end of the code.`);
86+
else
87+
visConsole.addLine(`Program 0 is in a deadlock on a RCV instruction.`);
88+
if (i[1] >= instructions.length)
89+
visConsole.addLine(`Program 1 has reached the end of the code.`);
90+
else
91+
visConsole.addLine(`Program 1 is in a deadlock on a RCV instruction.`);
92+
visConsole.addLine(`Program 1 has sent <span class="highlighted">${numberOfMessages[1]}</span> message${numberOfMessages[1] == 1 ? "" : "s"}.`);
93+
}
94+
return numberOfMessages[1];
95+
}
96+
97+
for (let p = 0; p < numberOfPrograms; p++) {
98+
let opcode = instructions[i[p]].opcode, operands = instructions[i[p]].operands;
99+
let registers = memory[p];
100+
101+
if (opcode == "snd") {
102+
if (part == 1)
103+
soundFrequency = typeof operands[0] == "number" ? operands[0] : registers[operands[0]];
104+
else {
105+
messageQueues[1 - p].push(typeof operands[0] == "number" ? operands[0] : registers[operands[0]]);
106+
numberOfMessages[p]++;
107+
}
108+
}
109+
else if (opcode == "set")
110+
registers[operands[0]] = (typeof operands[1] == "number" ? operands[1] : registers[operands[1]]);
111+
else if (opcode == "add")
112+
registers[operands[0]] += (typeof operands[1] == "number" ? operands[1] : registers[operands[1]]);
113+
else if (opcode == "mul")
114+
registers[operands[0]] *= (typeof operands[1] == "number" ? operands[1] : registers[operands[1]]);
115+
else if (opcode == "mod")
116+
registers[operands[0]] %= (typeof operands[1] == "number" ? operands[1] : registers[operands[1]]);
117+
else if (opcode == "rcv") {
118+
if (part == 1 && (typeof operands[0] == "number" ? operands[0] : registers[operands[0]]) != 0) {
119+
if (visualization)
120+
visConsole.addLine(`The first RCV instruction with a non-zero value is executed after a SND instruction with value <span class="highlighted">${soundFrequency}</span>.`);
121+
return soundFrequency;
122+
}
123+
if (part == 2) {
124+
if (messageQueues[p].length > 0)
125+
registers[operands[0]] = messageQueues[p].shift();
126+
else
127+
i[p]--;
128+
}
129+
}
130+
else if (opcode == "jgz" && (typeof operands[0] == "number" ? operands[0] : registers[operands[0]]) > 0)
131+
i[p] += (typeof operands[1] == "number" ? operands[1] : registers[operands[1]]) - 1;
132+
}
133+
}
134+
}
135+
136+
finally {
137+
this.isSolving = false;
138+
}
139+
}
140+
141+
/**
142+
* Stops solving the puzzle.
143+
*/
144+
async stopSolving() {
145+
this.isStopping = true;
146+
while (this.isSolving)
147+
await(delay(10));
148+
this.isStopping = false;
149+
}
150+
}
151+
152+
/**
153+
* Puzzle instruction class.
154+
*/
155+
export class Instruction {
156+
/**
157+
* @param {string} opcode Opcode.
158+
* @param {number|string[]} operands Operands.
159+
*/
160+
constructor(opcode, operands) {
161+
/**
162+
* Opcode.
163+
* @type {string}
164+
*/
165+
this.opcode = opcode;
166+
/**
167+
* Operands.
168+
* @type {number|string[]}
169+
*/
170+
this.operands = operands;
171+
}
172+
}

2017/18/testInput.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
set a 1
2+
add a 2
3+
mul a a
4+
mod a 5
5+
snd a
6+
set a 0
7+
rcv a
8+
jgz a -1
9+
set a 1
10+
jgz a -2

tree.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,10 @@ export const years = [
466466
{
467467
name: "Day 17: Spinlock", path: "./2017/17", taskUrl: "https://adventofcode.com/2017/day/17",
468468
answers: {part1: 638}
469+
},
470+
{
471+
name: "Day 18: Duet", path: "./2017/18", taskUrl: "https://adventofcode.com/2017/day/18",
472+
answers: {part1: 4, part2: 1}
469473
}
470474
]
471475
},

0 commit comments

Comments
 (0)