AoC 2025 Day1

This commit is contained in:
🐙PiperYxzzy
2025-12-01 12:30:32 +02:00
parent 8df60ae0df
commit faaebde0bb
7 changed files with 4204 additions and 250 deletions

View File

@@ -1,75 +0,0 @@
## \-\-\- Day 24: Never Tell Me The Odds ---
It seems like something is going wrong with the snow-making process. Instead of forming snow, the water that's been absorbed into the air seems to be forming [hail](https://en.wikipedia.org/wiki/Hail)!
Maybe there's something you can do to break up the hailstones?
Due to strong, probably-magical winds, the hailstones are all flying through the air in perfectly linear trajectories. You make a note of each hailstone's _position_ and _velocity_ (your puzzle input). For example:
```
19, 13, 30 @ -2, 1, -2
18, 19, 22 @ -1, -1, -2
20, 25, 34 @ -2, -2, -4
12, 31, 28 @ -1, -2, -1
20, 19, 15 @ 1, -5, -3
```
Each line of text corresponds to the position and velocity of a single hailstone. The positions indicate where the hailstones are _right now_ (at time `0`). The velocities are constant and indicate exactly how far each hailstone will move in _one nanosecond_.
Each line of text uses the format `px py pz @ vx vy vz`. For instance, the hailstone specified by `20, 19, 15 @ 1, -5, -3` has initial X position `20`, Y position `19`, Z position `15`, X velocity `1`, Y velocity `-5`, and Z velocity `-3`. After one nanosecond, the hailstone would be at `21, 14, 12`.
Perhaps you won't have to do anything. How likely are the hailstones to collide with each other and smash into tiny ice crystals?
To estimate this, consider only the X and Y axes; _ignore the Z axis_. Looking _forward in time_, how many of the hailstones' _paths_ will intersect within a test area? (The hailstones themselves don't have to collide, just test for intersections between the paths they will trace.)
In this example, look for intersections that happen with an X and Y position each at least `7` and at most `27`; in your actual data, you'll need to check a much larger test area. Comparing all pairs of hailstones' future paths produces the following results:
```
Hailstone A: 19, 13, 30 @ -2, 1, -2
Hailstone B: 18, 19, 22 @ -1, -1, -2
Hailstones' paths will cross inside the test area (at x=14.333, y=15.333).
Hailstone A: 19, 13, 30 @ -2, 1, -2
Hailstone B: 20, 25, 34 @ -2, -2, -4
Hailstones' paths will cross inside the test area (at x=11.667, y=16.667).
Hailstone A: 19, 13, 30 @ -2, 1, -2
Hailstone B: 12, 31, 28 @ -1, -2, -1
Hailstones' paths will cross outside the test area (at x=6.2, y=19.4).
Hailstone A: 19, 13, 30 @ -2, 1, -2
Hailstone B: 20, 19, 15 @ 1, -5, -3
Hailstones' paths crossed in the past for hailstone A.
Hailstone A: 18, 19, 22 @ -1, -1, -2
Hailstone B: 20, 25, 34 @ -2, -2, -4
Hailstones' paths are parallel; they never intersect.
Hailstone A: 18, 19, 22 @ -1, -1, -2
Hailstone B: 12, 31, 28 @ -1, -2, -1
Hailstones' paths will cross outside the test area (at x=-6, y=-5).
Hailstone A: 18, 19, 22 @ -1, -1, -2
Hailstone B: 20, 19, 15 @ 1, -5, -3
Hailstones' paths crossed in the past for both hailstones.
Hailstone A: 20, 25, 34 @ -2, -2, -4
Hailstone B: 12, 31, 28 @ -1, -2, -1
Hailstones' paths will cross outside the test area (at x=-2, y=3).
Hailstone A: 20, 25, 34 @ -2, -2, -4
Hailstone B: 20, 19, 15 @ 1, -5, -3
Hailstones' paths crossed in the past for hailstone B.
Hailstone A: 12, 31, 28 @ -1, -2, -1
Hailstone B: 20, 19, 15 @ 1, -5, -3
Hailstones' paths crossed in the past for both hailstones.
```
So, in this example, `2` hailstones' future paths cross inside the boundaries of the test area.
However, you'll need to search a much larger test area if you want to see if any hailstones might collide. Look for intersections that happen with an X and Y position each at least `200000000000000` and at most `400000000000000`. Disregard the Z axis entirely.
Considering only the X and Y axes, check all pairs of hailstones' future paths for intersections. _How many of these intersections occur within the test area?_

View File

