Skip to content

Commit ed6cf86

Browse files
committed
Day 7 Part 1
1 parent 7216fc5 commit ed6cf86

File tree

4 files changed

+288
-0
lines changed

4 files changed

+288
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Each day will be setup as a separate folder.
1313
- [Day 4](/day-4/) - Ceres Search
1414
- [Day 5](/day-5/) - Print Queue
1515
- [Day 6](/day-6/) - Guard Gallivant
16+
- [Day 7](/day-7/) - Bridge Repair
1617

1718
## Environment Setup
1819

day-7/README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Day 7 - Bridge Repair
2+
3+
## Part 1
4+
5+
The Historians take you to a familiar rope bridge over a river in the middle of a jungle. The Chief isn't on this side of the bridge, though; maybe he's on the other side?
6+
7+
When you go to cross the bridge, you notice a group of engineers trying to repair it. (Apparently, it breaks pretty frequently.) You won't be able to cross until it's fixed.
8+
9+
You ask how long it'll take; the engineers tell you that it only needs final calibrations, but some young elephants were playing nearby and stole all the operators from their calibration equations! They could finish the calibrations if only someone could determine which test values could possibly be produced by placing any combination of operators into their calibration equations (your puzzle input).
10+
11+
For example:
12+
13+
```
14+
190: 10 19
15+
3267: 81 40 27
16+
83: 17 5
17+
156: 15 6
18+
7290: 6 8 6 15
19+
161011: 16 10 13
20+
192: 17 8 14
21+
21037: 9 7 18 13
22+
292: 11 6 16 20
23+
```
24+
25+
Each line represents a single equation. The test value appears before the colon on each line; it is your job to determine whether the remaining numbers can be combined with operators to produce the test value.
26+
27+
Operators are always evaluated left-to-right, not according to precedence rules. Furthermore, numbers in the equations cannot be rearranged. Glancing into the jungle, you can see elephants holding two different types of operators: add (+) and multiply (\*).
28+
29+
Only three of the above equations can be made true by inserting operators:
30+
31+
- 190: 10 19 has only one position that accepts an operator: between 10 and 19. Choosing + would give 29, but choosing _ would give the test value (10 _ 19 = 190).
32+
- 3267: 81 40 27 has two positions for operators. Of the four possible configurations of the operators, two cause the right side to match the test value: 81 + 40 _ 27 and 81 _ 40 + 27 both equal 3267 (when evaluated left-to-right)!
33+
- 292: 11 6 16 20 can be solved in exactly one way: 11 + 6 \* 16 + 20.
34+
35+
The engineers just need the total calibration result, which is the sum of the test values from just the equations that could possibly be true. In the above example, the sum of the test values for the three equations listed above is 3749.
36+
37+
Determine which equations could possibly be true. What is their total calibration result?
38+
39+
## Part 2
40+
41+
The engineers seem concerned; the total calibration result you gave them is nowhere close to being within safety tolerances. Just then, you spot your mistake: some well-hidden elephants are holding a third type of operator.
42+
43+
The concatenation operator (||) combines the digits from its left and right inputs into a single number. For example, 12 || 345 would become 12345. All operators are still evaluated left-to-right.
44+
45+
Now, apart from the three equations that could be made true using only addition and multiplication, the above example has three more equations that can be made true by inserting operators:
46+
47+
- 156: 15 6 can be made true through a single concatenation: 15 || 6 = 156.
48+
- 7290: 6 8 6 15 can be made true using 6 _ 8 || 6 _ 15.
49+
- 192: 17 8 14 can be made true using 17 || 8 + 14.
50+
51+
Adding up all six test values (the three that could be made before using only + and \* plus the new three that can now be made by also using ||) produces the new total calibration result of 11387.
52+
53+
Using your new knowledge of elephant hiding spots, determine which equations could possibly be true. What is their total calibration result?

