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+ }
0 commit comments