181 lines
3.8 KiB
Go
181 lines
3.8 KiB
Go
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
|
|
}
|