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 {EnhancementRule[] } Enhancement rules.
19+ */
20+ parse ( input ) {
21+ let consoleLine = this . solConsole . addLine ( "Parsing..." ) ;
22+
23+ let enhancementRules = input . split ( / \r ? \n / ) . map ( ( line , index ) => {
24+ let match = line . match ( / ^ ( [ \. \/ # ] + ) = > ( [ \. \/ # ] + ) $ / ) ;
25+ if ( match == null )
26+ throw new Error ( `Invalid data in line ${ index + 1 } ` ) ;
27+
28+ let from = match [ 1 ] . split ( "/" ) . map ( e => e . split ( "" ) . map ( e => e == "#" ? 1 : 0 ) ) ;
29+ let to = match [ 2 ] . split ( "/" ) . map ( e => e . split ( "" ) . map ( e => e == "#" ? 1 : 0 ) ) ;
30+
31+ let flip = shape => shape . map ( ( e1 , y ) => shape [ 0 ] . map ( ( e2 , x ) => shape [ y ] [ shape [ 0 ] . length - 1 - x ] ) ) ;
32+ let rotate = shape => shape [ 0 ] . map ( ( e1 , x ) => shape . map ( ( e2 , y ) => shape [ shape . length - 1 - y ] [ x ] ) ) ;
33+
34+ if ( ( from . length == 2 && from . every ( e => e . length == 2 ) && to . length == 3 && to . every ( e => e . length == 3 ) )
35+ || ( from . length == 3 && from . every ( e => e . length == 3 ) && to . length == 4 && to . every ( e => e . length == 4 ) ) ) {
36+ return new EnhancementRule ( [ from , rotate ( from ) , rotate ( rotate ( from ) ) , rotate ( rotate ( rotate ( from ) ) ) ,
37+ flip ( from ) , rotate ( flip ( from ) ) , rotate ( rotate ( flip ( from ) ) ) , rotate ( rotate ( rotate ( flip ( from ) ) ) ) ] , to )
38+ }
39+ else
40+ throw new Error ( `Invalid data in line ${ index + 1 } ` ) ;
41+ } ) ;
42+
43+ consoleLine . innerHTML += " done." ;
44+ return enhancementRules ;
45+ }
46+
47+ /**
48+ * Finds the number of pixels that stay on after the specified number of iterations.
49+ * @param {number } part Puzzle part.
50+ * @param {string } input Puzzle input.
51+ * @param {boolean } visualization Enable visualization.
52+ * @returns {number } Number of pixels that stay on after the specified number of iterations.
53+ */
54+ async solve ( part , input , visualization ) {
55+ try {
56+ this . isSolving = true ;
57+
58+ let enhancementRules = this . parse ( input ) ;
59+ let image = [ [ 0 , 1 , 0 ] , [ 0 , 0 , 1 ] , [ 1 , 1 , 1 ] ] ;
60+ let numberOfIterations = enhancementRules . length < 5 ? 2 : ( part == 1 ? 5 : 18 ) ;
61+
62+ let visConsole = new Console ( ) ;
63+ if ( visualization )
64+ this . visContainer . append ( visConsole . container ) ;
65+
66+ for ( let i = 0 ; i < numberOfIterations ; i ++ ) {
67+ let imageSize = image . length ;
68+ let fromBlockSize = imageSize % 2 == 0 ? 2 : 3 ;
69+ let toBlockSize = imageSize % 2 == 0 ? 3 : 4 ;
70+ let newImageSize = imageSize % 2 == 0 ? imageSize / 2 * 3 : imageSize / 3 * 4 ;
71+ let newImage = new Array ( newImageSize ) . fill ( null ) . map ( e => e = new Array ( newImageSize ) . fill ( 0 ) ) ;
72+
73+ for ( let x = 0 ; x < imageSize / fromBlockSize ; x ++ ) {
74+ for ( let y = 0 ; y < imageSize / fromBlockSize ; y ++ ) {
75+ for ( let enhancementRuleIndex = 0 , enhancementRuleApplied = false ; enhancementRuleIndex < enhancementRules . length && ! enhancementRuleApplied ; enhancementRuleIndex ++ ) {
76+ let enhancementRule = enhancementRules [ enhancementRuleIndex ]
77+ let to = enhancementRule . to ;
78+ if ( to . length == toBlockSize ) {
79+ for ( let fromIndex = 0 ; fromIndex < enhancementRule . from . length && ! enhancementRuleApplied ; fromIndex ++ ) {
80+ let from = enhancementRule . from [ fromIndex ] ;
81+ if ( from . length == fromBlockSize ) {
82+ let match = true ;
83+ for ( let ix = 0 ; ix < fromBlockSize && match ; ix ++ ) {
84+ for ( let iy = 0 ; iy < fromBlockSize && match ; iy ++ ) {
85+ if ( from [ iy ] [ ix ] != image [ y * fromBlockSize + iy ] [ x * fromBlockSize + ix ] )
86+ match = false ;
87+ }
88+ }
89+ if ( match ) {
90+ for ( let ix = 0 ; ix < toBlockSize ; ix ++ ) {
91+ for ( let iy = 0 ; iy < toBlockSize ; iy ++ )
92+ newImage [ y * toBlockSize + iy ] [ x * toBlockSize + ix ] = to [ iy ] [ ix ] ;
93+ }
94+ enhancementRuleApplied = true ;
95+ }
96+ }
97+ }
98+ }
99+ }
100+ }
101+ }
102+
103+ image = newImage ;
104+
105+ if ( visualization ) {
106+ let numberOfOnPixels = image . reduce ( ( acc , e ) => acc + e . reduce ( ( acc , e ) => acc + e , 0 ) , 0 ) ;
107+ visConsole . addLine ( `Iteration ${ i + 1 } : ${ numberOfOnPixels } pixel${ numberOfOnPixels == 1 ? "" : "s" } are on.` ) ;
108+ }
109+ }
110+
111+ return image . reduce ( ( acc , e ) => acc + e . reduce ( ( acc , e ) => acc + e , 0 ) , 0 ) ;
112+ }
113+
114+ finally {
115+ this . isSolving = false ;
116+ }
117+ }
118+
119+ /**
120+ * Stops solving the puzzle.
121+ */
122+ async stopSolving ( ) {
123+ this . isStopping = true ;
124+ while ( this . isSolving )
125+ await ( delay ( 10 ) ) ;
126+ this . isStopping = false ;
127+ }
128+ }
129+
130+ /**
131+ * Puzzle particle class.
132+ */
133+ class EnhancementRule {
134+ /**
135+ * @param {number[][][] } from From.
136+ * @param {number[][] } to To.
137+ */
138+ constructor ( from , to ) {
139+ /**
140+ * From.
141+ * @type {number[][] }
142+ */
143+ this . from = from ;
144+ /**
145+ * To.
146+ * @type {number[][] }
147+ */
148+ this . to = to ;
149+ }
150+ }
0 commit comments