1+ import { delay , Console , Vector2D , PixelMap } from "../../utility.mjs" ;
2+
3+ const pathColorIndex = 1 ;
4+ const pathColor = "#999999" ;
5+ const packetColorIndex = 2 ;
6+ const packetColor = "#ffffff" ;
7+ const letterColorIndex = 3 ;
8+ const letterColor = "#ffff00" ;
9+
10+ export default class {
11+ /**
12+ * @param {Console } solConsole Solution console.
13+ * @param {HTMLElement } visContainer Visualization container.
14+ */
15+ constructor ( solConsole , visContainer ) {
16+ this . isSolving = false ;
17+ this . isStopping = false ;
18+ this . solConsole = typeof solConsole !== "undefined" ? solConsole : new Console ( ) ;
19+ this . visContainer = visContainer ;
20+ }
21+
22+ /**
23+ * Parses the puzzle input.
24+ * @param {string } input Puzzle input.
25+ * @returns {{
26+ * map: number[][],
27+ * letters: object<string,Vector2D>,
28+ * }} Map and letters.
29+ */
30+ parse ( input ) {
31+ let consoleLine = this . solConsole . addLine ( "Parsing..." ) ;
32+
33+ let letters = { } ;
34+ let map = input . split ( / \r ? \n / ) . filter ( e => e != "" ) . map ( ( line , index , lines ) => {
35+ if ( line . length != lines [ 0 ] . length )
36+ throw new Error ( `Invalid length of line ${ index + 1 } ` ) ;
37+ if ( ! / ^ [ A - Z \- \| \+ \s ] + $ / . test ( line ) )
38+ throw new Error ( `Invalid data in line ${ index + 1 } ` ) ;
39+ let symbols = line . split ( "" ) ;
40+ for ( let x = 0 ; x < symbols . length ; x ++ ) {
41+ if ( / [ A - Z ] / . test ( symbols [ x ] ) )
42+ letters [ symbols [ x ] ] = new Vector2D ( x , index ) ;
43+ }
44+ return symbols . map ( e => e != " " ? pathColorIndex : 0 ) ;
45+ } ) ;
46+
47+ if ( map . length == 0 )
48+ throw new Error ( "Invalid input data" ) ;
49+
50+ if ( map [ 0 ] . every ( e => e == 0 ) )
51+ throw new Error ( "Start not found" ) ;
52+
53+ consoleLine . innerHTML += " done." ;
54+ return { map, letters } ;
55+ }
56+
57+ /**
58+ * Finds the sequence of letters along the path (part 1) or the number of steps (part 2).
59+ * @param {number } part Puzzle part.
60+ * @param {string } input Puzzle input.
61+ * @param {boolean } visualization Enable visualization.
62+ * @returns {string|number } Sequence of letters along the path (part 1) or the number of steps (part 2).
63+ */
64+ async solve ( part , input , visualization ) {
65+ try {
66+ this . isSolving = true ;
67+
68+ let { map, letters} = this . parse ( input ) ;
69+
70+ let mapWidth = map [ 0 ] . length , mapHeight = map . length ;
71+
72+ let solConsoleLine ;
73+ let pixelMap = new PixelMap ( mapWidth , mapHeight ) ;
74+ if ( visualization ) {
75+ solConsoleLine = this . solConsole . addLine ( part == 1 ? "Letter sequence: ." : "Step: 1." ) ;
76+ this . visContainer . append ( pixelMap . container ) ;
77+ pixelMap . palette [ pathColorIndex ] = pathColor ;
78+ pixelMap . palette [ packetColorIndex ] = packetColor ;
79+ pixelMap . palette [ letterColorIndex ] = letterColor ;
80+ pixelMap . draw ( map ) ;
81+ for ( let letter in letters )
82+ pixelMap . drawPixel ( letters [ letter ] . x , letters [ letter ] . y , letterColorIndex ) ;
83+ }
84+
85+ let letterSequence = "" , step = 1 ;
86+ let position = new Vector2D ( map [ 0 ] . indexOf ( pathColorIndex ) , 0 ) ;
87+ let letter = Object . keys ( letters ) . find ( e => letters [ e ] . equals ( position ) ) ;
88+ if ( letter != undefined )
89+ letterSequence += letter ;
90+ if ( visualization )
91+ pixelMap . drawPixel ( position . x , position . y , packetColorIndex ) ;
92+
93+ let direction = new Vector2D ( 0 , 1 ) ;
94+ let directions = [ new Vector2D ( 1 , 0 ) , new Vector2D ( 0 , 1 ) , new Vector2D ( - 1 , 0 ) , new Vector2D ( 0 , - 1 ) ] ;
95+
96+ while ( 1 ) {
97+ if ( this . isStopping )
98+ return ;
99+
100+ let nextPosition = [ direction , ...directions . filter ( e => ! e . equals ( direction ) && ! e . equals ( direction . clone ( ) . multiply ( - 1 ) ) ) ]
101+ . map ( dir => position . clone ( ) . add ( dir ) )
102+ . filter ( nextPosition => nextPosition . x >= 0 && nextPosition . x < mapWidth && nextPosition . y >= 0 && nextPosition . y < mapHeight && map [ nextPosition . y ] [ nextPosition . x ] != 0 ) [ 0 ] ;
103+
104+ if ( nextPosition == undefined )
105+ return part == 1 ? letterSequence : step ;
106+
107+ step ++ ;
108+ direction = nextPosition . clone ( ) . subtract ( position ) ;
109+ position = nextPosition ;
110+
111+ letter = Object . keys ( letters ) . find ( e => letters [ e ] . equals ( position ) ) ;
112+ if ( letter != undefined )
113+ letterSequence += letter ;
114+
115+ if ( visualization && pixelMap . image [ position . y ] [ position . x ] == pathColorIndex ) {
116+ pixelMap . drawPixel ( position . x , position . y , packetColorIndex ) ;
117+ solConsoleLine . innerHTML = part == 1 ? `Letter sequence: ${ letterSequence } .` : `Step: ${ step } .` ;
118+ await delay ( 1 ) ;
119+ }
120+ }
121+ }
122+
123+ finally {
124+ this . isSolving = false ;
125+ }
126+ }
127+
128+ /**
129+ * Stops solving the puzzle.
130+ */
131+ async stopSolving ( ) {
132+ this . isStopping = true ;
133+ while ( this . isSolving )
134+ await ( delay ( 10 ) ) ;
135+ this . isStopping = false ;
136+ }
137+ }
0 commit comments