1+ import { delay , Console , PixelMap , leastCommonMultiple } from "../../utility.mjs" ;
2+
3+ const packetColorIndex = 1 ;
4+ const packetColor = "#ffffff" ;
5+ const scannerColorIndex = 2 ;
6+ const scannerColor = "#999999" ;
7+ const caughtColorIndex = 3 ;
8+ const caughtColor = "#ff0000" ;
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 {number[] } Scanner ranges.
26+ */
27+ parse ( input ) {
28+ let consoleLine = this . solConsole . addLine ( "Parsing..." ) ;
29+
30+ let scannerRanges = [ ] ;
31+ input . trim ( ) . split ( / \r ? \n / ) . forEach ( ( line , index ) => {
32+ let match = line . match ( / ^ ( \d + ) : ( \d + ) $ / ) ;
33+ if ( match == null )
34+ throw new Error ( `Invalid data in line ${ index + 1 } ` ) ;
35+ let layerIndex = parseInt ( match [ 1 ] ) ;
36+ let range = parseInt ( match [ 2 ] ) ;
37+ for ( let i = scannerRanges . length ; i <= layerIndex ; i ++ )
38+ scannerRanges . push ( 0 ) ;
39+ scannerRanges [ layerIndex ] = range ;
40+ } ) ;
41+
42+ consoleLine . innerHTML += " done." ;
43+ return scannerRanges ;
44+ }
45+
46+
47+ /**
48+ * Finds the severity of trip through the firewall.
49+ * @param {number } part Puzzle part.
50+ * @param {string } input Puzzle input.
51+ * @param {boolean } visualization Enable visualization.
52+ * @returns {number } Severity of trip through the firewall.
53+ */
54+ async solve ( part , input , visualization ) {
55+ try {
56+ this . isSolving = true ;
57+
58+ let scannerRanges = this . parse ( input ) ;
59+
60+ let solConsoleLine ;
61+ let pixelMap = new PixelMap ( scannerRanges . length , Math . max ( ...scannerRanges , 1 ) ) ;
62+ if ( visualization ) {
63+ solConsoleLine = this . solConsole . addLine ( ) ;
64+
65+ this . visContainer . append ( pixelMap . container ) ;
66+ pixelMap . palette [ packetColorIndex ] = packetColor ;
67+ pixelMap . palette [ scannerColorIndex ] = scannerColor ;
68+ pixelMap . palette [ caughtColorIndex ] = caughtColor ;
69+ }
70+
71+ let maxPacketDelay = scannerRanges . reduce ( ( acc , e ) => leastCommonMultiple ( acc , Math . max ( 1 , ( e - 1 ) * 2 ) ) , 1 ) ;
72+
73+ for ( let packetDelay = 0 ; packetDelay < maxPacketDelay ; packetDelay ++ ) {
74+ let caught = false ;
75+ let severity = 0 ;
76+ for ( let i = 0 ; i < scannerRanges . length ; i ++ ) {
77+ if ( scannerRanges [ i ] > 0 && ( scannerRanges [ i ] == 1 || ( packetDelay + i ) % ( ( scannerRanges [ i ] - 1 ) * 2 ) == 0 ) ) {
78+ caught = true ;
79+ severity += i * scannerRanges [ i ] ;
80+ }
81+ }
82+
83+ if ( part == 1 || ( part == 2 && ! caught ) ) {
84+ if ( visualization ) {
85+ if ( part == 2 )
86+ solConsoleLine . innerHTML = `Delay: ${ packetDelay } .` ;
87+
88+ severity = 0 ;
89+ for ( let i = 0 ; i < scannerRanges . length ; i ++ ) {
90+ if ( this . isStopping )
91+ return ;
92+
93+ for ( let x = 0 ; x < pixelMap . width ; x ++ ) {
94+ let scannerY = - 1 ;
95+ if ( scannerRanges [ x ] == 1 )
96+ scannerY = 0 ;
97+ else if ( scannerRanges [ x ] > 1 ) {
98+ scannerY = ( packetDelay + i ) % ( ( scannerRanges [ x ] - 1 ) * 2 ) ;
99+ if ( scannerY >= scannerRanges [ x ] )
100+ scannerY = ( scannerRanges [ x ] - 2 ) - ( scannerY - scannerRanges [ x ] ) ;
101+ }
102+
103+ for ( let y = 0 ; y < pixelMap . height ; y ++ ) {
104+ pixelMap . drawPixel ( x , y , scannerY == y ? scannerColorIndex : 0 ) ;
105+ }
106+ }
107+
108+ pixelMap . drawPixel ( i , 0 , pixelMap . image [ 0 ] [ i ] == 0 ? packetColorIndex : caughtColorIndex ) ;
109+ if ( pixelMap . image [ 0 ] [ i ] == caughtColorIndex )
110+ severity += i * scannerRanges [ i ] ;
111+ if ( part == 1 )
112+ solConsoleLine . innerHTML = `Severity: ${ severity } .` ;
113+
114+ await delay ( 100 ) ;
115+ }
116+ }
117+
118+ return part == 1 ? severity : packetDelay ;
119+ }
120+ }
121+
122+ throw new Error ( "Solution not found" ) ;
123+ }
124+
125+ finally {
126+ this . isSolving = false ;
127+ }
128+ }
129+
130+ /**
131+ * Stops solving the puzzle.
132+ */
133+ async stopSolving ( ) {
134+ this . isStopping = true ;
135+ while ( this . isSolving )
136+ await ( delay ( 10 ) ) ;
137+ this . isStopping = false ;
138+ }
139+ }
0 commit comments