Skip to content

Commit

Permalink
Solve day 21 puzzle part 1
Browse files Browse the repository at this point in the history
  • Loading branch information
Flashky committed Dec 31, 2023
1 parent 91b2224 commit 7751bfc
Showing 3 changed files with 83 additions and 147 deletions.
35 changes: 35 additions & 0 deletions src/main/java/com/adventofcode/flashk/day21/Cell.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.adventofcode.flashk.day21;

import lombok.Getter;
import lombok.Setter;

@Getter
public class Cell {

private static final char ROCK = '#';
private static final char GARDEN_PLOT = '.';

private final int row;
private final int col;
private final char value;

@Setter
private boolean visited = false;

@Setter
private int step = 0;

public Cell(int row, int col, char value) {
this.row = row;
this.col = col;
this.value = (value == 'S') ? GARDEN_PLOT : value;
}

public boolean isRock() {
return value == ROCK;
}

public boolean isEven() {
return step % 2 == 0;
}
}
185 changes: 45 additions & 140 deletions src/main/java/com/adventofcode/flashk/day21/StepCounter.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.adventofcode.flashk.day21;

import com.adventofcode.flashk.common.Vector2;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;

import java.util.ArrayDeque;
import java.util.Deque;
@@ -11,163 +9,74 @@

public class StepCounter {

private static final char ROCK = '#';
private static final char GARDEN_PLOT = '.';
private static final char REACH_TILE = '0';
private final Cell[][] map;

private char[][] map;
private char[][] solutionsMap;
private final int rows;
private final int cols;

private int rows;
private int cols;
private int reachableTiles = 0;
private Vector2 start;
private Cell start;

public StepCounter(char[][] inputs) {

rows = inputs.length;
cols = inputs[0].length;
map = inputs;

// Find starting position

solutionsMap = new char[rows][];
// Initialize map
map = new Cell[rows][];
for(int row = 0; row < rows; row++) {

solutionsMap[row] = new char[cols];

map[row] = new Cell[cols];
for(int col = 0; col < cols; col++) {
if(map[row][col] == 'S') {
start = new Vector2(col, row);
map[row][col] = GARDEN_PLOT;
map[row][col] = new Cell(row, col, inputs[row][col]);
if(inputs[row][col] == 'S') {
start = map[row][col];
}
solutionsMap[row][col] = map[row][col];

}
}
}

public long solveA(int totalSteps) {

// TODO, este algoritmo vale para datos muy pequeños, pero en este caso, la ramificación es muy amplia.
Deque<Pair<Vector2,Integer>> queue = new ArrayDeque<>();
queue.add(ImmutablePair.of(start,0));

while(!queue.isEmpty() && queue.peek().getRight() < totalSteps) {
Pair<Vector2,Integer> positionAndSteps = queue.poll();

Vector2 position = positionAndSteps.getLeft();
int steps = positionAndSteps.getRight();
Deque<Cell> queue = new ArrayDeque<>();

map[position.getY()][position.getX()] = GARDEN_PLOT;

Set<Pair<Vector2,Integer>> adjacentTiles = getAdjacentTiles(position, steps);
for(Pair<Vector2,Integer> positionAndStep : adjacentTiles) {
position = positionAndStep.getLeft();
map[position.getY()][position.getX()] = REACH_TILE;
queue.add(positionAndStep);
}
}

return countPositions();
}
long oddCells = 0;
long evenCells = 1; // First cell is even

public long solveADFS(int totalSteps) {
start.setVisited(true);
queue.add(start);

Set<Vector2> reachablePositions = new HashSet<>();
/*
long result = countReachableTiles(start.getY(),start.getX()+1, 1, totalSteps);
result += countReachableTiles(start.getY(), start.getX()-1, 1, totalSteps);
result += countReachableTiles(start.getY()-1, start.getX(), 1, totalSteps);
result += countReachableTiles(start.getY()+1, start.getX(), 1, totalSteps);
return result;
*/

countReachableTiles(start.getY(),start.getX()+1, 1, totalSteps, reachablePositions);
countReachableTiles(start.getY(), start.getX()-1, 1, totalSteps, reachablePositions);
countReachableTiles(start.getY()-1, start.getX(), 1, totalSteps, reachablePositions);
countReachableTiles(start.getY()+1, start.getX(), 1, totalSteps, reachablePositions);

return reachablePositions.size();
}

private void countReachableTiles(int row, int col, int steps, int maxSteps, Set<Vector2> reachablePositions) {

if(!isValid(row, col)) {
return;
}

if(steps == maxSteps) {
reachablePositions.add(new Vector2(col, row));
solutionsMap[row][col] = REACH_TILE;
return;
}
while(!queue.isEmpty()) {
Cell currentCell = queue.poll();

// TODO esta condición está mal
// Es necesario podar ramas que ya hayamos visitado para reducir el árbol de llamadas, pero hay que ver como.
/*if(solutionsMap[row][col] == REACH_TILE) {
return; // Already explored
}*/

if(maxSteps % 2 == 0) {
// Se buscan celdas pares
if(steps % 2 == 0) {
reachablePositions.add(new Vector2(col, row));
solutionsMap[row][col] = REACH_TILE;
}

} else {
// Se buscan celdas impares
if(steps % 2 != 0) {
reachablePositions.add(new Vector2(col, row));
solutionsMap[row][col] = REACH_TILE;
Set<Cell> adjacentCells = getAdjacentTiles(currentCell, totalSteps);
for(Cell adjacentCell : adjacentCells) {
if(!adjacentCell.isVisited()) {
adjacentCell.setVisited(true);
adjacentCell.setStep(currentCell.getStep()+1);
if(adjacentCell.isEven()) {
evenCells++;
} else {
oddCells++;
}
queue.add(adjacentCell);
}
}
}

countReachableTiles(row,col+1, steps+1, maxSteps, reachablePositions);
countReachableTiles(row, col-1, steps+1, maxSteps, reachablePositions);
countReachableTiles(row-1, col, steps+1, maxSteps, reachablePositions);
countReachableTiles(row+1, col, steps+1, maxSteps, reachablePositions);

return totalSteps % 2 == 0 ? evenCells : oddCells;
}

private long countReachableTiles(int row, int col, int steps, int maxSteps) {

if(!isValid(row, col)) {
return 0;
}

if(steps == maxSteps) {
return 1;
}

long result = countReachableTiles(row,col+1, steps+1, maxSteps);
result += countReachableTiles(row, col-1, steps+1, maxSteps);
result += countReachableTiles(row-1, col, steps+1, maxSteps);
result += countReachableTiles(row+1, col, steps+1, maxSteps);

return result;
}
private Set<Cell> getAdjacentTiles(Cell currentCell, int maxSteps) {

Set<Cell> adjacentTiles = new HashSet<>();

private long countPositions() {
long count = 0;
for(int row = 0; row < rows; row++) {
for(int col = 0; col < cols; col++) {
if(map[row][col] == REACH_TILE) {
count++;
}
}
if(currentCell.getStep() == maxSteps) {
return adjacentTiles;
}
return count;
}
private Set<Pair<Vector2,Integer>> getAdjacentTiles(Vector2 position, int stepCounter) {

Set<Pair<Vector2,Integer>> adjacentTiles = new HashSet<>();

//Vector2 position = positionAndSteps.getLeft();
//int stepCounter = positionAndSteps.getRight() + 1;
Vector2 position = new Vector2(currentCell.getCol(), currentCell.getRow());

// Possible positions
Vector2 left = Vector2.transform(position, Vector2.left());
@@ -177,35 +86,31 @@ private Set<Pair<Vector2,Integer>> getAdjacentTiles(Vector2 position, int stepCo

// Add valid movements to the adjacent set
if(isValid(left)) {
adjacentTiles.add(ImmutablePair.of(left, stepCounter+1));
adjacentTiles.add(map[left.getY()][left.getX()]);
}

if(isValid(right)) {
adjacentTiles.add(ImmutablePair.of(right, stepCounter+1));
adjacentTiles.add(map[right.getY()][right.getX()]);
}

if(isValid(up)) {
adjacentTiles.add(ImmutablePair.of(up, stepCounter+1));
adjacentTiles.add(map[up.getY()][up.getX()]);
}

if(isValid(down)) {
adjacentTiles.add(ImmutablePair.of(down, stepCounter+1));
adjacentTiles.add(map[down.getY()][down.getX()]);
}

return adjacentTiles;
}

/**
* A position is valid if is not out of bounds and does not contain a rock.
* @param position the position to check
* @return true if the cells is in bounds and not a rock. False otherwise.
*/
private boolean isValid(Vector2 position) {
return isNotOutOfBounds(position) && map[position.getY()][position.getX()] != ROCK;
}

private boolean isValid(int row, int col) {
// Empty tile that is in limits
// TODO we don't want to repeat movements
return isNotOutOfBounds(row, col) && map[row][col] == GARDEN_PLOT;
}
private boolean isNotOutOfBounds(int row, int col) {
return (row >= 0 && row < rows) && (col >= 0 && col < cols);
return isNotOutOfBounds(position) && !map[position.getY()][position.getX()].isRock();
}

private boolean isNotOutOfBounds(Vector2 position) {
10 changes: 3 additions & 7 deletions src/test/java/com/adventofcode/flashk/day21/Day21Test.java
Original file line number Diff line number Diff line change
@@ -23,7 +23,6 @@

@DisplayName(TestDisplayName.DAY_21)
@TestMethodOrder(OrderAnnotation.class)
@Disabled
public class Day21Test extends PuzzleTest {

private final static String INPUT_FOLDER = TestFolder.DAY_21;
@@ -47,11 +46,8 @@ public void testSolvePart1Sample() {
char[][] inputs = Input.read2DCharArray(INPUT_FOLDER, TestFilename.INPUT_FILE_SAMPLE);

StepCounter stepCounter = new StepCounter(inputs);
long result = stepCounter.solveADFS(1);
long result = stepCounter.solveA(6);

assertEquals(2, result);

result = stepCounter.solveADFS(6); // 6 para el ejemplo
assertEquals(16, result);

}
@@ -69,9 +65,9 @@ public void testSolvePart1Input() {
char[][] inputs = Input.read2DCharArray(INPUT_FOLDER, TestFilename.INPUT_FILE);

StepCounter stepCounter = new StepCounter(inputs);
long result = stepCounter.solveADFS(64);
long result = stepCounter.solveA(64);

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

}

0 comments on commit 7751bfc

Please sign in to comment.