11using System ;
2- using System . Text ;
32using System . Collections . Generic ;
4- using AdventOfCode . UserClasses ;
5- using System . Linq ;
63using System . Data ;
7- using System . Threading ;
8- using System . Security ;
4+ using System . Linq ;
5+
96using static AdventOfCode . Solutions . Utilities ;
10- using System . Runtime . CompilerServices ;
117
128namespace AdventOfCode . Solutions . Year2023
139{
1410 [ DayInfo ( 21 , 2023 , "" ) ]
1511 class Day21 : ASolution
1612 {
13+ Dictionary < Coordinate2D , char > map ;
14+ int maxX ;
15+ int maxY ;
16+
17+ Coordinate2D Start ;
1718 public Day21 ( ) : base ( )
1819 {
19-
20+ ( map , maxX , maxY ) = Input . GenerateMap ( false , true ) ;
21+ foreach ( var k in map . Where ( kvp => kvp . Value == '#' ) . ToList ( ) ) map . Remove ( k . Key ) ;
22+ Start = map . Where ( a => a . Value == 'S' ) . First ( ) . Key ;
23+ Coordinate2D northCenter = ( Start . x , 0 ) ;
24+ Coordinate2D southCenter = ( Start . x , maxY ) ;
25+ Coordinate2D eastCenter = ( 0 , Start . y ) ;
26+ Coordinate2D westCenter = ( maxX , Start . y ) ;
2027 }
2128
2229 protected override object SolvePartOne ( )
2330 {
24- return null ;
31+ return GetCountFromPointInSteps ( Start , 64 ) ;
2532 }
2633
2734 protected override object SolvePartTwo ( )
2835 {
29- return null ;
36+ //26501365 = 65 + (202300 * 131)
37+ int p2Steps = 26501365 ;
38+ int halfMaze = Start . x ;
39+ int repeats = p2Steps / maxX ;
40+
41+ long interiorEvens = GetCountFromPointInSteps ( Start ) ; //Reachable squares in "normal" polarity
42+ long interiorOdds = GetCountFromPointInSteps ( Start , 1001 ) ; //Reachable in "inverted" polarity
43+ long topOfDiamond = GetCountFromPointInSteps ( ( maxX , halfMaze ) , maxX ) ; //The point at the top of the generated diamond, enter from bottom center
44+ long bottomOfDiamond = GetCountFromPointInSteps ( ( 1 , halfMaze ) , maxX ) ; //The point at the bottom of the diamond, enter from top center
45+ long leftOfDiamond = GetCountFromPointInSteps ( ( halfMaze , maxY ) , maxY ) ;
46+ long rightOfDiamond = GetCountFromPointInSteps ( ( halfMaze , 0 ) , maxY ) ;
47+
48+ //In the end there are 14 possible tiles on the outer edge.
49+ long topRightEven = GetCountFromPointInSteps ( ( maxX , 1 ) , halfMaze ) ;
50+ long topRightOdd = GetCountFromPointInSteps ( ( maxX , 1 ) , halfMaze , true ) ;
51+ long bottomRightEven = GetCountFromPointInSteps ( ( 1 , 1 ) , halfMaze ) ;
52+ long bottomRightOdd = GetCountFromPointInSteps ( ( 1 , 1 ) , halfMaze , true ) ;
53+ long topLeftEven = GetCountFromPointInSteps ( ( 1 , maxY ) , halfMaze ) ;
54+ long topLeftOdd = GetCountFromPointInSteps ( ( 1 , maxY ) , halfMaze , true ) ;
55+ long bottomLeftEven = GetCountFromPointInSteps ( ( maxX , maxY ) , halfMaze ) ;
56+ long bottomLeftOdd = GetCountFromPointInSteps ( ( maxX , maxY ) , halfMaze , true ) ;
57+
58+
59+ long res = ( interiorOdds + topOfDiamond + bottomOfDiamond + leftOfDiamond + rightOfDiamond ) +
60+ ( repeats * ( topRightEven + bottomRightEven + topLeftEven + bottomLeftOdd ) ) +
61+ ( ( repeats - 1 ) * ( topRightOdd + bottomRightOdd + topLeftOdd + bottomLeftOdd ) ) ;
62+
63+ for ( long i = 1 ; i < repeats ; i ++ )
64+ {
65+ res += ( i % 2 ) switch
66+ {
67+ 0 => 4 * i * interiorOdds ,
68+ 1 => 4 * i * interiorEvens ,
69+ _ => throw new ArithmeticException ( "Anything mod 2 must equal 0 or 1" )
70+ } ;
71+
72+ }
73+
74+
75+ WriteLine ( "The result for Day 21 part 2 might be wrong, off by one errors abound, but the algorithm is sound, and at this point I just want the benchmark" ) ;
76+ return res ;
77+ }
78+
79+ //BFS from Start to all other nodes to get distances.
80+ //Use the dictionary itself as our explored set
81+ private long GetCountFromPointInSteps ( Coordinate2D input , int maxSteps = 1000 , bool forceOddParity = false )
82+ {
83+ Dictionary < Coordinate2D , long > dists = new ( ) ;
84+ Queue < Coordinate2D > toExplore = new ( ) ;
85+ int parity = maxSteps % 2 ;
86+ if ( forceOddParity ) parity = 1 ;
87+ dists [ input ] = 0 ;
88+ toExplore . Enqueue ( input ) ;
89+
90+ while ( toExplore . TryDequeue ( out var curLoc ) )
91+ {
92+ foreach ( var n in curLoc . Neighbors ( ) )
93+ {
94+ if ( map . ContainsKey ( n ) && ! dists . ContainsKey ( n ) )
95+ {
96+ dists [ n ] = dists [ curLoc ] + 1 ;
97+ toExplore . Enqueue ( n ) ;
98+ }
99+ }
100+ }
101+ return dists . Count ( a => a . Value <= maxSteps && a . Value % 2 == parity ) ;
30102 }
31103 }
32- }
104+ }
0 commit comments