1
+ package com .macasaet ;
2
+
3
+ import java .util .*;
4
+ import java .util .stream .Collectors ;
5
+ import java .util .stream .Stream ;
6
+ import java .util .stream .StreamSupport ;
7
+
8
+ import org .junit .jupiter .api .Test ;
9
+
10
+ /**
11
+ * --- Day 8: Seven Segment Search ---
12
+ */
13
+ public class Day08 {
14
+
15
+ protected Stream <String > getInput () {
16
+ return StreamSupport
17
+ .stream (new LineSpliterator ("day-08.txt" ),
18
+ false );
19
+ }
20
+
21
+ public record Digit (List <Character > segments ) {
22
+ public Digit decode (final Map <Character , Character > map ) {
23
+ return new Digit (segments ().stream ()
24
+ .map (map ::get )
25
+ .collect (Collectors .toUnmodifiableList ()));
26
+ }
27
+
28
+ public int asInt () {
29
+ if (segments ().size () == 6 && hasSegments ('a' , 'b' , 'c' , 'e' , 'f' , 'g' )) { // FIXME use on/off mechanism
30
+ return 0 ;
31
+ } else if (segments ().size () == 2 && hasSegments ('c' , 'f' )) {
32
+ return 1 ;
33
+ } else if (segments ().size () == 5 && hasSegments ('a' , 'c' , 'd' , 'e' , 'g' )) {
34
+ return 2 ;
35
+ } else if (segments ().size () == 5 && hasSegments ('a' , 'c' , 'd' , 'f' , 'g' )) {
36
+ return 3 ;
37
+ } else if (segments ().size () == 4 && hasSegments ('b' , 'c' , 'd' , 'f' )) {
38
+ return 4 ;
39
+ } else if (segments ().size () == 5 && hasSegments ('a' , 'b' , 'd' , 'f' , 'g' )) {
40
+ return 5 ;
41
+ } else if (segments ().size () == 6 && hasSegments ('a' , 'b' , 'd' , 'e' , 'f' , 'g' )) {
42
+ return 6 ;
43
+ } else if (segments ().size () == 3 && hasSegments ('a' , 'c' , 'f' )) {
44
+ return 7 ;
45
+ } else if (segments ().size () == 7 && hasSegments ('a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' )) {
46
+ return 8 ;
47
+ } else if (segments ().size () == 6 && hasSegments ('a' , 'b' , 'c' , 'd' , 'f' , 'g' )) {
48
+ return 9 ;
49
+ }
50
+ throw new IllegalStateException ("Invalid Digit: " + this );
51
+ }
52
+
53
+ public boolean hasSegments (final char ... segments ) {
54
+ for (final var segment : segments ) {
55
+ if (!hasSegment (segment )) {
56
+ return false ;
57
+ }
58
+ }
59
+ return true ;
60
+ }
61
+
62
+ public boolean hasSegment (final char segment ) {
63
+ return segments ().contains (segment );
64
+ }
65
+
66
+ public static Digit parse (final String string ) {
67
+ final var array = string .toCharArray ();
68
+ final var list = new ArrayList <Character >(array .length );
69
+ for (final var c : array ) {
70
+ list .add (c );
71
+ }
72
+ return new Digit (Collections .unmodifiableList (list ));
73
+ }
74
+ }
75
+
76
+ public static record Entry (List <Digit > uniqueSignalPatterns , List <Digit > outputValue ) {
77
+
78
+ public int decodeOutput () {
79
+ final var map = buildDecodingMap ();
80
+ final StringBuilder builder = new StringBuilder ();
81
+ for (final var outputDigit : outputValue ()) {
82
+ final var decodedDigit = outputDigit .decode (map );
83
+ final int digit = decodedDigit .asInt ();
84
+ builder .append (digit );
85
+ }
86
+ final String stringInt = builder .toString ();
87
+ return Integer .parseInt (stringInt );
88
+ }
89
+
90
+ protected SortedSet <Character > getDigitSegmentsWithCount (final int n ) {
91
+ return uniqueSignalPatterns ().stream ()
92
+ .filter (digit -> digit .segments ().size () == n )
93
+ .findFirst ()
94
+ .get ()
95
+ .segments ()
96
+ .stream ()
97
+ .collect (TreeSet ::new , SortedSet ::add , SortedSet ::addAll );
98
+ }
99
+
100
+ protected Set <Digit > getDigitsWithCount (final int n ) { // TODO return stream
101
+ return uniqueSignalPatterns ()
102
+ .stream ()
103
+ .filter (digit -> digit .segments ().size () == n ).collect (Collectors .toUnmodifiableSet ());
104
+ }
105
+
106
+ public Map <Character , Character > buildDecodingMap () {
107
+ final var encodingMap = buildEncodingMap ();
108
+ final var result = new HashMap <Character , Character >();
109
+ for (final var entry : encodingMap .entrySet ()) {
110
+ result .put (entry .getValue (), entry .getKey ());
111
+ }
112
+ return Collections .unmodifiableMap (result );
113
+ }
114
+
115
+ public Map <Character , Character > buildEncodingMap () {
116
+ final var map = new HashMap <Character , Character >();
117
+ final var oneSegments = getDigitSegmentsWithCount (2 );
118
+ final var sevenSegments = getDigitSegmentsWithCount (3 );
119
+ final var fourSegments = getDigitSegmentsWithCount (4 );
120
+ final var eightSegments = getDigitSegmentsWithCount (7 );
121
+ final var aMapping = sevenSegments .stream ().filter (c -> !oneSegments .contains (c )).findFirst ().get ();
122
+ map .put ('a' , aMapping );
123
+
124
+ final var zeroSixNine = getDigitsWithCount (6 );
125
+ var zsnSegments = zeroSixNine .stream ().flatMap (digit -> digit .segments ().stream ()).collect (Collectors .toList ());
126
+ zsnSegments .removeIf (sevenSegments ::contains );
127
+ zsnSegments .removeIf (fourSegments ::contains );
128
+ final var sssMap = new HashMap <Character , Integer >();
129
+ for (final var c : zsnSegments ) {
130
+ sssMap .compute (c , (_key , old ) -> old == null ? 1 : old + 1 );
131
+ }
132
+ if (sssMap .size () != 2 ) {
133
+ throw new IllegalStateException ("More segments for 0, 6, 9 encountered: " + sssMap );
134
+ }
135
+ for (final var entry : sssMap .entrySet ()) {
136
+ if (entry .getValue () == 3 ) {
137
+ map .put ('g' , entry .getKey ());
138
+ } else if (entry .getValue () == 2 ) {
139
+ map .put ('e' , entry .getKey ());
140
+ } else {
141
+ throw new IllegalStateException ();
142
+ }
143
+ }
144
+
145
+ final var twoFiveThree = getDigitsWithCount (5 );
146
+ var tftSegments = twoFiveThree .stream ().flatMap (digit -> digit .segments .stream ()).collect (Collectors .toList ());
147
+ tftSegments .removeIf (sevenSegments ::contains );
148
+ tftSegments .removeIf (candidate -> candidate .equals (map .get ('e' )));
149
+ tftSegments .removeIf (candidate -> candidate .equals (map .get ('g' )));
150
+ final var tftCounts = new HashMap <Character , Integer >();
151
+ for (final var c : tftSegments ) {
152
+ tftCounts .compute (c , (_key , old ) -> old == null ? 1 : old + 1 );
153
+ }
154
+ for (final var entry : tftCounts .entrySet ()) {
155
+ if (entry .getValue () == 3 ) {
156
+ map .put ('d' , entry .getKey ());
157
+ } else if (entry .getValue () == 1 ) {
158
+ map .put ('b' , entry .getKey ());
159
+ } else {
160
+ throw new IllegalStateException ();
161
+ }
162
+ }
163
+
164
+ zsnSegments = zeroSixNine .stream ().flatMap (digit -> digit .segments ().stream ()).collect (Collectors .toList ());
165
+ zsnSegments .removeIf (candidate -> candidate .equals (map .get ('a' )));
166
+ zsnSegments .removeIf (candidate -> candidate .equals (map .get ('b' )));
167
+ zsnSegments .removeIf (candidate -> candidate .equals (map .get ('d' )));
168
+ zsnSegments .removeIf (candidate -> candidate .equals (map .get ('e' )));
169
+ zsnSegments .removeIf (candidate -> candidate .equals (map .get ('g' )));
170
+ final var zsnCounts = new HashMap <Character , Integer >();
171
+ for (final var c : zsnSegments ) {
172
+ zsnCounts .compute (c , (_key , old ) -> old == null ? 1 : old + 1 );
173
+ }
174
+ for (final var entry : zsnCounts .entrySet ()) {
175
+ if (entry .getValue () == 2 ) {
176
+ map .put ('c' , entry .getKey ());
177
+ } else if ( entry .getValue () == 3 ) {
178
+ map .put ('f' , entry .getKey ());
179
+ } else {
180
+ throw new IllegalStateException ();
181
+ }
182
+ }
183
+
184
+ return map ;
185
+ }
186
+
187
+ public static Entry parse (final String string ) {
188
+ final var components = string .split (" \\ | " );
189
+ final var uniqueSignalPatterns = components [0 ].split (" " );
190
+ final var outputValue = components [1 ].split (" " );
191
+
192
+ return new Entry (Arrays .stream (uniqueSignalPatterns )
193
+ .map (Digit ::parse )
194
+ .collect (Collectors .toUnmodifiableList ()),
195
+ Arrays .stream (outputValue )
196
+ .map (Digit ::parse )
197
+ .collect (Collectors .toUnmodifiableList ()));
198
+ }
199
+
200
+ }
201
+
202
+ @ Test
203
+ public final void part1 () {
204
+ final var result = getInput ()
205
+ .map (Entry ::parse )
206
+ .flatMap (entry -> entry .outputValue ().stream ())
207
+ .filter (digit -> {
208
+ final var segments = digit .segments ();
209
+ final var numSegments = segments .size ();
210
+ return numSegments == 2 || numSegments == 4 || numSegments == 3 || numSegments == 7 ;
211
+ })
212
+ .count ();
213
+ System .out .println ("Part 1: " + result );
214
+ }
215
+
216
+ @ Test
217
+ public final void part2 () {
218
+ final var result = getInput ()
219
+ .map (Entry ::parse )
220
+ .mapToInt (Entry ::decodeOutput ).sum ();
221
+
222
+ System .out .println ("Part 2: " + result );
223
+ }
224
+
225
+ }
0 commit comments