@@ -1,170 +0,0 @@
package main
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/jpillora/puzzler/harness/aoc"
)
func main() {
aoc.Harness(run)
}
// on code change, run will be executed 4 times:
// 1. with: false (part1), and example input
// 2. with: true (part2), and example input
// 3. with: false (part1), and user input
// 4. with: true (part2), and user input
// the return value of each run is printed to stdout
type Hailstone struct {
X int
Y int
Z int
Xv int
Yv int
Zv int
}
func (h *Hailstone) DoPathsCrossXY(o *Hailstone, boundMin, boundMax float64) (x, y float64, err error) {
// Find which bounds this hailstone is going to
hyBound, hxBound := float64(h.Y), float64(h.X)
if h.Yv < 0 {
hyBound = boundMin
} else if h.Yv > 0 {
hyBound = boundMax
}
if h.Xv < 0 {
hxBound = boundMin
} else if h.Xv > 0 {
hxBound = boundMax
}
// which one reached first
/*
19, 13, _ @ -2, 1, _
y=19 -2c
x=13 + c
*/
}
func (h *Hailstone) FindIntersectionXY(o *Hailstone) (ns, x, y float64, err error) {
if h == o {
return -1, -1, -1, errors.New("same hailstone")
}
yc := (float64(h.Y) - float64(o.Y)) / (float64(o.Yv) - float64(h.Yv))
xc := (float64(h.X) - float64(o.X)) / (float64(o.Xv) - float64(h.Xv))
// if xInt == yInt is the same, we have a match
fmt.Printf("%v ; %v\n", xc, yc)
if yc == xc && yc > 0 {
return xc, float64(h.X) + float64(h.Xv)*xc, float64(h.Y) + float64(h.Yv)*yc, nil
}
return -1, -1, -1, errors.New("No intersect")
// y = ax+b Y =
/*
19, 13, _ @ -2, 1, _
18, 19, _ @ -1, -1, _
1:
y= 19 - 2c
x= 13 + c
hY = h.Y + c*h.Yv
oY = o.Y + c.o.Yv
if hY=oY:
h.Y + c*h.Yv = o.Y + c*o.Yv
h.Y - o.Y = c*o.Yv - c*h.Yv
h.Y - o.Y = c(o.Yv - h.Yv)
(h.Y - o.Y) / (o.Yv - h.Yv) =
2:
y= 18 - c
x= 19 - c
--- ?
where does y1 = y2
19 - 2c = 18 - c
19 - 18 - 2c = -c
1 = -c + 2c
1 = c
@ 1
*/
}
func run(part2 bool, input string) any {
hailRxp := regexp.MustCompile(`(?P<X>\d+), +(?P<Y>\d+), +(?P<Z>\d+) @ +(?P<VX>-?\d+), +(?P<VY>-?\d+), +(?P<VZ>-?\d+)`)
stones := make([]Hailstone, 0)
for _, l := range strings.Split(input, "\n") {
matches := hailRxp.FindStringSubmatch(l)
if len(matches) == 0 {
continue
}
px, _ := strconv.Atoi(matches[1])
py, _ := strconv.Atoi(matches[2])
pz, _ := strconv.Atoi(matches[3])
vx, _ := strconv.Atoi(matches[4])
vy, _ := strconv.Atoi(matches[5])
vz, _ := strconv.Atoi(matches[6])
stones = append(stones, Hailstone{px, py, pz, vx, vy, vz})
}
// when you're ready to do part 2, remove this "not implemented" block
if part2 {
return "not implemented"
}
// solve part 1 here
testMin, testMax := 7, 27
insideRange := 0
for _, h := range stones {
for _, o := range stones {
ns, x, y, err := h.FindIntersectionXY(&o)
if err == nil {
fmt.Printf("Intersection of %v:%v at %vns (%v, %v)\n", h, o, ns, x, y)
if x > float64(testMin) && x < float64(testMax) && y > float64(testMin) && y < float64(testMax) {
insideRange += 1
}
} else {
fmt.Printf("ERROR %v\n", err)
}
}
}
return insideRange
}

View File

@@ -1,5 +0,0 @@
19, 13, 30 @ -2, 1, -2
18, 19, 22 @ -1, -1, -2
20, 25, 34 @ -2, -2, -4
12, 31, 28 @ -1, -2, -1
20, 19, 15 @ 1, -5, -3

63
2025/01/README.md Executable file
View File

