1
+ package com .macasaet ;
2
+
3
+ import java .io .IOException ;
4
+ import java .util .ArrayList ;
5
+ import java .util .Arrays ;
6
+ import java .util .Collections ;
7
+ import java .util .HashMap ;
8
+ import java .util .Map .Entry ;
9
+ import java .util .stream .Collectors ;
10
+ import java .util .stream .Stream ;
11
+ import java .util .stream .StreamSupport ;
12
+
13
+ public class Day24 {
14
+
15
+ public static void main (final String [] args ) throws IOException {
16
+ final Address origin = new Address (0 , 0 , 0 );
17
+ final var lobbyFloor = new HashMap <Address , Integer >();
18
+ try (var spliterator = new LineSpliterator (Day22 .class .getResourceAsStream ("/day-24-input.txt" ))) {
19
+ StreamSupport .stream (spliterator , false )
20
+ .map (String ::toCharArray )
21
+ .map (array -> {
22
+ int i = 0 ;
23
+ var directions = new ArrayList <Direction >(array .length );
24
+ while (i < array .length ) {
25
+ if (i < array .length - 1 ) { // check if there are at least 2 chars available
26
+ final var twoLetterDirection = "" + array [i ] + array [i + 1 ];
27
+ // check if two chars is a valid direction
28
+ if ("se" .equalsIgnoreCase (twoLetterDirection )
29
+ || "sw" .equalsIgnoreCase (twoLetterDirection )
30
+ || "nw" .equalsIgnoreCase (twoLetterDirection )
31
+ || "ne" .equalsIgnoreCase (twoLetterDirection )) {
32
+ directions .add (Direction .forAbbreviation (twoLetterDirection ));
33
+ i = i + 2 ;
34
+ continue ;
35
+ }
36
+ }
37
+ final var oneLetterDirection = "" + array [i ];
38
+ if ("e" .equalsIgnoreCase (oneLetterDirection ) || "w" .equalsIgnoreCase (oneLetterDirection )) {
39
+ directions .add (Direction .forAbbreviation (oneLetterDirection ));
40
+ i = i + 1 ;
41
+ continue ;
42
+ }
43
+ throw new IllegalArgumentException ("Invalid direction: " + oneLetterDirection );
44
+ }
45
+ return Collections .unmodifiableList (directions );
46
+ }).map (directions -> {
47
+ Address cursor = origin ;
48
+ for (final var direction : directions ) {
49
+ cursor = direction .travel (cursor );
50
+ }
51
+ return cursor ;
52
+ }).forEach (address -> lobbyFloor .merge (address , 1 , (old , def ) -> old + 1 ));
53
+ }
54
+ final int blackTiles = lobbyFloor .values ().stream ().mapToInt (count -> count % 2 ).sum ();
55
+ System .out .println ("Part 1: " + blackTiles );
56
+
57
+ for (int i = 1 ; i <= 100 ; i ++) {
58
+ final var tasks = lobbyFloor .entrySet ().stream ().flatMap (entry -> {
59
+ // get the mapped tile
60
+ // as well as unmapped (white) adjacent tiles
61
+ final var from = entry .getKey ();
62
+ return Stream .concat (Stream .of (entry ), Arrays .stream (Direction .values ()).flatMap (direction -> {
63
+ final var neighbour = direction .travel (from );
64
+ if (!lobbyFloor .containsKey (neighbour )) {
65
+ // neighbour has never been visited, create a virtual entry for them
66
+ return Stream .of (new Entry <Address , Integer >() {
67
+ public Address getKey () {
68
+ return neighbour ;
69
+ }
70
+
71
+ public Integer getValue () {
72
+ return 0 ;
73
+ }
74
+
75
+ public Integer setValue (Integer value ) {
76
+ throw new UnsupportedOperationException ();
77
+ }
78
+ });
79
+ }
80
+ return Stream .empty ();
81
+ }));
82
+ }).map (entry -> { // NB: this might not be a real tile
83
+ final var address = entry .getKey ();
84
+ final int adjacentBlackTiles = Arrays .stream (Direction .values ())
85
+ .map (direction -> direction .travel (address ))
86
+ .mapToInt (neighbour -> {
87
+ final Integer neighbouringCount = lobbyFloor .get (neighbour );
88
+ return neighbouringCount != null && neighbouringCount % 2 == 1 ? 1 : 0 ;
89
+ }).sum ();
90
+ if (entry .getValue () % 2 == 1 && (adjacentBlackTiles == 0 || adjacentBlackTiles > 2 )) {
91
+ // Any black tile with zero or more than 2 black tiles immediately adjacent to it is flipped to white.
92
+ return (Runnable ) () -> lobbyFloor .put (address , 0 );
93
+ } else if (entry .getValue () % 2 == 0 && adjacentBlackTiles == 2 ) {
94
+ // Any white tile with exactly 2 black tiles immediately adjacent to it is flipped to black.
95
+ return (Runnable ) () -> lobbyFloor .put (address , 1 );
96
+ }
97
+ return (Runnable ) () -> {
98
+ };
99
+ }).collect (Collectors .toUnmodifiableList ());
100
+ for (final var task : tasks ) {
101
+ task .run ();
102
+ }
103
+ }
104
+ final int count = lobbyFloor .values ().stream ().mapToInt (value -> value % 2 ).sum ();
105
+ System .out .println ("Part 2: " + count );
106
+ }
107
+
108
+ public enum Direction {
109
+ EAST ("e" , 1 , -1 , 0 ),
110
+ SOUTH_EAST ("se" , 0 , -1 , 1 ),
111
+ SOUTH_WEST ("sw" , -1 , 0 , 1 ),
112
+ WEST ("w" , -1 , 1 , 0 ),
113
+ NORTH_WEST ("nw" , 0 , 1 , -1 ),
114
+ NORTH_EAST ("ne" , 1 , 0 , -1 );
115
+
116
+ private final String abbreviation ;
117
+ private final int xOffset ;
118
+ private final int yOffset ;
119
+ private final int zOffset ;
120
+
121
+ Direction (final String abbreviation , final int xOffset , final int yOffset , final int zOffset ) {
122
+ this .abbreviation = abbreviation ;
123
+ this .xOffset = xOffset ;
124
+ this .yOffset = yOffset ;
125
+ this .zOffset = zOffset ;
126
+ }
127
+
128
+ public static Direction forAbbreviation (final String abbreviation ) {
129
+ for (final var candidate : values ()) {
130
+ if (candidate .abbreviation .equalsIgnoreCase (abbreviation )) {
131
+ return candidate ;
132
+ }
133
+ }
134
+ throw new IllegalArgumentException ("Invalid direction: " + abbreviation );
135
+ }
136
+
137
+ public Address travel (final Address from ) {
138
+ return new Address (from .x + xOffset , from .y + yOffset , from .z + zOffset );
139
+ }
140
+
141
+ }
142
+
143
+ public static class Address {
144
+
145
+ private final int x ;
146
+ private final int y ;
147
+ private final int z ;
148
+
149
+ public Address (final int x , final int y , final int z ) {
150
+ this .x = x ;
151
+ this .y = y ;
152
+ this .z = z ;
153
+ }
154
+
155
+ public int hashCode () {
156
+ int retval = 0 ;
157
+ retval = 31 * retval + x ;
158
+ retval = 31 * retval + y ;
159
+ retval = 31 * retval + z ;
160
+ return retval ;
161
+ }
162
+
163
+ public boolean equals (final Object o ) {
164
+ if (this == o ) {
165
+ return true ;
166
+ } else if (o == null ) {
167
+ return false ;
168
+ }
169
+ try {
170
+ final Address other = (Address ) o ;
171
+ return x == other .x && y == other .y && z == other .z ;
172
+ } catch (final ClassCastException cce ) {
173
+ return false ;
174
+ }
175
+ }
176
+
177
+ }
178
+
179
+ }
0 commit comments