adventofcode/2022/day14/main.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
}