package main import ( "errors" "fmt" "strings" "time" h "git.bullercodeworks.com/brian/adventofcode/helpers" ) var debug bool func main() { inp := h.StdinToStringSlice() part1(inp) part2(inp) } func Check(spot h.Coordinate, walls, sand *h.CoordByteMap) byte { if v := walls.Get(spot); v != 0 { return v } return sand.Get(spot) } func part1(inp []string) { m := BuildMap(inp) // Now start dropping sand var err error for err == nil { entry := &h.Coordinate{X: 500, Y: 0} err = Drop(entry, m) if err != nil { } else { m.Put(*entry, 'o') } } fmt.Println("# Part 1") fmt.Println(m.Count('o'), "grains of sand") } // Drop 'grain' into the field described by 'walls', and 'sand' // It returns the path that it took // If it flows off the map, error is returned func Drop(grain *h.Coordinate, m *h.CoordByteMap) error { var path []h.Coordinate prevX, prevY := grain.X, grain.Y for { if grain.South().Y > m.BRY { return errors.New("Overflow") } else if m.Get(grain.South()) == 0 { path = append(path, grain.South()) grain.MoveSouth() } else if m.Get(grain.SW()) == 0 { path = append(path, grain.SW()) grain.MoveSW() } else if m.Get(grain.SE()) == 0 { path = append(path, grain.SE()) grain.MoveSE() } if debug { fmt.Println(h.CLEAR_SCREEN) m.Put(*grain, 'o') fmt.Println(m) m.Delete(*grain) time.Sleep(time.Second / 100) } // Grain didn't move, must be at rest if grain.X == prevX && grain.Y == prevY { return nil } prevX, prevY = grain.X, grain.Y } } func part2(inp []string) { m := BuildMap(inp) m.BRY += 1 // Now start dropping sand var err error for err == nil { entry := &h.Coordinate{X: 500, Y: 0} err = Drop2(entry, m) m.Put(*entry, 'o') } fmt.Println("# Part 2") fmt.Println(m.Count('o'), "grains of sand") } func Drop2(grain *h.Coordinate, m *h.CoordByteMap) error { var path []h.Coordinate prevX, prevY := grain.X, grain.Y var moved bool for { if grain.Y == m.BRY { return nil } if m.Get(grain.South()) == 0 { path = append(path, grain.South()) grain.MoveSouth() moved = true } else if m.Get(grain.SW()) == 0 { path = append(path, grain.SW()) grain.MoveSW() moved = true } else if m.Get(grain.SE()) == 0 { path = append(path, grain.SE()) grain.MoveSE() moved = true } if debug { fmt.Println(h.CLEAR_SCREEN) m.Put(*grain, 'o') fmt.Println(m) m.Delete(*grain) time.Sleep(time.Second / 100) } if grain.X == prevX && grain.Y == prevY { // Grain didn't move, must be at rest if !moved { return errors.New("Full") } return nil } prevX, prevY = grain.X, grain.Y } } func BuildMap(inp []string) *h.CoordByteMap { m := h.NewCoordByteMap() m.TLY = 0 m.StringEmptyIsSpace = true m.StringEmptyByte = '.' for i := range inp { var start, end *h.Coordinate pts := strings.Split(inp[i], " -> ") for j := range pts { if start == nil { start = h.CoordinateFromString(pts[j]) } else if end == nil { end = h.CoordinateFromString(pts[j]) } if start != nil && end != nil { puts := GetLine(start, end) for _, coord := range puts { m.Put(*coord, '#') } start, end = end, nil } } } return &m } func GetLine(c1, c2 *h.Coordinate) []*h.Coordinate { var ret []*h.Coordinate if c1.X == c2.X { if c1.Y < c2.Y { for y := c1.Y; y <= c2.Y; y++ { ret = append(ret, &h.Coordinate{X: c1.X, Y: y}) } return ret } else if c1.Y > c2.Y { for y := c2.Y; y <= c1.Y; y++ { ret = append(ret, &h.Coordinate{X: c1.X, Y: y}) } } } else if c1.Y == c2.Y { if c1.X < c2.X { for x := c1.X; x <= c2.X; x++ { ret = append(ret, &h.Coordinate{X: x, Y: c1.Y}) } } else if c1.X > c2.X { for x := c2.X; x <= c1.X; x++ { ret = append(ret, &h.Coordinate{X: x, Y: c1.Y}) } } } return ret }