1+ import { delay , Console , Vector2D , PixelMap } from "../../utility.mjs" ;
2+
3+ const usedSquareColorIndex = 1 ;
4+ const usedSquareColor = "#999999" ;
5+ const regionColors = [ "#999999" , "#ffffff" , "#00aa00" , "#ffff00" ] ;
6+
7+ export default class {
8+ /**
9+ * @param {Console } solConsole Solution console.
10+ * @param {HTMLElement } visContainer Visualization container.
11+ */
12+ constructor ( solConsole , visContainer ) {
13+ this . isSolving = false ;
14+ this . isStopping = false ;
15+ this . solConsole = typeof solConsole !== "undefined" ? solConsole : new Console ( ) ;
16+ this . visContainer = visContainer ;
17+ }
18+
19+ /**
20+ * Parses the puzzle input.
21+ * @param {string } input Puzzle input.
22+ * @returns {string } Key string.
23+ */
24+ parse ( input ) {
25+ let consoleLine = this . solConsole . addLine ( "Parsing..." ) ;
26+
27+ let keyString = input . trim ( ) ;
28+
29+ consoleLine . innerHTML += " done." ;
30+ return keyString ;
31+ }
32+
33+
34+ /**
35+ * Finds the number of used grid squares (part 1) or the number of regions (part 2).
36+ * @param {number } part Puzzle part.
37+ * @param {string } input Puzzle input.
38+ * @param {boolean } visualization Enable visualization.
39+ * @returns {number } Number of used grid squares (part 1) or the number of regions (part 2).
40+ */
41+ async solve ( part , input , visualization ) {
42+ try {
43+ this . isSolving = true ;
44+
45+ let keyString = this . parse ( input ) ;
46+
47+ let pixelMap = new PixelMap ( 128 , 128 ) ;
48+ if ( visualization ) {
49+ this . visContainer . append ( pixelMap . container ) ;
50+ pixelMap . palette [ usedSquareColorIndex ] = usedSquareColor ;
51+ regionColors . forEach ( ( e , i ) => pixelMap . palette [ usedSquareColorIndex + 1 + i ] = e ) ;
52+ }
53+
54+ let numberOfUsedSquares = 0 ;
55+
56+ // Calculate hashes and draw the map
57+ for ( let y = 0 ; y < pixelMap . height ; y ++ ) {
58+ let lengths = [ ...`${ keyString } -${ y } ` . split ( "" ) . map ( e => e . charCodeAt ( 0 ) ) , 17 , 31 , 73 , 47 , 23 ] ;
59+ let list = new Array ( 256 ) . fill ( null ) . map ( ( e , i ) => i ) ;
60+ let currentPosition = 0 , skipSize = 0 ;
61+ for ( let round = 0 ; round < 64 ; round ++ ) {
62+ for ( let length of lengths ) {
63+ let newList = list . slice ( ) ;
64+ for ( let i = 0 ; i < length ; i ++ )
65+ newList [ ( currentPosition + i ) % list . length ] = list [ ( currentPosition + length - 1 - i ) % list . length ] ;
66+ list = newList ;
67+ currentPosition = ( currentPosition + length + ( skipSize ++ ) ) % list . length ;
68+ }
69+ }
70+ let denseHash = [ ] ;
71+ for ( let i = 0 ; i < 16 ; i ++ )
72+ denseHash . push ( ...list . slice ( i * 16 , ( i + 1 ) * 16 ) . reduce ( ( acc , e ) => acc ^ e , 0 ) . toString ( 2 ) . padStart ( 8 , "0" ) . split ( "" ) . map ( e => e != "0" ? 1 : 0 ) ) ;
73+
74+ numberOfUsedSquares += denseHash . reduce ( ( acc , e ) => acc += e , 0 ) ;
75+
76+ denseHash . forEach ( ( e , x ) => pixelMap . drawPixel ( x , y , e != 0 ? usedSquareColorIndex : 0 ) ) ;
77+ }
78+
79+ // Fill the regions in the map
80+ if ( part == 1 )
81+ return numberOfUsedSquares ;
82+
83+ let regionIndex = 2 ;
84+ for ( let x = 0 ; x < pixelMap . width ; x ++ ) {
85+ for ( let y = 0 ; y < pixelMap . height ; y ++ ) {
86+ if ( pixelMap . image [ y ] [ x ] == 1 ) {
87+ let squares = [ new Vector2D ( x , y ) ] ;
88+ while ( squares . length ) {
89+ let newSquares = [ ] ;
90+ for ( let square of squares ) {
91+ [ new Vector2D ( 1 , 0 ) , new Vector2D ( 0 , 1 ) , new Vector2D ( - 1 , 0 ) , new Vector2D ( 0 , - 1 ) ]
92+ . map ( e => e . add ( square ) )
93+ . filter ( e => e . x >= 0 && e . x < pixelMap . width && e . y >= 0 && e . y < pixelMap . height && pixelMap . image [ e . y ] [ e . x ] == 1 )
94+ . forEach ( e => {
95+ newSquares . push ( e ) ;
96+ pixelMap . image [ e . y ] [ e . x ] = regionIndex
97+ } ) ;
98+ }
99+ squares = newSquares ;
100+ }
101+ regionIndex ++ ;
102+ }
103+ }
104+ }
105+
106+ if ( visualization ) {
107+ for ( let x = 0 ; x < pixelMap . width ; x ++ ) {
108+ for ( let y = 0 ; y < pixelMap . height ; y ++ ) {
109+ if ( pixelMap . image [ y ] [ x ] != 0 )
110+ pixelMap . drawPixel ( x , y , usedSquareColorIndex + 1 + ( pixelMap . image [ y ] [ x ] % regionColors . length ) ) ;
111+ }
112+ }
113+ }
114+
115+ return regionIndex - 2 ;
116+ }
117+
118+ finally {
119+ this . isSolving = false ;
120+ }
121+ }
122+
123+ /**
124+ * Stops solving the puzzle.
125+ */
126+ async stopSolving ( ) {
127+ this . isStopping = true ;
128+ while ( this . isSolving )
129+ await ( delay ( 10 ) ) ;
130+ this . isStopping = false ;
131+ }
132+ }
0 commit comments