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+ this . noPart2 = true ;
14+ }
15+
16+ /**
17+ * Parses the puzzle input.
18+ * @param {string } input Puzzle input.
19+ * @returns {{
20+ * startState: string,
21+ * numberOfSteps: number,
22+ * stateRules: object<string,Rule>,
23+ * }} Replacements and medicine molecule.
24+ */
25+ parse ( input ) {
26+ let consoleLine = this . solConsole . addLine ( "Parsing..." ) ;
27+
28+ let blocks = input . trim ( ) . split ( / \r ? \n \r ? \n / ) ;
29+ if ( blocks . length < 2 )
30+ throw new Error ( "Invalid input data" ) ;
31+
32+ let headerLines = blocks [ 0 ] . split ( / \r ? \n / ) ;
33+ if ( headerLines . length != 2 )
34+ throw new Error ( "Invalid header" ) ;
35+ let match = headerLines [ 0 ] . match ( / ^ B e g i n i n s t a t e ( [ A - Z ] ) .$ / ) ;
36+ if ( match == null )
37+ throw new Error ( "Invalid initial state" ) ;
38+ let startState = match [ 1 ] ;
39+ match = headerLines [ 1 ] . match ( / ^ P e r f o r m a d i a g n o s t i c c h e c k s u m a f t e r ( \d + ) s t e p s ? .$ / ) ;
40+ if ( match == null )
41+ throw new Error ( "Invalid number of steps state" ) ;
42+ let numberOfSteps = parseInt ( match [ 1 ] ) ;
43+
44+ let stateRules = { } ;
45+
46+ blocks . slice ( 1 ) . forEach ( ( block , blockIndex ) => {
47+ let blockLines = block . split ( / \r ? \n / ) . map ( e => e . trim ( ) ) ;
48+ if ( blockLines . length != 9 )
49+ throw new Error ( `Invalid data in rule ${ blockIndex + 1 } ` ) ;
50+ match = blockLines [ 0 ] . match ( / ^ I n s t a t e ( [ A - Z ] ) : $ / )
51+ if ( match == null )
52+ throw new Error ( `Invalid data in rule ${ blockIndex + 1 } line 1` ) ;
53+ let ruleState = match [ 1 ] ;
54+ if ( ruleState in stateRules )
55+ throw new Error ( `Rule for state ${ ruleState } is defined more than once` ) ;
56+
57+ let valuesToWrite = [ ] , moves = [ ] , nextStates = [ ] ;
58+ if ( blockLines [ 1 ] != "If the current value is 0:" )
59+ throw new Error ( `Invalid data in rule ${ blockIndex + 1 } line 2` ) ;
60+ match = blockLines [ 2 ] . match ( / ^ - W r i t e t h e v a l u e ( 0 | 1 ) .$ / ) ;
61+ if ( match == null )
62+ throw new Error ( `Invalid data in rule ${ blockIndex + 1 } line 3` ) ;
63+ valuesToWrite . push ( parseInt ( match [ 1 ] ) ) ;
64+ match = blockLines [ 3 ] . match ( / ^ - M o v e o n e s l o t t o t h e ( l e f t | r i g h t ) .$ / ) ;
65+ if ( match == null )
66+ throw new Error ( `Invalid data in rule ${ blockIndex + 1 } line 4` ) ;
67+ moves . push ( parseInt ( match [ 1 ] == "left" ? - 1 : 1 ) ) ;
68+ match = blockLines [ 4 ] . match ( / ^ - C o n t i n u e w i t h s t a t e ( [ A - Z ] ) .$ / ) ;
69+ if ( match == null )
70+ throw new Error ( `Invalid data in rule ${ blockIndex + 1 } line 5` ) ;
71+ nextStates . push ( match [ 1 ] ) ;
72+
73+ if ( blockLines [ 5 ] != "If the current value is 1:" )
74+ throw new Error ( `Invalid data in rule ${ blockIndex + 1 } line 6` ) ;
75+ match = blockLines [ 6 ] . match ( / ^ - W r i t e t h e v a l u e ( 0 | 1 ) .$ / ) ;
76+ if ( match == null )
77+ throw new Error ( `Invalid data in rule ${ blockIndex + 1 } line 7` ) ;
78+ valuesToWrite . push ( parseInt ( match [ 1 ] ) ) ;
79+ match = blockLines [ 7 ] . match ( / ^ - M o v e o n e s l o t t o t h e ( l e f t | r i g h t ) .$ / ) ;
80+ if ( match == null )
81+ throw new Error ( `Invalid data in rule ${ blockIndex + 1 } line 8` ) ;
82+ moves . push ( parseInt ( match [ 1 ] == "left" ? - 1 : 1 ) ) ;
83+ match = blockLines [ 8 ] . match ( / ^ - C o n t i n u e w i t h s t a t e ( [ A - Z ] ) .$ / ) ;
84+ if ( match == null )
85+ throw new Error ( `Invalid data in rule ${ blockIndex + 1 } line 9` ) ;
86+ nextStates . push ( match [ 1 ] ) ;
87+
88+ stateRules [ ruleState ] = new Rule ( valuesToWrite , moves , nextStates ) ;
89+ } ) ;
90+
91+ consoleLine . innerHTML += " done." ;
92+ return { startState, numberOfSteps, stateRules} ;
93+ }
94+
95+ /**
96+ * Finds the diagnostic checksum.
97+ * @param {number } part Puzzle part.
98+ * @param {string } input Puzzle input.
99+ * @param {boolean } visualization Enable visualization.
100+ * @returns {number } Diagnostic checksum.
101+ */
102+ async solve ( part , input , visualization ) {
103+ try {
104+ this . isSolving = true ;
105+
106+ let { startState, numberOfSteps, stateRules} = this . parse ( input ) ;
107+
108+ let visConsole = new Console ( ) ;
109+ if ( visualization )
110+ this . visContainer . append ( visConsole . container ) ;
111+
112+ let onePositions = new Set ( ) , position = 0 , state = startState ;
113+ for ( let i = 0 ; i < numberOfSteps ; i ++ ) {
114+ let rule = stateRules [ state ] ;
115+ if ( rule == undefined )
116+ throw new Error ( `Invalid state ${ state } ` ) ;
117+ if ( ! onePositions . has ( position ) ) {
118+ if ( rule . valuesToWrite [ 0 ] == 1 )
119+ onePositions . add ( position ) ;
120+ position += rule . moves [ 0 ] ;
121+ state = rule . nextStates [ 0 ] ;
122+ }
123+ else {
124+ if ( rule . valuesToWrite [ 1 ] == 0 )
125+ onePositions . delete ( position ) ;
126+ position += rule . moves [ 1 ] ;
127+ state = rule . nextStates [ 1 ] ;
128+ }
129+ }
130+
131+ let numberOfOnes = onePositions . size ;
132+ if ( visualization )
133+ visConsole . addLine ( `After ${ numberOfSteps } step${ numberOfSteps == 1 ? "" : "s" } "1" appears <span class="highlighted">${ numberOfOnes } </span> time${ numberOfOnes == 1 ? "" : "s" } on the tape.` ) ;
134+
135+ return numberOfOnes ;
136+ }
137+
138+ finally {
139+ this . isSolving = false ;
140+ }
141+ }
142+
143+ /**
144+ * Stops solving the puzzle.
145+ */
146+ async stopSolving ( ) {
147+ this . isStopping = true ;
148+ while ( this . isSolving )
149+ await ( delay ( 10 ) ) ;
150+ this . isStopping = false ;
151+ }
152+ }
153+
154+ /**
155+ * Puzzle rule class.
156+ */
157+ class Rule {
158+ /**
159+ * @param {number[] } valuesToWrite Values to write if current value is 0 or 1.
160+ * @param {number[] } moves Moves if current value is 0 or 1.
161+ * @param {string[] } nextStates Next states if current value is 0 or 1.
162+ */
163+ constructor ( valuesToWrite , moves , nextStates ) {
164+ /**
165+ * Values to write if current value is 0 or 1.
166+ * @type {number[] }
167+ */
168+ this . valuesToWrite = valuesToWrite ;
169+ /**
170+ * Moves if current value is 0 or 1.
171+ * @type {number[] }
172+ */
173+ this . moves = moves ;
174+ /**
175+ * Next states if current value is 0 or 1.
176+ * @type {string[] }
177+ */
178+ this . nextStates = nextStates ;
179+ }
180+ }
0 commit comments