1+ import static com .github .pareronia .aoc .IntegerSequence .Range .range ;
2+
3+ import java .util .Arrays ;
4+ import java .util .HashMap ;
5+ import java .util .HashSet ;
6+ import java .util .Iterator ;
7+ import java .util .List ;
8+ import java .util .Map ;
9+ import java .util .Set ;
10+ import java .util .function .ToIntBiFunction ;
11+
12+ import com .github .pareronia .aoc .Grid .Cell ;
13+ import com .github .pareronia .aoc .Utils ;
14+ import com .github .pareronia .aoc .geometry .Direction ;
15+ import com .github .pareronia .aoc .graph .BFS ;
16+ import com .github .pareronia .aoc .solution .Sample ;
17+ import com .github .pareronia .aoc .solution .Samples ;
18+ import com .github .pareronia .aoc .solution .SolutionBase ;
19+
20+ public final class AoC2024_12 extends SolutionBase <List <String >, Integer , Integer > {
21+
22+ private static final List <List <Direction >> CORNER_DIRS = List .of (
23+ List .of (Direction .LEFT_AND_UP , Direction .LEFT , Direction .UP ),
24+ List .of (Direction .RIGHT_AND_UP , Direction .RIGHT , Direction .UP ),
25+ List .of (Direction .RIGHT_AND_DOWN , Direction .RIGHT , Direction .DOWN ),
26+ List .of (Direction .LEFT_AND_DOWN , Direction .LEFT , Direction .DOWN )
27+ );
28+
29+ private AoC2024_12 (final boolean debug ) {
30+ super (debug );
31+ }
32+
33+ public static AoC2024_12 create () {
34+ return new AoC2024_12 (false );
35+ }
36+
37+ public static AoC2024_12 createDebug () {
38+ return new AoC2024_12 (true );
39+ }
40+
41+ @ Override
42+ protected List <String > parseInput (final List <String > inputs ) {
43+ return inputs ;
44+ }
45+
46+ private int solve (
47+ final List <String > input ,
48+ final ToIntBiFunction <Cell , Set <Cell >> count
49+ ) {
50+ final Map <Character , Set <Cell >> plotsByPlant = new HashMap <>();
51+ range (input .size ()).forEach (r ->
52+ range (input .get (r ).length ()).forEach (c ->
53+ plotsByPlant
54+ .computeIfAbsent (input .get (r ).charAt (c ), k -> new HashSet <>())
55+ .add (Cell .at (r , c ))));
56+ final Iterator <Set <Cell >> regions = new Iterator <>() {
57+ final Iterator <Character > keys = plotsByPlant .keySet ().iterator ();
58+ char key = keys .next ();
59+ Set <Cell > allPlotsWithPlant = plotsByPlant .get (key );
60+
61+ @ Override
62+ public Set <Cell > next () {
63+ if (allPlotsWithPlant .isEmpty ()) {
64+ key = keys .next ();
65+ allPlotsWithPlant = plotsByPlant .get (key );
66+ }
67+ final Set <Cell > region = BFS .floodFill (
68+ allPlotsWithPlant .iterator ().next (),
69+ cell -> cell .capitalNeighbours ()
70+ .filter (allPlotsWithPlant ::contains ));
71+ allPlotsWithPlant .removeAll (region );
72+ return region ;
73+ }
74+
75+ @ Override
76+ public boolean hasNext () {
77+ return !allPlotsWithPlant .isEmpty () || keys .hasNext ();
78+ }
79+ };
80+ return Utils .stream (regions )
81+ .mapToInt (region -> region .stream ()
82+ .mapToInt (plot -> count .applyAsInt (plot , region ) * region .size ())
83+ .sum ())
84+ .sum ();
85+ }
86+
87+ @ Override
88+ public Integer solvePart1 (final List <String > input ) {
89+ final ToIntBiFunction <Cell , Set <Cell >> countEdges
90+ = (plot , region ) -> (4 - (int ) plot .capitalNeighbours ()
91+ .filter (region ::contains )
92+ .count ());
93+ return solve (input , countEdges );
94+ }
95+
96+ @ Override
97+ public Integer solvePart2 (final List <String > input ) {
98+ final Set <int []> matches = Set .of (
99+ new int [] {0 , 0 , 0 }, new int [] {1 , 0 , 0 }, new int [] {0 , 1 , 1 });
100+ final ToIntBiFunction <Cell , Set <Cell >> countCorners
101+ = (plot , region ) -> ((int ) CORNER_DIRS .stream ()
102+ .filter (d -> {
103+ final int [] test = range (3 ).intStream ()
104+ .map (i -> region .contains (plot .at (d .get (i ))) ? 1 : 0 )
105+ .toArray ();
106+ return matches .stream ().anyMatch (m -> Arrays .equals (m , test ));
107+ })
108+ .count ());
109+ return solve (input , countCorners );
110+ }
111+
112+ @ Override
113+ @ Samples ({
114+ @ Sample (method = "part1" , input = TEST1 , expected = "140" ),
115+ @ Sample (method = "part1" , input = TEST2 , expected = "772" ),
116+ @ Sample (method = "part1" , input = TEST3 , expected = "1930" ),
117+ @ Sample (method = "part2" , input = TEST1 , expected = "80" ),
118+ @ Sample (method = "part2" , input = TEST2 , expected = "436" ),
119+ @ Sample (method = "part2" , input = TEST3 , expected = "1206" ),
120+ @ Sample (method = "part2" , input = TEST4 , expected = "236" ),
121+ @ Sample (method = "part2" , input = TEST5 , expected = "368" ),
122+ })
123+ public void samples () {
124+ }
125+
126+ public static void main (final String [] args ) throws Exception {
127+ AoC2024_12 .create ().run ();
128+ }
129+
130+ private static final String TEST1 = """
131+ AAAA
132+ BBCD
133+ BBCC
134+ EEEC
135+ """ ;
136+ private static final String TEST2 = """
137+ OOOOO
138+ OXOXO
139+ OOOOO
140+ OXOXO
141+ OOOOO
142+ """ ;
143+ private static final String TEST3 = """
144+ RRRRIICCFF
145+ RRRRIICCCF
146+ VVRRRCCFFF
147+ VVRCCCJFFF
148+ VVVVCJJCFE
149+ VVIVCCJJEE
150+ VVIIICJJEE
151+ MIIIIIJJEE
152+ MIIISIJEEE
153+ MMMISSJEEE
154+ """ ;
155+ private static final String TEST4 = """
156+ EEEEE
157+ EXXXX
158+ EEEEE
159+ EXXXX
160+ EEEEE
161+ """ ;
162+ private static final String TEST5 = """
163+ AAAAAA
164+ AAABBA
165+ AAABBA
166+ ABBAAA
167+ ABBAAA
168+ AAAAAA
169+ """ ;
170+ }
0 commit comments