@@ -0,0 +1,63 @@
## \-\-\- Day 1: Secret Entrance ---
The Elves have good news and bad news.
The good news is that they've discovered [project management](https://en.wikipedia.org/wiki/Project_management)! This has given them the tools they need to prevent their usual Christmas emergency. For example, they now know that the North Pole decorations need to be finished soon so that other critical tasks can start on time.
The bad news is that they've realized they have a _different_ emergency: according to their resource planning, none of them have any time left to decorate the North Pole!
To save Christmas, the Elves need _you_ to _finish decorating the North Pole by December 12th_.
Collect stars by solving puzzles. Two puzzles will be made available on each day; the second puzzle is unlocked when you complete the first. Each puzzle grants _one star_. Good luck!
You arrive at the secret entrance to the North Pole base ready to start decorating. Unfortunately, the _password_ seems to have been changed, so you can't get in. A document taped to the wall helpfully explains:
"Due to new security protocols, the password is locked in the safe below. Please see the attached document for the new combination."
The safe has a dial with only an arrow on it; around the dial are the numbers `0` through `99` in order. As you turn the dial, it makes a small _click_ noise as it reaches each number.
The attached document (your puzzle input) contains a sequence of _rotations_, one per line, which tell you how to open the safe. A rotation starts with an `L` or `R` which indicates whether the rotation should be to the _left_ (toward lower numbers) or to the _right_ (toward higher numbers). Then, the rotation has a _distance_ value which indicates how many clicks the dial should be rotated in that direction.
So, if the dial were pointing at `11`, a rotation of `R8` would cause the dial to point at `19`. After that, a rotation of `L19` would cause it to point at `0`.
Because the dial is a circle, turning the dial _left from `0`_ one click makes it point at `99`. Similarly, turning the dial _right from `99`_ one click makes it point at `0`.
So, if the dial were pointing at `5`, a rotation of `L10` would cause it to point at `95`. After that, a rotation of `R5` could cause it to point at `0`.
The dial starts by pointing at `50`.
You could follow the instructions, but your recent required official North Pole secret entrance security training seminar taught you that the safe is actually a decoy. The actual password is _the number of times the dial is left pointing at `0` after any rotation in the sequence_.
For example, suppose the attached document contained the following rotations:
```
L68
L30
R48
L5
R60
L55
L1
L99
R14
L82
```
Following these rotations would cause the dial to move as follows:
- The dial starts by pointing at `50`.
- The dial is rotated `L68` to point at `82`.
- The dial is rotated `L30` to point at `52`.
- The dial is rotated `R48` to point at `0`.
- The dial is rotated `L5` to point at `95`.
- The dial is rotated `R60` to point at `55`.
- The dial is rotated `L55` to point at `0`.
- The dial is rotated `L1` to point at `99`.
- The dial is rotated `L99` to point at `0`.
- The dial is rotated `R14` to point at `14`.
- The dial is rotated `L82` to point at `32`.
Because the dial points at `0` a total of three times during this process, the password in this example is `3`.
Analyze the rotations in your attached document. _What's the actual password to open the door?_

94
2025/01/code.go Normal file
View File

@@ -0,0 +1,94 @@
package main
import (
"strconv"
"strings"
"github.com/jpillora/puzzler/harness/aoc"
)
func main() {
aoc.Harness(run)
}
// on code change, run will be executed 4 times:
// 1. with: false (part1), and example input
// 2. with: true (part2), and example input
// 3. with: false (part1), and user input
// 4. with: true (part2), and user input
// the return value of each run is printed to stdout
func run(part2 bool, input string) any {
// when you're ready to do part 2, remove this "not implemented" block
if part2 {
dial_val := 50
zcount := 0
for _, line := range strings.Split(input, "\n") {
if line == "" {
continue
}
dir, cstr := line[0], line[1:]
c, _ := strconv.Atoi(cstr)
full_rotations := c / 100
zcount += full_rotations
c -= full_rotations * 100
new_val := dial_val
if dir == 'R' {
new_val += c
if new_val > 99 {
new_val -= 100
zcount++
}
} else {
new_val -= c
if new_val < 0 {
new_val += 100
if dial_val != 0 {
zcount++
}
} else if new_val == 0 {
zcount++
}
}
dial_val = new_val
}
return zcount
}
// solve part 1 here
dial_val := 50
zcount := 0
for _, line := range strings.Split(input, "\n") {
//fmt.Printf("%v\n", line)
if line == "" {
continue
}
dir, cstr := line[0], line[1:]
c, _ := strconv.Atoi(cstr)
c = c % 100
if dir == 'R' {
dial_val += c
} else {
dial_val += (100 - c)
}
if dial_val >= 100 {
dial_val = dial_val % 100
}
if dial_val == 0 {
zcount++
}
//fmt.Printf("Pos: %v (%v)\n", dial_val, zcount)
}
return zcount
}

10
2025/01/input-example.txt Executable file
View File

@@ -0,0 +1,10 @@
L68
L30
R48
L5
R60
L55
L1
L99
R14
L82

4037
2025/01/input-user.txt Normal file

File diff suppressed because it is too large Load Diff