diff --git a/2023/22/README.md b/2023/22/README.md new file mode 100755 index 0000000..a190db4 --- /dev/null +++ b/2023/22/README.md @@ -0,0 +1,139 @@ +r## \-\-\- Day 22: Sand Slabs --- + +Enough sand has fallen; it can finally filter water for Snow Island. + +Well, _almost_. + +The sand has been falling as large compacted _bricks_ of sand, piling up to form an impressive stack here near the edge of Island Island. In order to make use of the sand to filter water, some of the bricks will need to be broken apart - nay, _disintegrated_ \- back into freely flowing sand. + +The stack is tall enough that you'll have to be careful about choosing which bricks to disintegrate; if you disintegrate the wrong brick, large portions of the stack could topple, which sounds pretty dangerous. + +The Elves responsible for water filtering operations took a _snapshot of the bricks while they were still falling_ (your puzzle input) which should let you work out which bricks are safe to disintegrate. For example: + +``` +1,0,1~1,2,1 +0,0,2~2,0,2 +0,2,3~2,2,3 +0,0,4~0,2,4 +2,0,5~2,2,5 +0,1,6~2,1,6 +1,1,8~1,1,9 + +``` + +Each line of text in the snapshot represents the position of a single brick at the time the snapshot was taken. The position is given as two `x,y,z` coordinates - one for each end of the brick - separated by a tilde ( `~`). Each brick is made up of a single straight line of cubes, and the Elves were even careful to choose a time for the snapshot that had all of the free-falling bricks at _integer positions above the ground_, so the whole snapshot is aligned to a three-dimensional cube grid. + +A line like `2,2,2~2,2,2` means that both ends of the brick are at the same coordinate - in other words, that the brick is a single cube. + +Lines like `0,0,10~1,0,10` or `0,0,10~0,1,10` both represent bricks that are _two cubes_ in volume, both oriented horizontally. The first brick extends in the `x` direction, while the second brick extends in the `y` direction. + +A line like `0,0,1~0,0,10` represents a _ten-cube brick_ which is oriented _vertically_. One end of the brick is the cube located at `0,0,1`, while the other end of the brick is located directly above it at `0,0,10`. + +The ground is at `z=0` and is perfectly flat; the lowest `z` value a brick can have is therefore `1`. So, `5,5,1~5,6,1` and `0,2,1~0,2,5` are both resting on the ground, but `3,3,2~3,3,3` was above the ground at the time of the snapshot. + +Because the snapshot was taken while the bricks were still falling, some bricks will _still be in the air_; you'll need to start by figuring out where they will end up. Bricks are magically stabilized, so they _never rotate_, even in weird situations like where a long horizontal brick is only supported on one end. Two bricks cannot occupy the same position, so a falling brick will come to rest upon the first other brick it encounters. + +Here is the same example again, this time with each brick given a letter so it can be marked in diagrams: + +``` +1,0,1~1,2,1 <- A +0,0,2~2,0,2 <- B +0,2,3~2,2,3 <- C +0,0,4~0,2,4 <- D +2,0,5~2,2,5 <- E +0,1,6~2,1,6 <- F +1,1,8~1,1,9 <- G + +``` + +At the time of the snapshot, from the side so the `x` axis goes left to right, these bricks are arranged like this: + +``` + x +012 +.G. 9 +.G. 8 +... 7 +FFF 6 +..E 5 z +D.. 4 +CCC 3 +BBB 2 +.A. 1 +--- 0 + +``` + +Rotating the perspective 90 degrees so the `y` axis now goes left to right, the same bricks are arranged like this: + +``` + y +012 +.G. 9 +.G. 8 +... 7 +.F. 6 +EEE 5 z +DDD 4 +..C 3 +B.. 2 +AAA 1 +--- 0 + +``` + +Once all of the bricks fall downward as far as they can go, the stack looks like this, where `?` means bricks are hidden behind other bricks at that location: + +``` + x +012 +.G. 6 +.G. 5 +FFF 4 +D.E 3 z +??? 2 +.A. 1 +--- 0 + +``` + +Again from the side: + +``` + y +012 +.G. 6 +.G. 5 +.F. 4 +??? 3 z +B.C 2 +AAA 1 +--- 0 + +``` + +Now that all of the bricks have settled, it becomes easier to tell which bricks are supporting which other bricks: + +- Brick `A` is the only brick supporting bricks `B` and `C`. +- Brick `B` is one of two bricks supporting brick `D` and brick `E`. +- Brick `C` is the other brick supporting brick `D` and brick `E`. +- Brick `D` supports brick `F`. +- Brick `E` also supports brick `F`. +- Brick `F` supports brick `G`. +- Brick `G` isn't supporting any bricks. + +Your first task is to figure out _which bricks are safe to disintegrate_. A brick can be safely disintegrated if, after removing it, _no other bricks_ would fall further directly downward. Don't actually disintegrate any bricks - just determine what would happen if, for each brick, only that brick were disintegrated. Bricks can be disintegrated even if they're completely surrounded by other bricks; you can squeeze between bricks if you need to. + +In this example, the bricks can be disintegrated as follows: + +- Brick `A` cannot be disintegrated safely; if it were disintegrated, bricks `B` and `C` would both fall. +- Brick `B` _can_ be disintegrated; the bricks above it ( `D` and `E`) would still be supported by brick `C`. +- Brick `C` _can_ be disintegrated; the bricks above it ( `D` and `E`) would still be supported by brick `B`. +- Brick `D` _can_ be disintegrated; the brick above it ( `F`) would still be supported by brick `E`. +- Brick `E` _can_ be disintegrated; the brick above it ( `F`) would still be supported by brick `D`. +- Brick `F` cannot be disintegrated; the brick above it ( `G`) would fall. +- Brick `G` _can_ be disintegrated; it does not support any other bricks. + +So, in this example, `5` bricks can be safely disintegrated. + +Figure how the blocks will settle based on the snapshot. Once they've settled, consider disintegrating a single brick; _how many bricks could be safely chosen as the one to get disintegrated?_ \ No newline at end of file diff --git a/2023/22/code.go b/2023/22/code.go new file mode 100644 index 0000000..358ea49 --- /dev/null +++ b/2023/22/code.go @@ -0,0 +1,24 @@ +package main + +import ( + "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 { + return "not implemented" + } + // solve part 1 here + return 42 +} diff --git a/2023/22/input-example.txt b/2023/22/input-example.txt new file mode 100755 index 0000000..43a7fc5 --- /dev/null +++ b/2023/22/input-example.txt @@ -0,0 +1,7 @@ +1,0,1~1,2,1 +0,0,2~2,0,2 +0,2,3~2,2,3 +0,0,4~0,2,4 +2,0,5~2,2,5 +0,1,6~2,1,6 +1,1,8~1,1,9 diff --git a/2023/23/README.md b/2023/23/README.md new file mode 100755 index 0000000..e69bc02 --- /dev/null +++ b/2023/23/README.md @@ -0,0 +1,73 @@ +## \-\-\- Day 23: A Long Walk --- + +The Elves resume water filtering operations! Clean water starts flowing over the edge of Island Island. + +They offer to help _you_ go over the edge of Island Island, too! Just hold on tight to one end of this impossibly long rope and they'll lower you down a safe distance from the massive waterfall you just created. + +As you finally reach Snow Island, you see that the water isn't really reaching the ground: it's being _absorbed by the air_ itself. It looks like you'll finally have a little downtime while the moisture builds up to snow-producing levels. Snow Island is pretty scenic, even without any snow; why not take a walk? + +There's a map of nearby hiking trails (your puzzle input) that indicates _paths_ ( `.`), _forest_ ( `#`), and steep _slopes_ ( `^`, `>`, `v`, and `<`). + +For example: + +``` +#.##################### +#.......#########...### +#######.#########.#.### +###.....#.>.>.###.#.### +###v#####.#v#.###.#.### +###.>...#.#.#.....#...# +###v###.#.#.#########.# +###...#.#.#.......#...# +#####.#.#.#######.#.### +#.....#.#.#.......#...# +#.#####.#.#.#########v# +#.#...#...#...###...>.# +#.#.#v#######v###.###v# +#...#.>.#...>.>.#.###.# +#####v#.#.###v#.#.###.# +#.....#...#...#.#.#...# +#.#########.###.#.#.### +#...###...#...#...#.### +###.###.#.###v#####v### +#...#...#.#.>.>.#.>.### +#.###.###.#.###.#.#v### +#.....###...###...#...# +#####################.# + +``` + +You're currently on the single path tile in the top row; your goal is to reach the single path tile in the bottom row. Because of all the mist from the waterfall, the slopes are probably quite _icy_; if you step onto a slope tile, your next step must be _downhill_ (in the direction the arrow is pointing). To make sure you have the most scenic hike possible, _never step onto the same tile twice_. What is the longest hike you can take? + +In the example above, the longest hike you can take is marked with `O`, and your starting position is marked `S`: + +``` +#S##################### +#OOOOOOO#########...### +#######O#########.#.### +###OOOOO#OOO>.###.#.### +###O#####O#O#.###.#.### +###OOOOO#O#O#.....#...# +###v###O#O#O#########.# +###...#O#O#OOOOOOO#...# +#####.#O#O#######O#.### +#.....#O#O#OOOOOOO#...# +#.#####O#O#O#########v# +#.#...#OOO#OOO###OOOOO# +#.#.#v#######O###O###O# +#...#.>.#...>OOO#O###O# +#####v#.#.###v#O#O###O# +#.....#...#...#O#O#OOO# +#.#########.###O#O#O### +#...###...#...#OOO#O### +###.###.#.###v#####O### +#...#...#.#.>.>.#.>O### +#.###.###.#.###.#.#O### +#.....###...###...#OOO# +#####################O# + +``` + +This hike contains `94` steps. (The other possible hikes you could have taken were `90`, `86`, `82`, `82`, and `74` steps long.) + +Find the longest hike you can take through the hiking trails listed on your map. _How many steps long is the longest hike?_ \ No newline at end of file diff --git a/2023/23/code.go b/2023/23/code.go new file mode 100644 index 0000000..358ea49 --- /dev/null +++ b/2023/23/code.go @@ -0,0 +1,24 @@ +package main + +import ( + "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 { + return "not implemented" + } + // solve part 1 here + return 42 +} diff --git a/2023/23/input-example.txt b/2023/23/input-example.txt new file mode 100755 index 0000000..ea945a4 --- /dev/null +++ b/2023/23/input-example.txt @@ -0,0 +1,23 @@ +#.##################### +#.......#########...### +#######.#########.#.### +###.....#.>.>.###.#.### +###v#####.#v#.###.#.### +###.>...#.#.#.....#...# +###v###.#.#.#########.# +###...#.#.#.......#...# +#####.#.#.#######.#.### +#.....#.#.#.......#...# +#.#####.#.#.#########v# +#.#...#...#...###...>.# +#.#.#v#######v###.###v# +#...#.>.#...>.>.#.###.# +#####v#.#.###v#.#.###.# +#.....#...#...#.#.#...# +#.#########.###.#.#.### +#...###...#...#...#.### +###.###.#.###v#####v### +#...#...#.#.>.>.#.>.### +#.###.###.#.###.#.#v### +#.....###...###...#...# +#####################.# diff --git a/2023/24/README.md b/2023/24/README.md new file mode 100755 index 0000000..136fff9 --- /dev/null +++ b/2023/24/README.md @@ -0,0 +1,75 @@ +## \-\-\- 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?_ \ No newline at end of file diff --git a/2023/24/code.go b/2023/24/code.go new file mode 100644 index 0000000..c3d6996 --- /dev/null +++ b/2023/24/code.go @@ -0,0 +1,170 @@ +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\d+), +(?P\d+), +(?P\d+) @ +(?P-?\d+), +(?P-?\d+), +(?P-?\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 +} diff --git a/2023/24/input-example.txt b/2023/24/input-example.txt new file mode 100755 index 0000000..35963dc --- /dev/null +++ b/2023/24/input-example.txt @@ -0,0 +1,5 @@ +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