Day - 14
Part 1 - Code
package main
import (
"bufio"
"fmt"
"log"
"os"
"strconv"
"strings"
)
var year = 2022
var day = 14
var path = os.Getenv("ADVENT_HOME")
type Tuple struct {
x, y int
}
func (t Tuple) String() string {
return fmt.Sprintf("(%d,%d)", t.x, t.y)
}
func TupleFromStr(str string) Tuple {
tokens := strings.Split(str, ",")
valX, _ := strconv.Atoi(tokens[0])
valY, _ := strconv.Atoi(tokens[1])
return Tuple{valX, valY}
}
func getLineBetweenPaths(t1 Tuple, t2 Tuple) []Tuple {
path := []Tuple{}
if t1.x == t2.x {
sy := t1.y
ey := t2.y
if sy < ey {
for i := sy; i <= ey; i += 1 {
path = append(path, Tuple{t1.x, i})
}
} else {
for i := sy; i >= ey; i -= 1 {
path = append(path, Tuple{t1.x, i})
}
}
} else {
sx := t1.x
ex := t2.x
if sx < ex {
for i := sx; i <= ex; i += 1 {
path = append(path, Tuple{i, t1.y})
}
} else {
for i := sx; i >= ex; i -= 1 {
path = append(path, Tuple{i, t1.y})
}
}
}
return path
}
func getPath(dataLine string) []Tuple {
path := []Tuple{}
DELIMETER := " -> "
tokens := strings.Split(dataLine, DELIMETER)
segments := []Tuple{}
for _, token := range tokens {
tuple := TupleFromStr(token)
segments = append(segments, tuple)
}
for i := 0; i < len(segments)-1; i++ {
left, right := segments[i], segments[i+1]
straight := getLineBetweenPaths(left, right)
for _, t := range straight {
path = append(path, t)
}
}
return path
}
func constructGridMap(data []string) map[Tuple]string {
grid := make(map[Tuple]string)
for _, dataLine := range data {
for _, tuple := range getPath(dataLine) {
grid[tuple] = "#"
}
}
return grid
}
func getDepthBounds(m map[Tuple]string) int {
maxY := 0
for key, _ := range m {
if maxY < key.y {
maxY = key.y
}
}
return maxY
}
func FallDown(sand Tuple, grid map[Tuple]string) Tuple {
belowL, belowM, belowR := Tuple{sand.x - 1, sand.y + 1}, Tuple{sand.x, sand.y + 1}, Tuple{sand.x + 1, sand.y + 1}
_, okL := grid[belowL]
_, okM := grid[belowM]
if !okM {
// fmt.Printf("current = %s, next = %s\n", sand, belowM)
return belowM
}
if !okL {
// fmt.Printf("current = %s, next = %s\n", sand, belowL)
return belowL
}
// fmt.Printf("current = %s, next = %s\n", sand, belowR)
return belowR
}
func CanFallDown(sand Tuple, grid map[Tuple]string, maxY int) (bool, bool) {
nextY := sand.y + 1
if nextY > maxY {
return true, true
}
belowL, belowM, belowR := Tuple{sand.x - 1, nextY}, Tuple{sand.x, nextY}, Tuple{sand.x + 1, nextY}
_, okL := grid[belowL]
_, okM := grid[belowM]
_, okR := grid[belowR]
return !okL || !okM || !okR, false
}
func PrintGrid(grid map[Tuple]string) {
maxX, maxY := 0, 0
for key, _ := range grid {
if maxX < key.x {
maxX = key.x
}
if maxY < key.y {
maxY = key.y
}
}
minX, minY := maxX, maxY
for key, _ := range grid {
if minX > key.x {
minX = key.x
}
if minY > key.y {
minY = key.y
}
}
out := ""
for j := minY; j <= maxY; j++ {
line := ""
for i := minX; i <= maxX; i++ {
tuple := Tuple{i, j}
val, exists := grid[tuple]
if !exists {
line += "."
} else {
line += val
}
}
out += line + "\n"
}
fmt.Print(out)
}
func simulateP1(grid map[Tuple]string, source Tuple, maxDepth int) int {
end := false
for !end {
falling := true
sand := Tuple{source.x, source.y}
for falling {
if canFall, outOfBounds := CanFallDown(sand, grid, maxDepth); !outOfBounds {
if canFall {
sand = FallDown(sand, grid)
} else {
grid[sand] = "o"
falling = false
}
} else {
falling = false
end = true
}
}
}
rockCount := 0
for _, val := range grid {
if val == "o" {
rockCount += 1
}
}
// fmt.Println()
// PrintGrid(grid)
// fmt.Println()
return rockCount
}
func CanFallDownWithFloor(sand Tuple, grid map[Tuple]string, floor int) bool {
nextY := sand.y + 1
if nextY == floor {
return false
}
belowL, belowM, belowR := Tuple{sand.x - 1, nextY}, Tuple{sand.x, nextY}, Tuple{sand.x + 1, nextY}
_, okL := grid[belowL]
_, okM := grid[belowM]
_, okR := grid[belowR]
return !okL || !okM || !okR
}
func simulateP2(grid map[Tuple]string, source Tuple, maxDepth int) int {
end := false
floorY := maxDepth + 2
for !end {
falling := true
sand := Tuple{source.x, source.y}
_, sourceBlocked := grid[sand]
if sourceBlocked {
end = true
break
}
for falling {
if canFall := CanFallDownWithFloor(sand, grid, floorY); canFall {
sand = FallDown(sand, grid)
} else {
grid[sand] = "o"
falling = false
}
}
}
rockCount := 0
for _, val := range grid {
if val == "o" {
rockCount += 1
}
}
// fmt.Println()
// PrintGrid(grid)
// fmt.Println()
return rockCount
}
func main() {
dataPath := fmt.Sprintf("%s/%d/data/day%d/data.txt", path, year, day)
fmt.Println(dataPath)
file, err := os.Open(dataPath)
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
data := []string{}
for scanner.Scan() {
line := scanner.Text()
data = append(data, line)
}
gridMap := constructGridMap(data)
maxY := getDepthBounds(gridMap)
fmt.Printf("maxY = %d\n", maxY)
sandCount := simulateP1(gridMap, Tuple{500, 0}, maxY)
fmt.Printf("sandCount = %d\n", sandCount)
sandCount = simulateP2(gridMap, Tuple{500, 0}, maxY)
fmt.Printf("sandCount = %d\n", sandCount)
}