Skip to content

Commit

Permalink
Add Day 19
Browse files Browse the repository at this point in the history
  • Loading branch information
kapilgain committed Dec 19, 2023
1 parent a34ffe3 commit 50e2671
Show file tree
Hide file tree
Showing 22 changed files with 559 additions and 10 deletions.
4 changes: 3 additions & 1 deletion .idea/runConfigurations/Jacoco.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions .idea/runConfigurations/Run_tests.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/main/java/org/advent/Main.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package org.advent;

import org.advent.day18.Day18;
import org.advent.day19.Day19;

public class Main {

public static void main(String[] args) {
new Day18().run();
new Day19().run();
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
package org.advent.utils;
package org.advent.day17;

import org.advent.utils.Pointer;

import static org.apache.commons.lang3.builder.EqualsBuilder.reflectionEquals;
import static org.apache.commons.lang3.builder.HashCodeBuilder.reflectionHashCode;
Expand Down
1 change: 0 additions & 1 deletion src/main/java/org/advent/day17/Day17Part1.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.advent.day17;

import io.vavr.collection.PriorityQueue;
import org.advent.utils.Crucible;
import org.advent.utils.Direction;
import org.advent.utils.Location;
import org.advent.utils.Pointer;
Expand Down
1 change: 0 additions & 1 deletion src/main/java/org/advent/day17/Day17Part2.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.advent.day17;

import io.vavr.collection.PriorityQueue;
import org.advent.utils.Crucible;

import java.util.List;

Expand Down
14 changes: 14 additions & 0 deletions src/main/java/org/advent/day19/Day19.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.advent.day19;

import org.advent.utils.ClasspathFileReader;

public class Day19 implements Runnable {

@Override
public void run() {
var lines = new ClasspathFileReader().readAllLines("day19.input");
System.out.println(new Day19Part1().solve(lines));
System.out.println(new Day19Part2().solve(lines));
}

}
81 changes: 81 additions & 0 deletions src/main/java/org/advent/day19/Day19Part1.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.advent.day19;

import io.vavr.Tuple;
import org.apache.commons.lang3.StringUtils;

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

import static io.vavr.collection.List.ofAll;
import static java.util.Objects.requireNonNull;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;
import static org.advent.utils.StringUtils.parseSections;
import static org.advent.utils.StringUtils.readLines;

public class Day19Part1 {

public Number solve(List<String> lines) {
var sections = parseSections(lines);
var workflowMap = createWorkflowMap(sections.getFirst());
var parts = readLines(sections.getLast()).stream()
.map(Part::parse)
.toList();

return ofAll(parts.stream()
.map(part -> Tuple.of(part, runPartThroughWorkflows(part, workflowMap)))
.filter(tuple -> tuple._2.contains("A"))
.map(tuple -> tuple._1.calculateChristmasRating()))
.sum();
}

public Map<String, Workflow> createWorkflowMap(String lines) {
return readLines(lines).stream()
.map(Workflow::parse)
.collect(toMap(Workflow::name, identity()));
}

public List<String> runPartThroughWorkflows(Part part, Map<String, Workflow> workflowMap) {
var workflowName = "in";
var path = new ArrayList<String>();
path.add(workflowName);

while (true) {
var workflow = requireNonNull(workflowMap.get(workflowName));
for (var rule : workflow.rules()) {
if (evaluateCondition(rule.condition(), part)) {
if ("A".equals(rule.destination()) || "R".equals(rule.destination())) {
path.add(rule.destination());
return path;

} else {
workflowName = rule.destination();
path.add(workflowName);
break;
}
}
}
}
}

public boolean evaluateCondition(String condition, Part part) {
if (StringUtils.isBlank(condition)) {
return true;
}

var isLt = condition.contains("<");
String[] parts = condition.split(isLt ? "<" : ">");
String lhs = parts[0];
int rhs = Integer.parseInt(parts[1]);

return switch (lhs) {
case "x" -> isLt ? part.x() < rhs : part.x() > rhs;
case "m" -> isLt ? part.m() < rhs : part.m() > rhs;
case "a" -> isLt ? part.a() < rhs : part.a() > rhs;
case "s" -> isLt ? part.s() < rhs : part.s() > rhs;
default -> throw new IllegalArgumentException("Invalid condition format");
};
}

}
83 changes: 83 additions & 0 deletions src/main/java/org/advent/day19/Day19Part2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package org.advent.day19;

import io.vavr.Tuple;
import io.vavr.Tuple2;
import org.advent.utils.MathUtils;

import java.util.List;
import java.util.Map;
import java.util.Optional;

import static io.vavr.collection.HashMap.of;
import static org.advent.utils.StringUtils.parseSections;

public class Day19Part2 extends Day19Part1 {

public Number solve(List<String> lines) {
var sections = parseSections(lines);
var workflowMap = createWorkflowMap(sections.getFirst());
return countAcceptedCombinations(
of(
'x', Tuple.of(1, 4000),
'm', Tuple.of(1, 4000),
'a', Tuple.of(1, 4000),
's', Tuple.of(1, 4000)
),
workflowMap,
"in"
);
}

public long countAcceptedCombinations(
io.vavr.collection.Map<Character, Tuple2<Integer, Integer>> christmasRanges,
Map<String, Workflow> workflowMap,
String startingWorkflowName) {
if ("R".equals(startingWorkflowName)) {
return 0L;
}

if ("A".equals(startingWorkflowName)) {
return christmasRanges.values()
.map(range -> range._2 - range._1 + 1)
.product()
.longValue();
}

var workflow = workflowMap.get(startingWorkflowName);
var count = 0L;
for (var rule : workflow.rules()) {
var range = christmasRanges.get(rule.conditionLHS()).getOrNull();

// Last rule, no condition, just go to destination
if (rule.conditionOperator() == null) {
count += countAcceptedCombinations(christmasRanges, workflowMap, rule.destination());
continue;
}

char operator = rule.conditionOperator();
var rhs = Optional.ofNullable(rule.conditionRHS());
if (operator == '<') {
var matchedRange = MathUtils.intersectRange(range, Tuple.of(range._1, rhs.orElse(0) - 1));
var unmatchedRange = Tuple.of(matchedRange._2 + 1, range._2);
count += countAcceptedCombinations(
christmasRanges.put(rule.conditionLHS(), matchedRange),
workflowMap,
rule.destination());
christmasRanges = christmasRanges.put(rule.conditionLHS(), unmatchedRange);
continue;
}

// operator == '>'
var matchedRange = MathUtils.intersectRange(range, Tuple.of(rhs.orElse(0) + 1, range._2));
var unmatchedRange = Tuple.of(range._1, matchedRange._1 - 1);
count += countAcceptedCombinations(
christmasRanges.put(rule.conditionLHS(), matchedRange),
workflowMap,
rule.destination());
christmasRanges = christmasRanges.put(rule.conditionLHS(), unmatchedRange);
}

return count;
}

}
18 changes: 18 additions & 0 deletions src/main/java/org/advent/day19/Part.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.advent.day19;

public record Part(int x, int m, int a, int s) {

public int calculateChristmasRating() {
return x + m + a + s; // 🎄
}

public static Part parse(String line) {
var values = line.substring(1, line.length() - 1).split(",");
var x = Integer.parseInt(values[0].split("=")[1]);
var m = Integer.parseInt(values[1].split("=")[1]);
var a = Integer.parseInt(values[2].split("=")[1]);
var s = Integer.parseInt(values[3].split("=")[1]);
return new Part(x, m, a, s);
}

}
19 changes: 19 additions & 0 deletions src/main/java/org/advent/day19/Workflow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.advent.day19;

import java.util.Arrays;
import java.util.List;

import static java.util.stream.Collectors.toList;

public record Workflow(String name, List<WorkflowRule> rules) {

public static Workflow parse(String line) {
var parts = line.split("\\{");
var name = parts[0];
var rules = Arrays.stream(parts[1].substring(0, parts[1].length() - 1).split(","))
.map(WorkflowRule::parse)
.collect(toList());
return new Workflow(name, rules);
}

}
22 changes: 22 additions & 0 deletions src/main/java/org/advent/day19/WorkflowRule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.advent.day19;

public record WorkflowRule(String condition, String destination) {

public static WorkflowRule parse(String rule) {
var ruleParts = rule.split(":");
return new WorkflowRule(ruleParts.length > 1 ? ruleParts[0] : null, ruleParts[ruleParts.length - 1]);
}

public Character conditionLHS() {
return condition == null ? null : condition.charAt(0);
}

public Character conditionOperator() {
return condition == null ? null : condition.charAt(1);
}

public Integer conditionRHS() {
return condition == null ? null : Integer.parseInt(condition.substring(2));
}

}
11 changes: 11 additions & 0 deletions src/main/java/org/advent/utils/MathUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,15 @@ public static long manhattanDistance(Tuple2<Integer, Integer> first, Tuple2<Inte
return Math.abs(second._1 - first._1) + Math.abs(second._2 - first._2);
}

public static Tuple2<Integer, Integer> intersectRange(
Tuple2<Integer, Integer> firstRange, Tuple2<Integer, Integer> secondRange
) {
var returnVal = Tuple.of(Math.max(firstRange._1, secondRange._1), Math.min(firstRange._2, secondRange._2));
if (returnVal._1 > returnVal._2) {
return Tuple.of(0, 0);
}

return returnVal;
}

}
6 changes: 3 additions & 3 deletions src/main/java/org/advent/utils/Pointer.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
public record Pointer(Location location, Direction direction) {

public Pointer move() {
return new Pointer(location().move(direction), direction);
return new Pointer(location.move(direction), direction);
}

public Pointer turnLeft() {
return new Pointer(location(), direction.turnLeft());
return new Pointer(location, direction.turnLeft());
}

public Pointer turnRight() {
return new Pointer(location(), direction.turnRight());
return new Pointer(location, direction.turnRight());
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package org.advent.utils;
package org.advent.day17;

import org.advent.day17.Crucible;
import org.advent.utils.Direction;
import org.advent.utils.Location;
import org.advent.utils.Pointer;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;
Expand Down
3 changes: 3 additions & 0 deletions src/test/java/org/advent/day18/Day18Part2Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.advent.utils.Direction;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable;

import static org.advent.day18.Day18Part1Test.TEST_DATA;
import static org.junit.jupiter.api.Assertions.assertEquals;
Expand Down Expand Up @@ -31,6 +32,8 @@ void testParseDirection() {
}

@Test
// This test is slow so disabling it in local environment
@DisabledIfEnvironmentVariable(named = "local", matches = "true")
void solvesForTestInput() {
assertEquals(952_408_144_115L, underTest.solve(TEST_DATA));
}
Expand Down
Loading

0 comments on commit 50e2671

Please sign in to comment.