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 ( / ^ ( s n d ) ( [ a - z ] | - ? \d + ) $ / ) ) != null )
26+ return new Instruction ( match [ 1 ] , [ isNaN ( match [ 2 ] ) ? match [ 2 ] : parseInt ( match [ 2 ] ) ] ) ;
27+ if ( ( match = line . match ( / ^ ( r c v ) ( [ a - z ] ) $ / ) ) != null )
28+ return new Instruction ( match [ 1 ] , [ isNaN ( match [ 2 ] ) ? match [ 2 ] : parseInt ( match [ 2 ] ) ] ) ;
29+ if ( ( match = line . match ( / ^ ( s e t | a d d | m u l | m o d ) ( [ 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 ( / ^ ( j g z ) ( [ 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+ }
0 commit comments