🌅
This commit is contained in:
295
solutions/day14/day14.go
Normal file
295
solutions/day14/day14.go
Normal file
@@ -0,0 +1,295 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user