package main import ( "bufio" "fmt" "os" "strconv" "strings" "time" ) func mustAtoi(line string) int { i, _ := strconv.Atoi(line) return i } func main() { start := time.Now() partOne() duration := time.Since(start) partTwo() duration2 := time.Since(start) fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration) } func makeScanner(test bool) *bufio.Scanner { var f *os.File if test { f, _ = os.Open("inputs/testinput") } else { f, _ = os.Open("inputs/input") } reader := bufio.NewReader(f) return bufio.NewScanner(reader) } type coord struct { x int y int } func getRange(start, end int) []int { if start > end { s := make([]int, start-end+1) for i := range s { s[i] = start start-- } return s } if end > start { s := make([]int, end-start+1) for i := range s { s[i] = start start++ } return s } return []int{} } func printCave(cave map[coord]int, xmin, xmax, depth int, floor bool) { for _, d := range getRange(0, depth) { fmt.Printf("%3d ", d) for _, x := range getRange(xmin, xmax) { if d == 0 && x == 500 { fmt.Print("+") } else if score, ok := cave[coord{x: x, y: d}]; ok { if score == 1 { fmt.Print("#") } else if score == 2 { fmt.Print("o") } } else { fmt.Print(".") } } fmt.Printf("\n") } if floor { fmt.Printf("%3d ", depth+1) for range getRange(xmin, xmax) { fmt.Print("#") } fmt.Printf("\n") } } // return value is false if sand reached max depth func addSand(cave map[coord]int, depth int, floor bool) bool { x := 500 y := 0 for { if y > depth { if floor { // made it to the floor, come to rest cave[coord{x: x, y: y}] = 2 return true } return false } if _, blocked := cave[coord{x: x, y: y + 1}]; !blocked { y += 1 continue } if _, blocked := cave[coord{x: x - 1, y: y + 1}]; !blocked { y += 1 x -= 1 continue } if _, blocked := cave[coord{x: x + 1, y: y + 1}]; !blocked { y += 1 x += 1 continue } // filled the cavern, return false if y == 0 { cave[coord{x: x, y: y}] = 2 return false } // blocked in all directions, come to rest cave[coord{x: x, y: y}] = 2 return true } } func partOne() { scanner := makeScanner(false) cave := map[coord]int{} maxx, minx, depth := 0, 1000, 0 for scanner.Scan() { line := scanner.Text() coords := strings.Split(line, " -> ") prevx, prevy := 0, 0 for _, i := range coords { x, y, _ := strings.Cut(i, ",") if prevx == 0 && prevy == 0 { prevx, prevy = mustAtoi(x), mustAtoi(y) continue } if prevx == mustAtoi(x) { for _, curry := range getRange(prevy, mustAtoi(y)) { cave[coord{x: prevx, y: curry}] = 1 } prevy = mustAtoi(y) } else if prevy == mustAtoi(y) { for _, currx := range getRange(prevx, mustAtoi(x)) { cave[coord{x: currx, y: prevy}] = 1 } prevx = mustAtoi(x) } if mustAtoi(x) > maxx { maxx = mustAtoi(x) } else if mustAtoi(x) < minx { minx = mustAtoi(x) } if mustAtoi(y) > depth { depth = mustAtoi(y) } } } for addSand(cave, depth, false) { } printCave(cave, minx-1, maxx+1, depth+1, false) score := 0 for _, c := range cave { if c == 2 { score++ } } fmt.Println(score) } func partTwo() { scanner := makeScanner(false) cave := map[coord]int{} maxx, minx, depth := 0, 1000, 0 for scanner.Scan() { line := scanner.Text() coords := strings.Split(line, " -> ") prevx, prevy := 0, 0 for _, i := range coords { x, y, _ := strings.Cut(i, ",") if prevx == 0 && prevy == 0 { prevx, prevy = mustAtoi(x), mustAtoi(y) continue } if prevx == mustAtoi(x) { for _, curry := range getRange(prevy, mustAtoi(y)) { cave[coord{x: prevx, y: curry}] = 1 } prevy = mustAtoi(y) } else if prevy == mustAtoi(y) { for _, currx := range getRange(prevx, mustAtoi(x)) { cave[coord{x: currx, y: prevy}] = 1 } prevx = mustAtoi(x) } if mustAtoi(x) > maxx { maxx = mustAtoi(x) } else if mustAtoi(x) < minx { minx = mustAtoi(x) } if mustAtoi(y) > depth { depth = mustAtoi(y) } } } for addSand(cave, depth, true) { } printCave(cave, minx-1, maxx+1, depth+1, true) score := 0 for _, c := range cave { if c == 2 { score++ } } fmt.Println(score) }