Skip to content

Commit

Permalink
Day 12
Browse files Browse the repository at this point in the history
  • Loading branch information
bqcuong committed Dec 13, 2024
1 parent f0ff527 commit aceae58
Show file tree
Hide file tree
Showing 6 changed files with 346 additions and 2 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ This repository contains my Java solutions for the [Advent of Code](https://adve
| 09 | [Disk Fragmenter](https://adventofcode.com/2024/day/9) | ⭐⭐ | [Code](src/main/java/net/bqc/aoc/year2024/Day09.java), [Test](src/test/java/net/bqc/aoc/year2024/Day09Test.java) |
| 10 | [Hoof It](https://adventofcode.com/2024/day/10) | ⭐⭐ | [Code](src/main/java/net/bqc/aoc/year2024/Day10.java), [Test](src/test/java/net/bqc/aoc/year2024/Day10Test.java) |
| 11 | [Plutonian Pebbles](https://adventofcode.com/2024/day/11) | ⭐⭐ | [Code](src/main/java/net/bqc/aoc/year2024/Day11.java), [Test](src/test/java/net/bqc/aoc/year2024/Day11Test.java) |
| 12 | [](https://adventofcode.com/2024/day/12) | | [Code](src/main/java/net/bqc/aoc/year2024/Day12.java), [Test](src/test/java/net/bqc/aoc/year2024/Day12Test.java) |
| 13 | [](https://adventofcode.com/2024/day/13) | | [Code](src/main/java/net/bqc/aoc/year2024/Day13.java), [Test](src/test/java/net/bqc/aoc/year2024/Day13Test.java) |
| 12 | [Garden Groups](https://adventofcode.com/2024/day/12) | ⭐⭐ | [Code](src/main/java/net/bqc/aoc/year2024/Day12.java), [Test](src/test/java/net/bqc/aoc/year2024/Day12Test.java) |
| 13 | [Claw Contraption](https://adventofcode.com/2024/day/13) | | [Code](src/main/java/net/bqc/aoc/year2024/Day13.java), [Test](src/test/java/net/bqc/aoc/year2024/Day13Test.java) |
| 14 | [](https://adventofcode.com/2024/day/14) | | [Code](src/main/java/net/bqc/aoc/year2024/Day14.java), [Test](src/test/java/net/bqc/aoc/year2024/Day14Test.java) |
| 15 | [](https://adventofcode.com/2024/day/15) | | [Code](src/main/java/net/bqc/aoc/year2024/Day15.java), [Test](src/test/java/net/bqc/aoc/year2024/Day15Test.java) |
| 16 | [](https://adventofcode.com/2024/day/16) | | [Code](src/main/java/net/bqc/aoc/year2024/Day16.java), [Test](src/test/java/net/bqc/aoc/year2024/Day16Test.java) |
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/net/bqc/aoc/utils/Pos.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,19 @@ public Pos(int x, int y) {
this.x = x;
this.y = y;
}

@Override
public int hashCode() {
return Integer.hashCode(x) ^ Integer.hashCode(y);
}

@Override
public boolean equals(Object obj) {
return x == ((Pos) obj).x && y == ((Pos) obj).y;
}

@Override
public String toString() {
return String.format("[%d, %d]", x, y);
}
}
136 changes: 136 additions & 0 deletions src/main/java/net/bqc/aoc/year2024/Day12.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package net.bqc.aoc.year2024;

import net.bqc.aoc.Solution;
import net.bqc.aoc.utils.Array2DUtils;
import net.bqc.aoc.utils.Pos;

import java.util.*;
import java.util.concurrent.atomic.AtomicLong;

public class Day12 extends Solution {

enum Direction {
NORTH(-1, 0), EAST(0, 1), SOUTH(1, 0), WEST(0, -1);

final int dx, dy;

Direction(int dx, int dy) {
this.dx = dx;
this.dy = dy;
}
}

private char[][] plants;
private int[][] fences;

@Override
public long solve(Part part, List<String> inputLines) {
super.solve(part, inputLines);

this.plants = Array2DUtils.readAsMatrix(inputLines);
this.fences = computeIndividualPlantFence();

long price = 0;
for (int i = 0; i < plants.length; i++) {
for (int j = 0; j < fences[i].length; j++) {
if (fences[i][j] != 0) {
price += bfsAndComputePlantRegionPrice(new Pos(i, j));
}
}
}
return price;
}

private long bfsAndComputePlantRegionPrice(Pos startPos) {
List<Pos> visited = new ArrayList<>();
Queue<Pos> queue = new LinkedList<>();
queue.add(startPos);
visited.add(startPos);

while (!queue.isEmpty()) {
Pos pos = queue.remove();
for (Direction d : EnumSet.allOf(Direction.class)) {
int newX = pos.x + d.dx;
int newY = pos.y + d.dy;
Pos newPos = new Pos(newX, newY);

if (newX < 0 || newX >= plants.length || newY < 0 || newY >= plants[0].length) continue;
if (plants[newX][newY] != plants[pos.x][pos.y] || visited.contains(newPos)) continue;

queue.add(newPos);
visited.add(newPos);
}
}
return (isPart2() ? getSides(visited) : getPerimeter(visited)) * visited.size();
}

private long getSides(List<Pos> visited) {
// the number of sizes is the number of corners of a region
visited.sort((o1, o2) -> o1.x * 10000 + o1.y - o2.x * 10000 - o2.y); // assume the input has maximum of 10000 columns

long corners = 0;
int m = plants.length;
int n = plants[0].length;
for (Pos pos : visited) {
int x = pos.x, y = pos.y;

// convex corners
if (fences[x][y] == 4) corners += 4; // single-slot plant 4 corners
else if (fences[x][y] == 3) corners += 2; // 2 corners
else if (fences[x][y] == 2) {
if (topFence(x, y) && rightFence(x, y) // NE
|| bottomFence(x, y) && rightFence(x, y) // SE
|| bottomFence(x, y) && leftFence(x, y) // SW
|| topFence(x, y) && leftFence(x, y)) { // NW
corners++;
}
}

// concave corners
if (x - 1 >= 0 && y - 1 >= 0 && plants[x-1][y] == plants[x][y] && plants[x][y-1] == plants[x][y] && plants[x-1][y-1] != plants[x][y]) corners++; // NW
if (x - 1 >= 0 && y + 1 < n && plants[x-1][y] == plants[x][y] && plants[x][y+1] == plants[x][y] && plants[x-1][y+1] != plants[x][y]) corners++; // NE
if (x + 1 < m && y + 1 < n && plants[x+1][y] == plants[x][y] && plants[x][y+1] == plants[x][y] && plants[x+1][y+1] != plants[x][y]) corners++; // SE
if (x + 1 < m && y - 1 >= 0 && plants[x+1][y] == plants[x][y] && plants[x][y-1] == plants[x][y] && plants[x+1][y-1] != plants[x][y]) corners++; // SW
}
visited.forEach(pos -> fences[pos.x][pos.y] = 0);
return corners;
}

private long getPerimeter(List<Pos> visited) {
AtomicLong perimeter = new AtomicLong();
visited.forEach(pos -> {
perimeter.addAndGet(fences[pos.x][pos.y]);
fences[pos.x][pos.y] = 0;
});
return perimeter.get();
}

private int[][] computeIndividualPlantFence() {
int[][] fences = new int[plants.length][plants[0].length];
for (int i = 0; i < plants.length; i++) {
for (int j = 0; j < plants[0].length; j++) {
fences[i][j] += topFence(i, j) ? 1 : 0;
fences[i][j] += bottomFence(i, j) ? 1 : 0;
fences[i][j] += leftFence(i, j) ? 1 : 0;
fences[i][j] += rightFence(i, j) ? 1 : 0;
}
}
return fences;
}

private boolean topFence(int i, int j) {
return i == 0 || plants[i - 1][j] != plants[i][j];
}

private boolean bottomFence(int i, int j) {
return i == plants.length - 1 || plants[i + 1][j] != plants[i][j];
}

private boolean leftFence(int i, int j) {
return j == 0 || plants[i][j - 1] != plants[i][j];
}

private boolean rightFence(int i, int j) {
return j == plants[0].length - 1 || plants[i][j + 1] != plants[i][j];
}
}
49 changes: 49 additions & 0 deletions src/test/java/net/bqc/aoc/year2024/Day12Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package net.bqc.aoc.year2024;

import net.bqc.aoc.AbstractTest;
import net.bqc.aoc.Solution;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.List;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class Day12Test extends AbstractTest {

private Day12 solution;

@BeforeAll
static void setUp() {
INPUT_PATH = "year2024/Day12_Input.txt";
SAMPLE_INPUT_PATH = "year2024/Day12_SampleInput.txt";
}

@BeforeEach
void init() {
solution = new Day12();
}

@ParameterizedTest
@MethodSource("inputDataSource")
void testSolver(Solution.Part part, List<String> inputLines, long expectedSum) {
long computedSum = solution.solve(part, inputLines);
assertEquals(expectedSum, computedSum);
}

static Stream<Arguments> inputDataSource() {
List<String> sampleInputLines = getSampleInput();
List<String> inputLines = getInput();
return Stream.of(
Arguments.of(Solution.Part.ONE, sampleInputLines, 140),
Arguments.of(Solution.Part.ONE, inputLines, 1402544),

Arguments.of(Solution.Part.TWO, sampleInputLines, 80),
Arguments.of(Solution.Part.TWO, inputLines, 862486)
);
}
}
Loading

0 comments on commit aceae58

Please sign in to comment.