296 lines
4.8 KiB
Go
296 lines
4.8 KiB
Go
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")
|
|
}
|
|
}
|