Next up: the lobby of a resort on a tropical island. The Historians take a moment to admire the hexagonal floor tiles before spreading out.
Fortunately, it looks like the resort has a new arcade! Maybe you can win some prizes from the claw machines?
Solution in Java
Full source can be found: in GitHub
First I created some records to represent the various entities in the assignment. This consists out of the PlayCost
representing the amount of steps that each hit of the button will cost. The second record WinPoint
represents the exact location that has to be hit to win a game.
1record PlayCost(int x, int y) {}
2
3record WinPoint(long x, long y) {
4 public WinPoint translate(long dx) {
5 return new WinPoint(x + dx, y + dx);
6 }
7}
The we still have the actual slot machine represented by Machine
.
1record Machine(WinPoint price, PlayCost buttonA, PlayCost buttonB) {}
Using these records it is possible to parse the input string into the slot machine instructions.
1public void readInput() {
2 Function<String, PlayCost> costExtractor = costLine -> {
3 var matcher = Pattern.compile("X\\+(?<X>\\d+), Y\\+(?<Y>\\d+)")
4 .matcher(costLine);
5 matcher.find();
6 return new PlayCost(
7 parseInt(matcher.group("X")),
8 parseInt(matcher.group("Y")));
9 };
10 Function<String, WinPoint> priceExtractor = costLine -> {
11 var matcher = Pattern.compile("X=(?<X>\\d+), Y=(?<Y>\\d+)")
12 .matcher(costLine);
13 matcher.find();
14 return new WinPoint(
15 parseInt(matcher.group("X")),
16 parseInt(matcher.group("Y")));
17 };
18
19 for (var machineString : inputLoader.string().split("\n\n")) {
20 var instructions = machineString.split("\n");
21 computeMachines.add(new Machine(
22 priceExtractor.apply(instructions[2]),
23 costExtractor.apply(instructions[0]),
24 costExtractor.apply(instructions[1])));
25 }
26}
Part 1
First I added a method computeMinCost
to the Machine
to calculate the winning move for each of the machines in the input.
1record Machine(WinPoint price, PlayCost buttonA, PlayCost buttonB) {
2 long computeMinCost() {
3 var top = price.y() * buttonA.x() - price().x() * buttonA.y();
4 var bottom = buttonA().x() * buttonB.y() - buttonA().y() * buttonB.x();
5 if (top % bottom == 0) {
6 var D = price.y() - (top / bottom) * buttonB.y();
7 if (D % buttonA.y() == 0) {
8 return 3 * (D / buttonA.y()) + (top / bottom);
9 }
10 }
11
12 return 0L;
13 }
14}
Using this method we can implement part 1 of today.
1public void part1() {
2 var totalCost = computeMachines.stream()
3 .mapToLong(Machine::computeMinCost)
4 .sum();
5
6 validator.part1(totalCost);
7}
Part 2
In part 2 for today the grid coordinate to hit to win is made more expensive. To achieve that I added an additional method to the Machine
to make the winning point more expensive.
1Machine makeMoreExpensive() {
2 return new Machine(price.translate(10000000000000L), buttonA, buttonB);
3}
With this new method added to the Machine
class it is relatively easy to adjust the code of part 1 to provide the answer for part 2.
1public void part2() {
2 var totalCost = computeMachines.stream()
3 .map(Machine::makeMoreExpensive)
4 .mapToLong(Machine::computeMinCost)
5 .sum();
6
7 validator.part2(totalCost);
8}