Skip to content

Commit

Permalink
Solve day 14 part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
Flashky committed Dec 14, 2023
1 parent 60b9f38 commit c5dfee3
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 97 deletions.
6 changes: 3 additions & 3 deletions src/main/java/com/adventofcode/flashk/day14/Direction.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
@Getter
public enum Direction {

NORTH(Vector2.up(), Vector2.down()),
NORTH(Vector2.down(),Vector2.up()),
WEST(Vector2.left(), Vector2.right()),
SOUTH(Vector2.down(), Vector2.up()),
SOUTH(Vector2.up(),Vector2.down()),
EAST(Vector2.right(), Vector2.left());

private final Vector2 move;
Expand All @@ -19,4 +19,4 @@ public enum Direction {
this.rollback = rollback;
}

}
}
Original file line number Diff line number Diff line change
@@ -1,140 +1,154 @@
package com.adventofcode.flashk.day14;

import com.adventofcode.flashk.common.Collider2D;
import com.adventofcode.flashk.common.Vector2;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ParabolicReflectorDish {

private static final char ROUNDED_ROCK = 'O';
private static final char CUBED_ROCK = '#';
private static final char EMPTY = '.';

private int rows;
private int cols;
private List<Collider2D> roundedRocks = new ArrayList<>();
private List<Collider2D> cubedRocks = new ArrayList<>();
private final char[][] map;
private final int rows;
private final int cols;

public ParabolicReflectorDish(List<String> inputs) {

rows = inputs.size();
cols = inputs.get(0).length();

int y = rows;
for(String input : inputs) {

char[] values = input.toCharArray();
for(int i = 0; i < values.length; i++) {
int x = i+1;
if(values[i] == ROUNDED_ROCK) {
roundedRocks.add(new Collider2D(new Vector2(x,y)));
} else if(values[i] == CUBED_ROCK) {
cubedRocks.add(new Collider2D(new Vector2(x,y)));
}
}
y--;
}
private final Map<String,Long> snapshots = new HashMap<>();
private final List<String> orderedSnapshots = new ArrayList<>();

public ParabolicReflectorDish(char[][] map) {
this.map = map;
this.rows = map.length;
this.cols = map[0].length;
}

public long solveA() {
move(Direction.NORTH);
paint(0);
return countRocks();
}

public long solveB(int cycles) {
System.out.println();
System.out.println("Cycle = " + 0 + " | Value = " + countRocks());
for(int i = 0; i < cycles; i++) {

int currentCycle = 0;
String snapshot;
boolean cycleFound = false;
do {

move(Direction.NORTH);
move(Direction.WEST);
move(Direction.SOUTH);
move(Direction.EAST);
System.out.println("Cycle = " + (i+1) + " | Value = " + countRocks());
//paint(i+1);
}
snapshot = createSnapshot();

if(snapshots.containsKey(snapshot)) {
cycleFound = true;
} else {
snapshots.put(snapshot, countRocks());
orderedSnapshots.add(snapshot);
currentCycle++;
}

// TODO realmente no podemos bruteforcear
// TODO Check: https://github.com/Flashky/advent-of-code-2022/tree/master/src/main/java/com/adventofcode/flashk/day17
// TODO buscar un patrón de repetición tal que una vez encontrado ese patrón, podamos comprobar cuantas veces
// TODO Se vuelve a repetir de aquí a un millón de ciclos
} while(!cycleFound && (currentCycle < cycles));

// Problema 1:
// ¿Cuando verificar repetición de ciclo?
// - Hay que tiltear primero en las cuatro direcciones.
// Una vez tilteado, hay que comprobar si el estado actual del tablero es un estado conocido.
// Cycle detected!
int snapshotsSize = orderedSnapshots.size();
int nonPatternItems = removeItemsNotInCycle(snapshot);
int itemsPerPattern = snapshotsSize - nonPatternItems;
int patternItems = cycles - nonPatternItems;
int position = (patternItems-1) % itemsPerPattern;

return countRocks();
String item = orderedSnapshots.get(position);

return snapshots.get(item);
}

private String createSnapshot() {
StringBuilder snapshotBuilder = new StringBuilder();
for(int i = 0; i < rows; i++) {
snapshotBuilder.append(new String(map[i]));
}
return snapshotBuilder.toString();
}

private int removeItemsNotInCycle(String snapshotCycle) {
int count = 0;
while(!snapshotCycle.equals(orderedSnapshots.get(0))) {
orderedSnapshots.remove(0);
count++;
}
return count;
}
private long countRocks() {
long result = 0;
int rowCounter = rows;
for(int row = 0; row < rows; row++) {
for(int col = 0; col < cols; col++) {
if(map[row][col] == ROUNDED_ROCK) {
result += rowCounter;
}
}
rowCounter--;
}
return result;
}

private void move(Direction direction) {
boolean move;
do {
move = tilt(direction);
} while (move);
}

private long countRocks() {
return roundedRocks.stream().map(r -> r.getStart().getY()).reduce(0, Integer::sum);
private boolean tilt(Direction direction) {
boolean move = false;
for(int row = 0; row < rows; row++) {
for(int col = 0; col < cols; col++) {
if(canMove(row, col, direction.getMove())){
map[row][col] = EMPTY;
map[direction.getMove().getY()+row][direction.getMove().getX()+col] = ROUNDED_ROCK;
move = true;
}
}
}
return move;
}

private boolean tilt(Direction direction) {
private boolean canMove(int row, int col, Vector2 direction) {
if(map[row][col] != ROUNDED_ROCK) {
return false;
}

Vector2 directionVector = direction.getMove();
Vector2 directionVectorRollback = direction.getRollback();
int expectedRow = direction.getY() + row;
int expectedCol = direction.getX() + col;

boolean movement = false;
for(Collider2D roundedRock : roundedRocks) {
if(expectedRow < 0 || expectedRow >= rows) {
return false;
}

if(isValidRock(roundedRock, direction)) {
roundedRock.transform(directionVector);
if(collidesWithAnyRock(roundedRock)) {
roundedRock.transform(directionVectorRollback);
} else {
movement = true;
}
}
if(expectedCol < 0 || expectedCol >= cols) {
return false;
}
return movement;
}

private boolean isValidRock(Collider2D roundedRock, Direction direction) {
return switch (direction) {
case NORTH -> roundedRock.getStart().getY() != rows;
case WEST -> roundedRock.getStart().getX() > 1;
case SOUTH -> roundedRock.getStart().getY() > 1;
case EAST -> roundedRock.getStart().getX() != cols;
};
}
private boolean collidesWithAnyRock(Collider2D roundedRock) {
if(roundedRocks.stream().filter(r -> r != roundedRock).anyMatch(r -> r.collidesWith(roundedRock))) {
return true;
if(map[expectedRow][expectedCol] != EMPTY) {
return false;
}

return cubedRocks.stream().anyMatch(c -> c.collidesWith(roundedRock));
return true;
}

private void paint(int cycle) {

System.out.println();
for(int y = 10; y > 0; y--) {
for(int x = 1; x <= cols; x++) {
int finalX = x;
int finalY = y;
if(roundedRocks.stream().anyMatch(c -> (c.getStart().getX() == finalX) && (c.getStart().getY() == finalY))){
System.out.print(ROUNDED_ROCK);
} else if(cubedRocks.stream().anyMatch(c -> (c.getStart().getX() == finalX) && (c.getStart().getY() == finalY))){
System.out.print(CUBED_ROCK);
} else {
System.out.print(EMPTY);
}
for(int row = 0; row < rows; row++) {
for(int col = 0; col < cols; col++) {
System.out.print(map[row][col]);
}
System.out.println();
}
System.out.println();
}

}
}
2 changes: 1 addition & 1 deletion src/main/java/com/adventofcode/flashk/day14/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Day 14
# Day 14: Parabolic Reflector Dish

[https://adventofcode.com/2023/day/14](https://adventofcode.com/2023/day/14)
22 changes: 11 additions & 11 deletions src/test/java/com/adventofcode/flashk/day14/Day14Test.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package com.adventofcode.flashk.day14;

import java.util.List;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
Expand Down Expand Up @@ -32,7 +29,7 @@ public static void beforeAll() {
Timer.printHeader(TestDisplayName.DAY_14);
}


@Test
@Order(1)
@Tag(TestTag.PART_ONE)
Expand All @@ -43,14 +40,13 @@ public void testSolvePart1Sample() {
System.out.print("1 | sample | ");

// Read input file
List<String> inputs = Input.readStringLines(INPUT_FOLDER, TestFilename.INPUT_FILE_SAMPLE);
char[][] inputs = Input.read2DCharArray(INPUT_FOLDER, TestFilename.INPUT_FILE_SAMPLE);

ParabolicReflectorDish parabolicReflectorDish = new ParabolicReflectorDish(inputs);
long result = parabolicReflectorDish.solveA();

assertEquals(136, result);


}

@Test
Expand All @@ -63,7 +59,7 @@ public void testSolvePart1Input() {
System.out.print("1 | input | ");

// Read input file
List<String> inputs = Input.readStringLines(INPUT_FOLDER, TestFilename.INPUT_FILE);
char[][] inputs = Input.read2DCharArray(INPUT_FOLDER, TestFilename.INPUT_FILE);

ParabolicReflectorDish parabolicReflectorDish = new ParabolicReflectorDish(inputs);
long result = parabolicReflectorDish.solveA();
Expand All @@ -81,12 +77,11 @@ public void testSolvePart2Sample() {
System.out.print("2 | sample | ");

// Read input file
List<String> inputs = Input.readStringLines(INPUT_FOLDER, TestFilename.INPUT_FILE_SAMPLE);
char[][] inputs = Input.read2DCharArray(INPUT_FOLDER, TestFilename.INPUT_FILE_SAMPLE);

ParabolicReflectorDish parabolicReflectorDish = new ParabolicReflectorDish(inputs);
long result = parabolicReflectorDish.solveB(30);
long result = parabolicReflectorDish.solveB(1000000000);

System.out.println("R: "+ result);
assertEquals(64, result);
}

Expand All @@ -100,7 +95,12 @@ public void testSolvePart2Input() {
System.out.print("2 | input | ");

// Read input file
List<String> inputs = Input.readStringLines(INPUT_FOLDER, TestFilename.INPUT_FILE);
char[][] inputs = Input.read2DCharArray(INPUT_FOLDER, TestFilename.INPUT_FILE);

ParabolicReflectorDish parabolicReflectorDish = new ParabolicReflectorDish(inputs);
long result = parabolicReflectorDish.solveB(1000000000);

assertEquals(89845, result);

}

Expand Down

0 comments on commit c5dfee3

Please sign in to comment.