@@ -5,140 +5,62 @@ namespace AdventOfCode.Y2025.Day09;
55using System . Linq ;
66using System . Numerics ;
77
8- record Segment ( Complex a , Complex b ) ;
9- record Rectangle ( Complex top , Complex left , Complex bottom , Complex right ) ;
8+ record Rectangle ( long top , long left , long bottom , long right ) ;
109
1110[ ProblemName ( "Movie Theater" ) ]
1211class Solution : Solver {
1312
1413 public object PartOne ( string input ) {
1514 var points = Parse ( input ) ;
1615 return (
17- from p1 in points
18- from p2 in points
19- select Area ( p1 , p2 )
20- ) . Max ( ) ;
16+ from r in RectanglesOrderedByArea ( points )
17+ select Area ( r )
18+ ) . First ( ) ;
2119 }
2220
2321 public object PartTwo ( string input ) {
24- // The input looks like a circle with a slot in the middle of it:
25- //
26- // xxx
27- // xxxxxxxxx
28- // xxxxxxxxxAx
29- // xx
30- // xxxxxxxxxBx
31- // xxxxxxxxx
32- // xxx
33- //
34- // To speed things up we find the corners A and B first. Based on
35- // visual observation one of them must be a corner of the final rectangle.
36- //
37- // Then go over the 'red' points in the upper and lower halves of
38- // the picture and find the greatest rectangle.
39-
4022 var points = Parse ( input ) ;
41- var segments = Segments ( points ) ;
42- var shape = Draw ( points ) ;
43-
44- var res = 0L ;
45-
46- var slot = Segments ( points )
47- . OrderByDescending ( s => Area ( s . Item1 , s . Item2 ) )
48- . ThenBy ( s=> s . Item1 . Imaginary )
49- . Select ( s => s . Item1 . Real > s . Item2 . Real ? s . Item1 : s . Item2 )
50- . Take ( 2 )
51- . ToArray ( ) ;
52-
53- var upper = from p in points where p . Imaginary >= slot [ 0 ] . Imaginary select ( p1 : slot [ 0 ] , p2 : p ) ;
54- var lower = from p in points where p . Imaginary <= slot [ 1 ] . Imaginary select ( p1 : slot [ 1 ] , p2 : p ) ;
55-
56- var reactanglesByArea = (
57- from ps in upper . Concat ( lower )
58- let p1 = ps . p1
59- let p2 = ps . p2
60- let top = Math . Min ( p1 . Imaginary , p2 . Imaginary ) * Complex . ImaginaryOne
61- let bottom = Math . Max ( p1 . Imaginary , p2 . Imaginary ) * Complex . ImaginaryOne
62- let left = Math . Min ( p1 . Real , p2 . Real )
63- let right = Math . Max ( p1 . Real , p2 . Real )
64- orderby Area ( top + left , bottom + right ) descending
65- select new Rectangle ( top , left , bottom , right )
66- ) . ToArray ( ) ;
67-
68- foreach ( var r in reactanglesByArea ) {
69- if ( Inside ( r . top + r . left , shape ) &&
70- Inside ( r . top + r . right , shape ) &&
71- Inside ( r . bottom + r . right , shape ) &&
72- Inside ( r . bottom + r . left , shape ) &&
73- points . All ( p => ! InRect ( p , r ) )
74- ) {
75- res = Area ( r . top + r . left , r . bottom + r . right ) ;
76- break ;
77- }
78- }
79-
80- return res ;
23+ var segments = Boundary ( points ) . ToArray ( ) ;
24+ return (
25+ from r in RectanglesOrderedByArea ( points )
26+ where segments . All ( s => ! AabbCollision ( r , s ) )
27+ select Area ( r )
28+ ) . First ( ) ;
8129 }
8230
83-
84- IEnumerable < ( Complex , Complex ) > Segments ( Complex [ ] points ) {
85- return points . Zip ( points . Prepend ( points . Last ( ) ) ) ;
31+ IEnumerable < Rectangle > RectanglesOrderedByArea ( Complex [ ] points ) =>
32+ from p1 in points
33+ from p2 in points
34+ let r = RectangleFromPoints ( p1 , p2 )
35+ orderby Area ( r ) descending
36+ select r ;
37+
38+ IEnumerable < Rectangle > Boundary ( Complex [ ] corners ) =>
39+ from pair in corners . Zip ( corners . Prepend ( corners . Last ( ) ) )
40+ select RectangleFromPoints ( pair . First , pair . Second ) ;
41+
42+ Rectangle RectangleFromPoints ( Complex p1 , Complex p2 ) {
43+ var top = Math . Min ( p1 . Imaginary , p2 . Imaginary ) ;
44+ var bottom = Math . Max ( p1 . Imaginary , p2 . Imaginary ) ;
45+ var left = Math . Min ( p1 . Real , p2 . Real ) ;
46+ var right = Math . Max ( p1 . Real , p2 . Real ) ;
47+ return new Rectangle ( ( long ) top , ( long ) left , ( long ) bottom , ( long ) right ) ;
8648 }
49+
8750 Complex [ ] Parse ( string input ) => (
8851 from line in input . Split ( "\n " )
8952 let parts = line . Split ( "," ) . Select ( int . Parse ) . ToArray ( )
9053 select parts [ 0 ] + Complex . ImaginaryOne * parts [ 1 ]
9154 ) . ToArray ( ) ;
9255
93- long Area ( Complex p1 , Complex p2 ) {
94- return ( long ) ( Math . Abs ( p1 . Real - p2 . Real ) + 1 ) *
95- ( long ) ( Math . Abs ( p1 . Imaginary - p2 . Imaginary ) + 1 ) ;
96- }
97- HashSet < Complex > Draw ( Complex [ ] points ) {
98- var res = new HashSet < Complex > ( ) ;
99- var segments = Segments ( points ) ;
100- foreach ( var line in segments ) {
101- var a = line . Item1 ;
102- var b = line . Item2 ;
103- var d =
104- Math . Sign ( b . Real - a . Real ) +
105- Complex . ImaginaryOne * Math . Sign ( b . Imaginary - a . Imaginary ) ;
106- for ( var p = a ; p != b ; p += d ) {
107- res . Add ( p ) ;
108- }
109- res . Add ( b ) ;
110- }
111- return res ;
112- }
113-
114- bool InRect ( Complex position , Rectangle r ) {
115- return
116- r . left . Real < position . Real && position . Real < r . right . Real &&
117- r . top . Imaginary < position . Imaginary && position . Imaginary < r . bottom . Imaginary
118- ;
119- }
120-
121- // Check if position is inside the loop using ray casting algorithm
122- bool Inside ( Complex position , HashSet < Complex > shape ) {
123- // Imagine a small elf starting from the top half of a cell and moving
124- // to the left jumping over the borders it encounters. It needs to jump
125- // over only 'vertically' oriented pipes leading upwards, since it runs
126- // in the top of the row. Each jump flips the "inside" variable.
127- if ( shape . Contains ( position ) ) {
128- return true ;
129- }
56+ long Area ( Rectangle r ) => ( r . bottom - r . top + 1 ) * ( r . right - r . left + 1 ) ;
13057
131- var inside = false ;
132- position -= 1 ;
133- while ( position . Real > 0 ) {
134- if ( shape . Contains ( position ) && shape . Contains ( position + Complex . ImaginaryOne ) ) {
135- inside = ! inside ;
136- }
137-
138- position -= 1 ;
139- }
140- return inside ;
58+ // see https://kishimotostudios.com/articles/aabb_collision/
59+ bool AabbCollision ( Rectangle a , Rectangle b ) {
60+ var aIsToTheLeft = a . right <= b . left ;
61+ var aIsToTheRight = a . left >= b . right ;
62+ var aIsAbove = a . bottom <= b . top ;
63+ var aIsBelow = a . top >= b . bottom ;
64+ return ! ( aIsToTheRight || aIsToTheLeft || aIsAbove || aIsBelow ) ;
14165 }
142-
143-
14466}
0 commit comments