day-7/main.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package main
2+
3+
import (
4+
_ "embed"
5+
"flag"
6+
"fmt"
7+
"log"
8+
"slices"
9+
"strconv"
10+
"strings"
11+
12+
file "github.com/shaunburdick/advent-of-code-2024/lib"
13+
)
14+
15+
var input string
16+
17+
func init() {
18+
// do this in init (not main) so test file has same input
19+
inputFile, err := file.LoadRelativeFile("input.txt")
20+
if err != nil {
21+
log.Println(err)
22+
}
23+
24+
input = strings.TrimRight(inputFile, "\n")
25+
}
26+
27+
func main() {
28+
var part int
29+
flag.IntVar(&part, "part", 1, "part 1 or 2")
30+
flag.Parse()
31+
fmt.Println("Running part", part)
32+
33+
if part == 1 {
34+
ans := part1(input)
35+
fmt.Println("Output:", ans)
36+
} else {
37+
ans := part2(input)
38+
fmt.Println("Output:", ans)
39+
}
40+
}
41+
42+
func part1(input string) int64 {
43+
parsed := parseInput(input)
44+
45+
var total int64
46+
total = 0
47+
48+
for _, calibration := range parsed {
49+
testValue, numbers := ParseCalibration(calibration)
50+
iter := IterateOperators(numbers[0], numbers[1:])
51+
if slices.Contains(iter, testValue) {
52+
total += testValue
53+
}
54+
}
55+
56+
return total
57+
}
58+
59+
func part2(input string) int64 {
60+
parsed := parseInput(input)
61+
_ = parsed
62+
63+
return 0
64+
}
65+
66+
func IterateOperators(carry int64, numbers []int64) (results []int64) {
67+
addResult := ApplyOperator(Add, carry, numbers[0])
68+
mulResult := ApplyOperator(Multiply, carry, numbers[0])
69+
70+
// base case
71+
if len(numbers) == 1 {
72+
results = append(results, addResult, mulResult)
73+
} else {
74+
nextAdd := IterateOperators(addResult, numbers[1:])
75+
nextMul := IterateOperators(mulResult, numbers[1:])
76+
77+
results = append(results, nextAdd...)
78+
results = append(results, nextMul...)
79+
}
80+
81+
return results
82+
}
83+
84+
func ParseCalibration(c string) (testValue int64, numbers []int64) {
85+
calSplit := strings.Split(c, ":")
86+
tv, tvErr := strconv.Atoi(calSplit[0])
87+
if tvErr != nil {
88+
log.Fatalf("Unable to parse test value: %s", calSplit[0])
89+
}
90+
testValue = int64(tv)
91+
92+
for i, num := range strings.Fields(calSplit[1]) {
93+
numInt, numErr := strconv.Atoi(num)
94+
if numErr != nil {
95+
log.Fatalf("Unable to parse number at position %d: %s", i, num)
96+
}
97+
98+
numbers = append(numbers, int64(numInt))
99+
}
100+
101+
return testValue, numbers
102+
}
103+
104+
type Operator int
105+
106+
const (
107+
Add Operator = iota
108+
Multiply
109+
)
110+
111+
func ApplyOperator(op Operator, a int64, b int64) int64 {
112+
switch op {
113+
case Add:
114+
return a + b
115+
case Multiply:
116+
return a * b
117+
default:
118+
panic("Unknown Operator")
119+
}
120+
}
121+
122+
func ApplyOperators(op Operator, numbers []int64) int64 {
123+
total := ApplyOperator(op, numbers[0], numbers[1])
124+
125+
if len(numbers) > 2 {
126+
for _, number := range numbers[2:] {
127+
total = ApplyOperator(op, total, number)
128+
}
129+
}
130+
131+
return total
132+
}
133+
134+
func parseInput(input string) (ans []string) {
135+
return strings.Split(input, "\n")
136+
}

day-7/main_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
6+
file "github.com/shaunburdick/advent-of-code-2024/lib"
7+
)
8+
9+
type TestDeclaration struct {
10+
name string
11+
input string
12+
want int64
13+
run bool
14+
}
15+
16+
var example1 = `190: 10 19
17+
3267: 81 40 27
18+
83: 17 5
19+
156: 15 6
20+
7290: 6 8 6 15
21+
161011: 16 10 13
22+
192: 17 8 14
23+
21037: 9 7 18 13
24+
292: 11 6 16 20`
25+
26+
func Test_day7_part1(t *testing.T) {
27+
tests := []TestDeclaration{
28+
{
29+
name: "example",
30+
input: example1,
31+
want: 3749,
32+
run: true,
33+
},
34+
{
35+
name: "actual",
36+
input: input,
37+
want: 1289579105366,
38+
run: file.ExistsRelativeFile("input.txt"),
39+
},
40+
}
41+
for _, tt := range tests {
42+
if tt.run {
43+
t.Run(tt.name, func(t *testing.T) {
44+
if got := part1(tt.input); got != tt.want {
45+
t.Errorf("part1() = %v, want %v", got, tt.want)
46+
}
47+
})
48+
}
49+
}
50+
}
51+
52+
func Benchmark_day7_part1(b *testing.B) {
53+
for i := 0; i < b.N; i++ {
54+
part1(example1)
55+
}
56+
}
57+
58+
var example2 = `190: 10 19
59+
3267: 81 40 27
60+
83: 17 5
61+
156: 15 6
62+
7290: 6 8 6 15
63+
161011: 16 10 13
64+
192: 17 8 14
65+
21037: 9 7 18 13
66+
292: 11 6 16 20`
67+
68+
func Test_day7_part2(t *testing.T) {
69+
tests := []TestDeclaration{
70+
{
71+
name: "example",
72+
input: example2,
73+
want: 11387,
74+
run: true,
75+
},
76+
{
77+
name: "actual",
78+
input: input,
79+
want: 0,
80+
run: file.ExistsRelativeFile("input.txt"),
81+
},
82+
}
83+
for _, tt := range tests {
84+
if tt.run {
85+
t.Run(tt.name, func(t *testing.T) {
86+
if got := part2(tt.input); got != tt.want {
87+
t.Errorf("part2() = %v, want %v", got, tt.want)
88+
}
89+
})
90+
}
91+
}
92+
}
93+
94+
func Benchmark_day7_part2(b *testing.B) {
95+
for i := 0; i < b.N; i++ {
96+
part2(example2)
97+
}
98+
}

0 commit comments

Comments
 (0)