@@ -10,100 +10,80 @@ class Solution : Solver {
10
10
11
11
// I used complex numbers for a change. The map is represented with a hashset of positions.
12
12
13
- public object PartOne ( string input ) {
14
- var state = Parse ( input ) ;
15
- for ( var i = 0 ; i < 10 ; i ++ ) {
16
- state = Step ( state ) ;
17
- }
18
-
19
- // smallest enclosing rectangle
20
- var width = state . elves . Select ( p => p . Real ) . Max ( ) -
21
- state . elves . Select ( p => p . Real ) . Min ( ) + 1 ;
13
+ public object PartOne ( string input )
14
+ => Simulate ( Parse ( input ) ) . Select ( Area ) . ElementAt ( 9 ) ;
22
15
23
- var height = state . elves . Select ( p => p . Imaginary ) . Max ( ) -
24
- state . elves . Select ( p => p . Imaginary ) . Min ( ) + 1 ;
16
+ public object PartTwo ( string input )
17
+ => Simulate ( Parse ( input ) ) . Count ( ) ;
25
18
26
- return width * height - state . elves . Count ;
27
- }
19
+ IEnumerable < HashSet < Complex > > Simulate ( HashSet < Complex > elves ) {
20
+ var lookAround = new Queue < Complex > ( new [ ] { N , S , W , E } ) ;
28
21
29
- public object PartTwo ( string input ) {
30
- // interate until fixpoint is reached
31
- var steps = 0 ;
22
+ for ( var fixpoint = false ; ! fixpoint ; lookAround . Enqueue ( lookAround . Dequeue ( ) ) ) {
23
+
24
+ // 1) collect proposals; for each position (key) compute the list of the elves
25
+ // who want to step there
26
+ var proposals = new Dictionary < Complex , List < Complex > > ( ) ;
32
27
33
- var state = Parse ( input ) ;
34
- while ( ! state . fixpoint ) {
35
- state = Step ( state ) ;
36
- steps ++ ;
37
- }
38
-
39
- return steps ;
40
- }
41
-
42
- State Step ( State state ) {
43
-
44
- bool occupied ( Complex pos ) => state . elves . Contains ( pos ) ;
45
- bool lonely ( Complex pos ) => directions . All ( dir => ! occupied ( pos + dir ) ) ;
46
-
47
- // elf proposes a postion if nobody is nearby in that direction
48
- bool proposes ( Complex elf , Complex dir ) => ExtendDir ( dir ) . All ( d => ! occupied ( elf + d ) ) ;
49
-
50
- // for each position (key) it has a list of the elves who wants to step there
51
- var proposals = new Dictionary < Complex , List < Complex > > ( ) ;
52
-
53
- foreach ( var elf in state . elves ) {
54
- if ( lonely ( elf ) ) {
55
- continue ;
56
- }
28
+ foreach ( var elf in elves ) {
29
+ var lonely = Directions . All ( dir => ! elves . Contains ( elf + dir ) ) ;
30
+ if ( lonely ) {
31
+ continue ;
32
+ }
57
33
58
- foreach ( var dir in state . directions ) {
59
- if ( proposes ( elf , dir ) ) {
60
- var pos = elf + dir ;
61
- if ( ! proposals . ContainsKey ( pos ) ) {
62
- proposals [ pos ] = new List < Complex > ( ) ;
34
+ foreach ( var dir in lookAround ) {
35
+
36
+ // elf proposes a postion if nobody stands in that direction
37
+ var proposes = ExtendDir ( dir ) . All ( d => ! elves . Contains ( elf + d ) ) ;
38
+ if ( proposes ) {
39
+ var pos = elf + dir ;
40
+ if ( ! proposals . ContainsKey ( pos ) ) {
41
+ proposals [ pos ] = new List < Complex > ( ) ;
42
+ }
43
+ proposals [ pos ] . Add ( elf ) ;
44
+ break ;
63
45
}
64
- proposals [ pos ] . Add ( elf ) ;
65
- break ;
66
46
}
67
47
}
68
- }
69
48
70
- // move elves, compute fixpoint flag
71
- var fixpoint = true ;
72
- foreach ( var p in proposals ) {
73
- var ( to , from ) = p ;
74
- if ( from . Count == 1 ) {
75
- state . elves . Remove ( from . Single ( ) ) ;
76
- state . elves . Add ( to ) ;
77
- fixpoint = false ;
49
+ // 2) move elves, compute fixpoint flag
50
+ fixpoint = true ;
51
+ foreach ( var p in proposals ) {
52
+ var ( to , from ) = p ;
53
+ if ( from . Count == 1 ) {
54
+ elves . Remove ( from . Single ( ) ) ;
55
+ elves . Add ( to ) ;
56
+ fixpoint = false ;
57
+ }
78
58
}
79
- }
80
59
81
- return new State (
82
- state . elves ,
83
- state . directions . Skip ( 1 ) . Concat ( state . directions . Take ( 1 ) ) . ToList ( ) ,
84
- fixpoint
85
- ) ;
60
+ yield return elves ;
61
+ }
86
62
}
87
63
88
- State Parse ( string input ) {
89
- var lines = input . Split ( "\n " ) ;
64
+ double Area ( HashSet < Complex > elves ) {
65
+ // smallest enclosing rectangle
66
+ var width = elves . Select ( p => p . Real ) . Max ( ) -
67
+ elves . Select ( p => p . Real ) . Min ( ) + 1 ;
90
68
91
- var elves = (
69
+ var height = elves . Select ( p => p . Imaginary ) . Max ( ) -
70
+ elves . Select ( p => p . Imaginary ) . Min ( ) + 1 ;
71
+
72
+ return width * height - elves . Count ;
73
+ }
74
+
75
+ HashSet < Complex > Parse ( string input ) {
76
+ var lines = input . Split ( "\n " ) ;
77
+ return (
92
78
from irow in Enumerable . Range ( 0 , lines . Length )
93
79
from icol in Enumerable . Range ( 0 , lines [ 0 ] . Length )
94
80
where lines [ irow ] [ icol ] == '#'
95
81
select new Complex ( icol , irow )
96
82
) . ToHashSet ( ) ;
97
-
98
- var directions = new List < Complex > ( ) { N , S , W , E } ;
99
-
100
- return new State ( elves , directions , fixpoint : false ) ;
101
83
}
102
84
103
85
/// -------
104
86
105
- record State ( HashSet < Complex > elves , List < Complex > directions , bool fixpoint ) ;
106
-
107
87
static Complex N = new Complex ( 0 , - 1 ) ;
108
88
static Complex E = new Complex ( 1 , 0 ) ;
109
89
static Complex S = new Complex ( 0 , 1 ) ;
@@ -113,7 +93,7 @@ record State(HashSet<Complex> elves, List<Complex> directions, bool fixpoint);
113
93
static Complex SE = S + E ;
114
94
static Complex SW = S + W ;
115
95
116
- static Complex [ ] directions = new [ ] { NW , N , NE , E , SE , S , SW , W } ;
96
+ static Complex [ ] Directions = new [ ] { NW , N , NE , E , SE , S , SW , W } ;
117
97
118
98
// Extends an ordinal position with its intercardinal neighbours
119
99
Complex [ ] ExtendDir ( Complex dir ) =>
0 commit comments