Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 6b64412

Browse files
day 13
1 parent 26e68e1 commit 6b64412

File tree

7 files changed

+162
-0
lines changed

7 files changed

+162
-0
lines changed

‎README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,16 @@ except count them for part 2. I'll take that win!
6767
Day 11 part 2 is the first problem I had to sleep on. Initially I thought it was one of those where you don't actually
6868
need to iterate, you could pre-calculate the resulting number of stones at a given evolution. I'm still not sure if that
6969
could actually work. In the end, I simply figured out a more efficient representation for iteration. It worked nicely.
70+
71+
### 12/13/2024
72+
73+
I've been dreading the type of problem that came up in Day 13, where the only viable solution isn't based on
74+
time- or spacing-saving algorithms, but on calculation. I won't spoil it here, but I'll just say it took me
75+
a little while to write out the equation.
76+
77+
I often declare records as members of the class when 1) they're only used by that class, 2) there's a bunch of them
78+
that are related and work together, 3) it's nice to see them all in one place instead of in separate files.
79+
Today I randomly discovered
80+
[local classes](https://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html) and
81+
[records](https://docs.oracle.com/en/java/javase/17/language/records.html). This allows you to scope, say, an intermediate record for a stream transformation, to a single method
82+
and avoid clutter at the class level. Pretty cool!
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.codefork.aoc2024.day13;
2+
3+
import java.util.ArrayList;
4+
import java.util.Iterator;
5+
import java.util.List;
6+
import java.util.Optional;
7+
import java.util.regex.Pattern;
8+
9+
public record Machine(Button a, Button b, Prize prize) {
10+
11+
private static Pattern buttonPattern = Pattern.compile("Button ([AB]): X\\+(\\d+), Y\\+(\\d+)");
12+
private static Pattern prizePattern = Pattern.compile("Prize: X=(\\d+), Y=(\\d+)");
13+
14+
public record Button(int moveX, int moveY) {
15+
16+
}
17+
18+
public record Prize(long x, long y) {
19+
20+
}
21+
22+
public record Solution(long countA, long countB) {
23+
24+
}
25+
26+
public Optional<Solution> findSolution() {
27+
// I wrote out on paper the two simultaneous equations, merged them into one using
28+
// substitution, and solved for the number of times button B is pressed (countB).
29+
// Once we have that, we can plug it into one of the original equations to get countA
30+
var numer = (a.moveY() * prize.x()) - (a.moveX() * prize.y());
31+
var denom = (a.moveY() * b.moveX()) - (a.moveX() * b.moveY());
32+
33+
// test that countB and countA are whole numbers; we can't push a button a fraction of a time,
34+
// so reject those solutions
35+
if (numer % denom == 0) {
36+
var countB = numer / denom;
37+
var numerCountA = prize.x() - (b.moveX() * countB);
38+
if (numerCountA % a.moveX() == 0) {
39+
var countA = numerCountA / a.moveX();
40+
return Optional.of(new Solution(countA, countB));
41+
}
42+
}
43+
return Optional.empty();
44+
}
45+
46+
/**
47+
* The nulls here are gross, but IntelliJ warns you're not supposed to use Optionals for parameters,
48+
* so I'm not sure how else to do this more safely.
49+
*/
50+
public static List<Machine> consume(Iterator<String> iter, long prizeAddition, List<Machine> machines, Button a, Button b) {
51+
if (iter.hasNext()) {
52+
var line = iter.next();
53+
var buttonMatcher = buttonPattern.matcher(line);
54+
if (buttonMatcher.find()) {
55+
var button = buttonMatcher.group(1);
56+
var x = Integer.parseInt(buttonMatcher.group(2));
57+
var y = Integer.parseInt(buttonMatcher.group(3));
58+
if (button.equals("A")) {
59+
return consume(iter, prizeAddition, machines, new Button(x, y), b);
60+
}
61+
if (button.equals("B")) {
62+
return consume(iter, prizeAddition, machines, a, new Button(x, y));
63+
}
64+
}
65+
var prizeMatcher = prizePattern.matcher(line);
66+
if (prizeMatcher.find()) {
67+
var x = Integer.parseInt(prizeMatcher.group(1)) + prizeAddition;
68+
var y = Integer.parseInt(prizeMatcher.group(2)) + prizeAddition;
69+
var prize = new Prize(x, y);
70+
// a and b should be present at this point; if they're not, something's wrong with the input
71+
machines.add(new Machine(a, b, prize));
72+
return consume(iter, prizeAddition, machines, null, null);
73+
}
74+
consume(iter, prizeAddition, machines, a, b);
75+
}
76+
return machines;
77+
}
78+
79+
public static List<Machine> consume(Iterator<String> iter, long prizeAddition) {
80+
return consume(iter, prizeAddition, new ArrayList<>(), null, null);
81+
}
82+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.codefork.aoc2024.day13;
2+
3+
import com.codefork.aoc2024.Problem;
4+
import com.codefork.aoc2024.util.Assert;
5+
6+
import java.util.Optional;
7+
import java.util.stream.Stream;
8+
9+
public class Part01 extends Problem {
10+
11+
public String solve(Stream<String> data) {
12+
var machines = Machine.consume(data.iterator(), 0);
13+
var tokens = machines.stream()
14+
.map(Machine::findSolution)
15+
.filter(Optional::isPresent)
16+
.map(Optional::orElseThrow)
17+
.map(solution -> (solution.countA() * 3) + solution.countB())
18+
.mapToLong(i-> i)
19+
.sum();
20+
21+
return String.valueOf(tokens);
22+
}
23+
24+
@Override
25+
public String solve() {
26+
Assert.assertEquals("480", solve(getSampleInput()));
27+
return solve(getInput());
28+
}
29+
30+
public static void main(String[] args) {
31+
new Part01().run();
32+
}
33+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.codefork.aoc2024.day13;
2+
3+
import com.codefork.aoc2024.Problem;
4+
5+
import java.util.Optional;
6+
import java.util.stream.Stream;
7+
8+
public class Part02 extends Problem {
9+
10+
public String solve(Stream<String> data) {
11+
var machines = Machine.consume(data.iterator(), 10000000000000L);
12+
var tokens = machines.stream()
13+
//.peek(System.out::println)
14+
.map(Machine::findSolution)
15+
.filter(Optional::isPresent)
16+
//.peek(System.out::println)
17+
.map(Optional::orElseThrow)
18+
.map(solution -> (solution.countA() * 3) + solution.countB())
19+
.mapToLong(i-> i)
20+
.sum();
21+
22+
return String.valueOf(tokens);
23+
}
24+
25+
@Override
26+
public String solve() {
27+
return solve(getInput());
28+
}
29+
30+
public static void main(String[] args) {
31+
new Part02().run();
32+
}
33+
34+
}

‎src/main/resources/day13/input

20.5 KB
Binary file not shown.

‎src/main/resources/day13/sample

285 Bytes
Binary file not shown.

‎src/main/resources/day13/sample2

22 Bytes
Binary file not shown.

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /