Files
advent2022/solutions/day14/day14.go
2022-12-23 19:45:56 -08:00

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")
}
}