mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-05-18 19:13:27 +02:00
2018 day17 crazy take on "trapping rainwater" with many buckets and weird physics
This commit is contained in:
Executable
+1713
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,292 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/alexchao26/advent-of-code-go/util"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var part int
|
||||
flag.IntVar(&part, "part", 1, "part 1 or 2")
|
||||
flag.Parse()
|
||||
fmt.Println("Running part", part)
|
||||
|
||||
if part == 1 {
|
||||
ans := part1(util.ReadFile("./input.txt"))
|
||||
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
||||
fmt.Println("Output:", ans)
|
||||
} else {
|
||||
ans := part2(util.ReadFile("./input.txt"))
|
||||
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
||||
fmt.Println("Output:", ans)
|
||||
}
|
||||
}
|
||||
|
||||
func part1(input string) int {
|
||||
grid, rowBounds := parseInputs(input)
|
||||
|
||||
pour(grid)
|
||||
|
||||
// // Uncomment to print part of the output
|
||||
// end := len(grid[0])
|
||||
// if end > 550 {
|
||||
// end = 550
|
||||
// }
|
||||
// for i, v := range grid {
|
||||
// fmt.Println(v[450:end])
|
||||
// if i >= 60 {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
|
||||
var ans int
|
||||
for r, row := range grid {
|
||||
for _, v := range row {
|
||||
// NOTE: used x's instead of ~'s because they're easier to see
|
||||
if (v == "|" || v == "x") && r <= rowBounds[1] && r >= rowBounds[0] {
|
||||
ans++
|
||||
}
|
||||
}
|
||||
}
|
||||
return ans
|
||||
}
|
||||
|
||||
func part2(input string) int {
|
||||
grid, rowBounds := parseInputs(input)
|
||||
|
||||
pour(grid)
|
||||
|
||||
var ans int
|
||||
for r, row := range grid {
|
||||
for _, v := range row {
|
||||
if v == "x" && r <= rowBounds[1] && r >= rowBounds[0] {
|
||||
ans++
|
||||
}
|
||||
}
|
||||
}
|
||||
return ans
|
||||
}
|
||||
|
||||
// x, y -> col, row...
|
||||
func parseInputs(input string) (grid [][]string, rowBounds [2]int) {
|
||||
lines := strings.Split(input, "\n")
|
||||
|
||||
verts, horis := [][3]int{}, [][3]int{}
|
||||
var largestX, largestY int
|
||||
lowestY := math.MaxInt32
|
||||
|
||||
for _, l := range lines {
|
||||
var char1, char2 string
|
||||
var num1, start, end int
|
||||
fmt.Sscanf(l, "%1s=%d, %1s=%d..%d", &char1, &num1, &char2, &start, &end)
|
||||
|
||||
if char1 == "x" { // vert
|
||||
verts = append(verts, [3]int{num1, start, end})
|
||||
if num1 > largestX {
|
||||
largestX = num1
|
||||
}
|
||||
if end > largestY {
|
||||
largestY = end
|
||||
}
|
||||
if start < lowestY {
|
||||
lowestY = start
|
||||
}
|
||||
} else {
|
||||
horis = append(horis, [3]int{num1, start, end})
|
||||
if num1 > largestY {
|
||||
largestY = num1
|
||||
}
|
||||
if num1 < lowestY {
|
||||
lowestY = num1
|
||||
}
|
||||
if end > largestX {
|
||||
largestX = end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grid = make([][]string, largestY+1)
|
||||
for i := range grid {
|
||||
grid[i] = make([]string, largestX+1)
|
||||
}
|
||||
|
||||
for _, coords := range verts {
|
||||
col, start, end := coords[0], coords[1], coords[2]
|
||||
for i := start; i <= end; i++ {
|
||||
grid[i][col] = "#"
|
||||
}
|
||||
}
|
||||
for _, coords := range horis {
|
||||
row, start, end := coords[0], coords[1], coords[2]
|
||||
for i := start; i <= end; i++ {
|
||||
grid[row][i] = "#"
|
||||
}
|
||||
}
|
||||
|
||||
for i, row := range grid {
|
||||
for j := range row {
|
||||
if grid[i][j] != "#" {
|
||||
grid[i][j] = "."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add an empty row ot the bottom
|
||||
|
||||
grid = append(grid, make([]string, len(grid[0])))
|
||||
for c := range grid[0] {
|
||||
grid[len(grid)-1][c] = "."
|
||||
}
|
||||
|
||||
return grid, [2]int{lowestY, largestY}
|
||||
}
|
||||
|
||||
func pour(grid [][]string) {
|
||||
// stack stores the coordinates that have been poured into
|
||||
// the stack is used ot backtrack to previous cells to see if they can
|
||||
// pour into additional spaces
|
||||
stack := [][2]int{{0, 500}}
|
||||
|
||||
for len(stack) > 0 {
|
||||
// take coordinate at top of stack
|
||||
top := stack[len(stack)-1]
|
||||
currentVal := grid[top[0]][top[1]]
|
||||
|
||||
// if it's a wall, pop it and continue
|
||||
if currentVal == "#" {
|
||||
stack = stack[:len(stack)-1]
|
||||
continue
|
||||
}
|
||||
|
||||
down := getNextCoord(top, "down")
|
||||
|
||||
// ensure it's in bounds, if not pop and continue
|
||||
if !isInBounds(grid, down) {
|
||||
stack = stack[:len(stack)-1]
|
||||
continue
|
||||
}
|
||||
|
||||
// this will happen on the second visit to a coordinate (it has to be
|
||||
// changed into a pipe first @ the bottom of this loop)
|
||||
if currentVal == "|" {
|
||||
// transform will check if the row below (down variable) is water (pipes)
|
||||
// bound by walls (assume that there isn't a sneaky hole in the floor)
|
||||
// if it is bound by walls, it will replace all pipes with x's to
|
||||
// indicate still water
|
||||
transformStillWater(grid, down)
|
||||
|
||||
// pop off stack
|
||||
stack = stack[:len(stack)-1]
|
||||
|
||||
// continue with the rest of the loop to add coords on the stack
|
||||
// this handles two cases in particular
|
||||
// |
|
||||
// | pouring over to the right side here
|
||||
// # | | #
|
||||
// # | v #
|
||||
// #-----###..#
|
||||
// #-----# #..#
|
||||
// #-----###..#
|
||||
// #----------#
|
||||
// ############
|
||||
//
|
||||
// |
|
||||
// |
|
||||
// # | #
|
||||
// # | #
|
||||
// #..|.......#
|
||||
// #..|.......#
|
||||
// #..|.......# <- filling up this row
|
||||
// #----------#
|
||||
// ############
|
||||
}
|
||||
|
||||
// if below is a wall, append left and right to the stack
|
||||
valDown := getValAt(grid, top, "down")
|
||||
|
||||
// add left and right to stack if they're sand
|
||||
if valDown == "#" || valDown == "x" {
|
||||
if getValAt(grid, top, "left") == "." {
|
||||
stack = append(stack, getNextCoord(top, "left"))
|
||||
}
|
||||
if getValAt(grid, top, "right") == "." {
|
||||
stack = append(stack, getNextCoord(top, "right"))
|
||||
}
|
||||
}
|
||||
// if down is sand, add it to stack
|
||||
if valDown == "." {
|
||||
stack = append(stack, down)
|
||||
}
|
||||
|
||||
// make self a water pipe
|
||||
grid[top[0]][top[1]] = "|"
|
||||
}
|
||||
}
|
||||
|
||||
// helper functions to make getting the next coordinate or its value or if its in bounds
|
||||
func isInBounds(grid [][]string, coord [2]int) bool {
|
||||
return coord[0] < len(grid) && coord[1] < len(grid[0])
|
||||
}
|
||||
|
||||
func getNextCoord(coord [2]int, direction string) [2]int {
|
||||
if !strings.Contains("downleftright", direction) {
|
||||
panic("invalid direction passed to getNextCoord")
|
||||
}
|
||||
|
||||
switch direction {
|
||||
case "down":
|
||||
return [2]int{coord[0] + 1, coord[1]}
|
||||
case "left":
|
||||
return [2]int{coord[0], coord[1] - 1}
|
||||
case "right":
|
||||
return [2]int{coord[0], coord[1] + 1}
|
||||
}
|
||||
|
||||
return [2]int{} // should never be hit...
|
||||
}
|
||||
|
||||
func getValAt(grid [][]string, coord [2]int, direction string) string {
|
||||
nextCoord := getNextCoord(coord, direction)
|
||||
|
||||
return grid[nextCoord[0]][nextCoord[1]]
|
||||
}
|
||||
|
||||
// check a particular row to see if it can be transformed into still water
|
||||
// i.e. is it all water pipes bound by walls on either end
|
||||
func transformStillWater(grid [][]string, coord [2]int) {
|
||||
var left, right int
|
||||
isWalled := true
|
||||
for col := coord[1] - 1; isInBounds(grid, [2]int{coord[0], col}); col-- {
|
||||
if grid[coord[0]][col] == "#" {
|
||||
left = col + 1
|
||||
break
|
||||
}
|
||||
if grid[coord[0]][col] == "." {
|
||||
isWalled = false
|
||||
break
|
||||
}
|
||||
}
|
||||
for col := coord[1] + 1; isInBounds(grid, [2]int{coord[0], col}); col++ {
|
||||
if grid[coord[0]][col] == "#" {
|
||||
right = col - 1
|
||||
break
|
||||
}
|
||||
if grid[coord[0]][col] == "." {
|
||||
isWalled = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isWalled {
|
||||
for i := left; i <= right; i++ {
|
||||
// only transform waters, not preexisting floors
|
||||
if grid[coord[0]][i] == "|" {
|
||||
grid[coord[0]][i] = "x"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alexchao26/advent-of-code-go/util"
|
||||
)
|
||||
|
||||
var tests1 = []struct {
|
||||
name string
|
||||
want int
|
||||
input string
|
||||
// add extra args if needed
|
||||
}{
|
||||
{"example", 57, `x=495, y=2..7
|
||||
y=7, x=495..501
|
||||
x=501, y=3..7
|
||||
x=498, y=2..4
|
||||
x=506, y=1..2
|
||||
x=498, y=10..13
|
||||
x=504, y=10..13
|
||||
y=13, x=498..504`},
|
||||
{"actual", 38364, util.ReadFile("input.txt")},
|
||||
}
|
||||
|
||||
func TestPart1(t *testing.T) {
|
||||
for _, tt := range tests1 {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := part1(tt.input)
|
||||
if got != tt.want {
|
||||
t.Errorf("got %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var tests2 = []struct {
|
||||
name string
|
||||
want int
|
||||
input string
|
||||
// add extra args if needed
|
||||
}{
|
||||
{"example", 29, `x=495, y=2..7
|
||||
y=7, x=495..501
|
||||
x=501, y=3..7
|
||||
x=498, y=2..4
|
||||
x=506, y=1..2
|
||||
x=498, y=10..13
|
||||
x=504, y=10..13
|
||||
y=13, x=498..504`},
|
||||
{"actual", 30551, util.ReadFile("input.txt")},
|
||||
}
|
||||
|
||||
func TestPart2(t *testing.T) {
|
||||
for _, tt := range tests2 {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := part2(tt.input)
|
||||
if got != tt.want {
|
||||
t.Errorf("got %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Executable
+195
@@ -0,0 +1,195 @@
|
||||
|
||||
--- Day 17: Reservoir Research ---
|
||||
You arrive in the year 18. If it weren't for the coat you got in 1018, you would be very cold: the North Pole base hasn't even been constructed.
|
||||
|
||||
|
||||
Rather, it hasn't been constructed yet. The Elves are making a little progress, but there's not a lot of liquid water in this climate, so they're getting very dehydrated. Maybe there's more underground?
|
||||
|
||||
|
||||
You scan a two-dimensional vertical slice of the ground nearby and discover that it is mostly sand with veins of clay. The scan only provides data with a granularity of square meters, but it should be good enough to determine how much water is trapped there. In the scan, x represents the distance to the right, and y represents the distance down. There is also a spring of water near the surface at x=500, y=0. The scan identifies which square meters are clay (your puzzle input).
|
||||
|
||||
|
||||
For example, suppose your scan shows the following veins of clay:
|
||||
|
||||
|
||||
x=495, y=2..7
|
||||
y=7, x=495..501
|
||||
x=501, y=3..7
|
||||
x=498, y=2..4
|
||||
x=506, y=1..2
|
||||
x=498, y=10..13
|
||||
x=504, y=10..13
|
||||
y=13, x=498..504
|
||||
|
||||
|
||||
|
||||
Rendering clay as #, sand as ., and the water spring as +, and with x increasing to the right and y increasing downward, this becomes:
|
||||
|
||||
|
||||
44444455555555
|
||||
99999900000000
|
||||
45678901234567
|
||||
0 ......+.......
|
||||
1 ............#.
|
||||
2 .#..#.......#.
|
||||
3 .#..#..#......
|
||||
4 .#..#..#......
|
||||
5 .#.....#......
|
||||
6 .#.....#......
|
||||
7 .#######......
|
||||
8 ..............
|
||||
9 ..............
|
||||
10 ....#.....#...
|
||||
11 ....#.....#...
|
||||
12 ....#.....#...
|
||||
13 ....#######...
|
||||
|
||||
|
||||
|
||||
The spring of water will produce water forever. Water can move through sand, but is blocked by clay. Water always moves down when possible, and spreads to the left and right otherwise, filling space that has clay on both sides and falling out otherwise.
|
||||
|
||||
|
||||
For example, if five squares of water are created, they will flow downward until they reach the clay and settle there. Water that has come to rest is shown here as ~, while sand through which water has passed (but which is now dry again) is shown as |:
|
||||
|
||||
|
||||
......+.......
|
||||
......|.....#.
|
||||
.#..#.|.....#.
|
||||
.#..#.|#......
|
||||
.#..#.|#......
|
||||
.#....|#......
|
||||
.#~~~~~#......
|
||||
.#######......
|
||||
..............
|
||||
..............
|
||||
....#.....#...
|
||||
....#.....#...
|
||||
....#.....#...
|
||||
....#######...
|
||||
|
||||
|
||||
|
||||
Two squares of water can't occupy the same location. If another five squares of water are created, they will settle on the first five, filling the clay reservoir a little more:
|
||||
|
||||
|
||||
......+.......
|
||||
......|.....#.
|
||||
.#..#.|.....#.
|
||||
.#..#.|#......
|
||||
.#..#.|#......
|
||||
.#~~~~~#......
|
||||
.#~~~~~#......
|
||||
.#######......
|
||||
..............
|
||||
..............
|
||||
....#.....#...
|
||||
....#.....#...
|
||||
....#.....#...
|
||||
....#######...
|
||||
|
||||
|
||||
|
||||
Water pressure does not apply in this scenario. If another four squares of water are created, they will stay on the right side of the barrier, and no water will reach the left side:
|
||||
|
||||
|
||||
......+.......
|
||||
......|.....#.
|
||||
.#..#.|.....#.
|
||||
.#..#~~#......
|
||||
.#..#~~#......
|
||||
.#~~~~~#......
|
||||
.#~~~~~#......
|
||||
.#######......
|
||||
..............
|
||||
..............
|
||||
....#.....#...
|
||||
....#.....#...
|
||||
....#.....#...
|
||||
....#######...
|
||||
|
||||
|
||||
|
||||
At this point, the top reservoir overflows. While water can reach the tiles above the surface of the water, it cannot settle there, and so the next five squares of water settle like this:
|
||||
|
||||
|
||||
......+.......
|
||||
......|.....#.
|
||||
.#..#||||...#.
|
||||
.#..#~~#|.....
|
||||
.#..#~~#|.....
|
||||
.#~~~~~#|.....
|
||||
.#~~~~~#|.....
|
||||
.#######|.....
|
||||
........|.....
|
||||
........|.....
|
||||
....#...|.#...
|
||||
....#...|.#...
|
||||
....#~~~~~#...
|
||||
....#######...
|
||||
|
||||
|
||||
|
||||
Note especially the leftmost |: the new squares of water can reach this tile, but cannot stop there. Instead, eventually, they all fall to the right and settle in the reservoir below.
|
||||
|
||||
|
||||
After 10 more squares of water, the bottom reservoir is also full:
|
||||
|
||||
|
||||
......+.......
|
||||
......|.....#.
|
||||
.#..#||||...#.
|
||||
.#..#~~#|.....
|
||||
.#..#~~#|.....
|
||||
.#~~~~~#|.....
|
||||
.#~~~~~#|.....
|
||||
.#######|.....
|
||||
........|.....
|
||||
........|.....
|
||||
....#~~~~~#...
|
||||
....#~~~~~#...
|
||||
....#~~~~~#...
|
||||
....#######...
|
||||
|
||||
|
||||
|
||||
Finally, while there is nowhere left for the water to settle, it can reach a few more tiles before overflowing beyond the bottom of the scanned data:
|
||||
|
||||
|
||||
......+....... (line not counted: above minimum y value)
|
||||
......|.....#.
|
||||
.#..#||||...#.
|
||||
.#..#~~#|.....
|
||||
.#..#~~#|.....
|
||||
.#~~~~~#|.....
|
||||
.#~~~~~#|.....
|
||||
.#######|.....
|
||||
........|.....
|
||||
...|||||||||..
|
||||
...|#~~~~~#|..
|
||||
...|#~~~~~#|..
|
||||
...|#~~~~~#|..
|
||||
...|#######|..
|
||||
...|.......|.. (line not counted: below maximum y value)
|
||||
...|.......|.. (line not counted: below maximum y value)
|
||||
...|.......|.. (line not counted: below maximum y value)
|
||||
|
||||
|
||||
|
||||
How many tiles can be reached by the water? To prevent counting forever, ignore tiles with a y coordinate smaller than the smallest y coordinate in your scan data or larger than the largest one. Any x coordinate is valid. In this example, the lowest y coordinate given is 1, and the highest is 13, causing the water spring (in row 0) and the water falling off the bottom of the render (in rows 14 through infinity) to be ignored.
|
||||
|
||||
|
||||
So, in the example above, counting both water at rest (~) and other sand tiles the water can hypothetically reach (|), the total number of tiles the water can reach is 57.
|
||||
|
||||
|
||||
How many tiles can the water reach within the range of y values in your scan?
|
||||
|
||||
|
||||
--- Part Two ---
|
||||
After a very long time, the water spring will run dry. How much water will be retained?
|
||||
|
||||
|
||||
In the example above, water that won't eventually drain out is shown as ~, a total of 29 tiles.
|
||||
|
||||
|
||||
How many water tiles are left after the water spring stops producing water and all remaining water not at rest has drained?
|
||||
|
||||
Reference in New Issue
Block a user