package day14 import ( "bufio" "errors" "fmt" "os" "strconv" "strings" ) type Point struct { x int y int } func parsePoint(v string) (result Point, err error) { var parts []string = strings.Split(v, ",") if len(parts) != 2 { err = errors.New("Weird number of parts in point") return } x, xerr := strconv.Atoi(parts[0]) if xerr != nil { err = xerr return } y, yerr := strconv.Atoi(parts[1]) if yerr != nil { err = yerr return } result = Point{x, y} return } type Segment struct { start Point end Point } func parseSegments(line string) (result []Segment, err error) { parts := strings.Split(line, " -> ") result = []Segment{} var left *Point = nil for _, rightString := range parts { right, rerr := parsePoint(rightString) if rerr != nil { err = rerr return } if left != nil { result = append(result, Segment{*left, right}) } left = &right } return } func segmentPoints(segments []Segment) []Point { retval := []Point{} for _, segment := range segments { retval = append(retval, segment.start, segment.end) } return retval } func findExtents(segments []Segment) (leftmost int, rightmost int, bottom int) { leftmost = int(^uint(0) >> 1) rightmost = 0 bottom = 0 for _, point := range segmentPoints(segments) { if point.x < leftmost { leftmost = point.x } if point.x > rightmost { rightmost = point.x } if point.y > bottom { bottom = point.y } } return } type Cell int const ( Air Cell = iota Stone Sand Start ) func drawGraph(graph [][]Cell) { for _, line := range graph { for _, v := range line { switch v { case Air: fmt.Printf(".") case Stone: fmt.Printf("#") case Sand: fmt.Printf("O") case Start: fmt.Printf("+") default: fmt.Printf("?") } } fmt.Println() } fmt.Println() } type State int const ( Good State = iota Stop Done ) func nextStep(curX int, curY int, graph [][]Cell) (nextX int, nextY int, state State) { if curY+1 >= len(graph) { state = Done return } if graph[curY+1][curX] == Air { nextX = curX nextY = curY + 1 state = Good return } if curX == 0 { state = Done return } if graph[curY+1][curX-1] == Air { nextX = curX - 1 nextY = curY + 1 state = Good return } if curX+1 >= len(graph[0]) { state = Done return } if graph[curY+1][curX+1] == Air { nextX = curX + 1 nextY = curY + 1 state = Good return } state = Stop return } func dropGrain(startX int, graph [][]Cell) bool { curX := startX curY := 0 for true { nextX, nextY, result := nextStep(curX, curY, graph) switch result { case Done: return true case Stop: result := graph[curY][curX] == Start graph[curY][curX] = Sand return result case Good: curX = nextX curY = nextY } } return true } func Run(filename string) { file, err := os.Open(filename) if err != nil { fmt.Println("Error opening file:", err) return } scanner := bufio.NewScanner(file) scanner.Split(bufio.ScanLines) segments := []Segment{} for scanner.Scan() { line := scanner.Text() newSegments, err := parseSegments(line) if err != nil { fmt.Println("PANIC", err) return } for _, segment := range newSegments { segments = append(segments, segment) } } file.Close() fmt.Println(segments) leftmostX, rightmostX, bottomY := findExtents(segments) fmt.Println("Leftmost X value is", leftmostX) fmt.Println("Rightmost X value is", rightmostX) fmt.Println("Bottom Y value is", bottomY) leftmostX = 0 rightmostX += 500 width := rightmostX - leftmostX graph := [][]Cell{} for y := 0; y <= bottomY+2; y++ { line := []Cell{} for x := 0; x <= width; x++ { line = append(line, Air) } graph = append(graph, line) } segments = append(segments, Segment{Point{leftmostX, bottomY + 2}, Point{rightmostX, bottomY + 2}}) for _, segment := range segments { if segment.start.x == segment.end.x { var top int var bottom int if segment.start.y < segment.end.y { top = segment.start.y bottom = segment.end.y } else { top = segment.end.y bottom = segment.start.y } for current := top; current <= bottom; current++ { graph[current][segment.start.x-leftmostX] = Stone } continue } if segment.start.y == segment.end.y { var left int var right int if segment.start.x < segment.end.x { left = segment.start.x right = segment.end.x } else { left = segment.end.x right = segment.start.x } for current := left; current <= right; current++ { graph[segment.start.y][current-leftmostX] = Stone } continue } fmt.Println("PANIC: Diagonal line!?") return } graph[0][500-leftmostX] = Start drawGraph(graph) count := 0 for !dropGrain(500-leftmostX, graph) { count += 1 } drawGraph(graph) fmt.Println("Dropped", count, "grains of sand") if graph[0][500-leftmostX] != Sand { fmt.Println("PANIC: NOT WIDE ENOUGH") } }