11import path from "node:path" ;
22import { readInputLineByLine } from "@utils/io" ;
33import { Coord } from "@utils/grid" ;
4- import { Coord3d } from "@utils/grid3d" ;
54
65export async function part1 ( inputFile : string ) {
76 return await day9 ( inputFile , findAreaOfLargestRectangle ) ;
87}
98
109export async function part2 ( inputFile : string ) {
11- return await day9 ( inputFile ) ;
10+ return await day9 ( inputFile , findAreaOfLargestRectanglePart2 ) ;
1211}
1312
1413async function day9 ( inputFile : string , calcFn ?: ( coords : Coord [ ] ) => number ) {
@@ -39,8 +38,177 @@ function findAreaOfLargestRectangle(coords: Coord[]): number {
3938 return maxArea ;
4039}
4140
41+ function findAreaOfLargestRectanglePart2 ( coords : Coord [ ] ) : number {
42+ const edges = buildEdges ( coords ) ;
43+ const { xs, ys, xIndex, yIndex} = buildCompressedAxes ( coords ) ;
44+ const prefix = buildInsidePrefix ( edges , xs , ys ) ;
45+
46+ let maxArea = 0 ;
47+
48+ for ( let i = 0 ; i < coords . length ; i ++ ) {
49+ const a = coords [ i ] ;
50+ for ( let j = i + 1 ; j < coords . length ; j ++ ) {
51+ const b = coords [ j ] ;
52+ if ( a . equals ( b ) ) continue ;
53+
54+ const loX = Math . min ( a . x , b . x ) ;
55+ const hiX = Math . max ( a . x , b . x ) ;
56+ const loY = Math . min ( a . y , b . y ) ;
57+ const hiY = Math . max ( a . y , b . y ) ;
58+
59+ const area = ( hiX - loX + 1 ) * ( hiY - loY + 1 ) ;
60+ if ( area <= maxArea ) continue ;
61+
62+ const x0 = xIndex . get ( loX ) ;
63+ const x1 = xIndex . get ( hiX + 1 ) ;
64+ const y0 = yIndex . get ( loY ) ;
65+ const y1 = yIndex . get ( hiY + 1 ) ;
66+ if ( x0 === undefined || x1 === undefined || y0 === undefined || y1 === undefined ) {
67+ continue ;
68+ }
69+
70+ const insideSum =
71+ prefix [ y1 ] [ x1 ] -
72+ prefix [ y0 ] [ x1 ] -
73+ prefix [ y1 ] [ x0 ] +
74+ prefix [ y0 ] [ x0 ] ;
75+
76+ if ( insideSum === area ) {
77+ maxArea = area ;
78+ }
79+ }
80+ }
81+
82+ return maxArea ;
83+ }
84+
4285export function findArea ( a : Coord , b : Coord ) : number {
4386 const sideA = Math . abs ( a . x - b . x ) + 1 ;
4487 const sideB = Math . abs ( a . y - b . y ) + 1 ;
4588 return sideA * sideB ;
46- }
89+ }
90+
91+ type VerticalEdge = { x : number ; loY : number ; hiY : number } ;
92+ type HorizontalEdge = { y : number ; loX : number ; hiX : number } ;
93+ type Edges = { vertical : VerticalEdge [ ] ; horizontal : HorizontalEdge [ ] } ;
94+
95+ function buildEdges ( coords : Coord [ ] ) : Edges {
96+ const vertical : VerticalEdge [ ] = [ ] ;
97+ const horizontal : HorizontalEdge [ ] = [ ] ;
98+
99+ for ( let i = 0 ; i < coords . length ; i ++ ) {
100+ const a = coords [ i ] ;
101+ const b = coords [ ( i + 1 ) % coords . length ] ;
102+
103+ if ( a . x === b . x ) {
104+ const loY = Math . min ( a . y , b . y ) ;
105+ const hiY = Math . max ( a . y , b . y ) ;
106+ vertical . push ( { x : a . x , loY, hiY} ) ;
107+ } else {
108+ const loX = Math . min ( a . x , b . x ) ;
109+ const hiX = Math . max ( a . x , b . x ) ;
110+ horizontal . push ( { y : a . y , loX, hiX} ) ;
111+ }
112+ }
113+
114+ return { vertical, horizontal} ;
115+ }
116+
117+ function buildCompressedAxes ( coords : Coord [ ] ) {
118+ const xsSet = new Set < number > ( ) ;
119+ const ysSet = new Set < number > ( ) ;
120+
121+ for ( const c of coords ) {
122+ xsSet . add ( c . x ) ;
123+ xsSet . add ( c . x + 1 ) ;
124+ ysSet . add ( c . y ) ;
125+ ysSet . add ( c . y + 1 ) ;
126+ }
127+
128+ const xs = Array . from ( xsSet ) . sort ( ( a , b ) => a - b ) ;
129+ const ys = Array . from ( ysSet ) . sort ( ( a , b ) => a - b ) ;
130+
131+ const xIndex = new Map < number , number > ( ) ;
132+ const yIndex = new Map < number , number > ( ) ;
133+
134+ xs . forEach ( ( x , i ) => xIndex . set ( x , i ) ) ;
135+ ys . forEach ( ( y , i ) => yIndex . set ( y , i ) ) ;
136+
137+ return { xs, ys, xIndex, yIndex} ;
138+ }
139+
140+ function buildInsidePrefix ( edges : Edges , xs : number [ ] , ys : number [ ] ) {
141+ const rowCount = ys . length - 1 ;
142+ const colCount = xs . length - 1 ;
143+ const dx = new Array < number > ( colCount ) ;
144+ for ( let i = 0 ; i < colCount ; i ++ ) {
145+ dx [ i ] = xs [ i + 1 ] - xs [ i ] ;
146+ }
147+ const xIndex = new Map < number , number > ( ) ;
148+ xs . forEach ( ( x , i ) => xIndex . set ( x , i ) ) ;
149+
150+ const prefix : Float64Array [ ] = [ ] ;
151+ prefix . push ( new Float64Array ( colCount + 1 ) ) ;
152+
153+ for ( let row = 0 ; row < rowCount ; row ++ ) {
154+ const y = ys [ row ] ;
155+ const height = ys [ row + 1 ] - y ;
156+
157+ const crossings : number [ ] = [ ] ;
158+ for ( const edge of edges . vertical ) {
159+ if ( y >= edge . loY && y < edge . hiY ) {
160+ crossings . push ( edge . x ) ;
161+ }
162+ }
163+ crossings . sort ( ( a , b ) => a - b ) ;
164+
165+ const intervals : Array < [ number , number ] > = [ ] ;
166+ for ( let i = 0 ; i + 1 < crossings . length ; i += 2 ) {
167+ const start = crossings [ i ] ;
168+ const endInclusive = crossings [ i + 1 ] ;
169+ intervals . push ( [ start , endInclusive + 1 ] ) ;
170+ }
171+
172+ for ( const edge of edges . horizontal ) {
173+ if ( edge . y === y ) {
174+ intervals . push ( [ edge . loX , edge . hiX + 1 ] ) ;
175+ }
176+ }
177+
178+ intervals . sort ( ( a , b ) => a [ 0 ] - b [ 0 ] || a [ 1 ] - b [ 1 ] ) ;
179+
180+ const merged : Array < [ number , number ] > = [ ] ;
181+ for ( const [ start , end ] of intervals ) {
182+ if ( merged . length === 0 ) {
183+ merged . push ( [ start , end ] ) ;
184+ continue ;
185+ }
186+ const last = merged [ merged . length - 1 ] ;
187+ if ( start <= last [ 1 ] ) {
188+ if ( end > last [ 1 ] ) last [ 1 ] = end ;
189+ } else {
190+ merged . push ( [ start , end ] ) ;
191+ }
192+ }
193+
194+ const rowWeights = new Float64Array ( colCount ) ;
195+ for ( const [ start , end ] of merged ) {
196+ const startIndex = xIndex . get ( start ) ;
197+ const endIndex = xIndex . get ( end ) ;
198+ if ( startIndex === - 1 || endIndex === - 1 ) continue ;
199+ if ( startIndex === undefined || endIndex === undefined ) continue ;
200+ for ( let x = startIndex ; x < endIndex ; x ++ ) {
201+ rowWeights [ x ] = dx [ x ] * height ;
202+ }
203+ }
204+
205+ const prev = prefix [ row ] ;
206+ const current = new Float64Array ( colCount + 1 ) ;
207+ for ( let x = 0 ; x < colCount ; x ++ ) {
208+ current [ x + 1 ] = prev [ x + 1 ] + current [ x ] - prev [ x ] + rowWeights [ x ] ;
209+ }
210+ prefix . push ( current ) ;
211+ }
212+
213+ return prefix ;
214+ }
0 commit comments