2018 day 13 complete!

This commit is contained in:
2018-12-13 12:13:56 -06:00
parent c94dfb5958
commit 52629a7b9c
2 changed files with 353 additions and 268 deletions

View File

@@ -5,311 +5,145 @@ import (
"fmt"
"os"
"sort"
"time"
)
const (
DIR_N = iota
DIR_E
DIR_S
DIR_W
DIR_ERR
DIR_N = -1i
DIR_E = 1
DIR_S = 1i
DIR_W = -1
TURN_L = iota
TURN_S
TURN_R
TURN_ERR
ClearScreen = "\033[H\033[2J"
TURN_L = -1i
TURN_S = 1
TURN_R = 1i
)
var course [][]Track
var carts []*Pos
var width int
var input []byte
var carts []*complex64
var cartMap map[complex64]*Cart
func main() {
inp := StdinToStringSlice()
part1(inp)
stdinToByteSlice()
setupCarts()
//part1()
part2()
}
func part1(inp []string) {
course, carts = initScrubTrack(inp)
var done bool
for i := 0; ; i++ {
printState()
tick()
func part1() {
var crashed bool
for !crashed {
sort.Sort(ByPos(carts))
for _, c := range carts {
if c.dir == DIR_ERR {
fmt.Println("= Part 1 =")
fmt.Println(c.string())
fmt.Println(i, "ticks")
done = true
break
}
}
if done {
break
}
if i%1000000 == 0 {
fmt.Print(".")
}
time.Sleep(time.Millisecond * 250)
}
}
func tick() {
// sort the carts list by y/x
sort.Sort(ByPos(carts))
for _, c := range carts {
switch c.dir {
case DIR_N:
t := getTrackAt(c.x, c.y-1)
if t.goesDown() {
c.y--
if byte(t) == '+' {
c.chooseTurn()
} else if byte(t) == '\\' {
c.dir = DIR_W
} else if byte(t) == '/' {
c.dir = DIR_E
}
}
case DIR_E:
t := getTrackAt(c.x+1, c.y)
if t.goesLeft() {
c.x++
if byte(t) == '+' {
c.chooseTurn()
} else if byte(t) == '/' {
c.dir = DIR_N
} else if byte(t) == '\\' {
c.dir = DIR_S
}
}
case DIR_S:
t := getTrackAt(c.x, c.y+1)
if t.goesUp() {
c.y++
if byte(t) == '+' {
c.chooseTurn()
} else if byte(t) == '\\' {
c.dir = DIR_E
} else if byte(t) == '/' {
c.dir = DIR_W
}
}
case DIR_W:
t := getTrackAt(c.x-1, c.y)
if t.goesRight() {
c.x--
if byte(t) == '+' {
c.chooseTurn()
} else if byte(t) == '/' {
c.dir = DIR_S
} else if byte(t) == '\\' {
c.dir = DIR_N
}
if !cartMap[*c].tick() {
return
}
}
}
}
func getTrackAt(x, y int) Track {
if x < 0 || y < 0 || y >= len(course) || x >= len(course[0]) {
return Track(' ')
}
return course[y][x]
}
func findCrash() (Pos, bool) {
for _, c1 := range carts {
for _, c2 := range carts {
if c1 == c2 {
continue
}
if c1.x == c2.x && c1.y == c2.y {
return Pos{x: c1.x, y: c1.y}, true
func part2() {
for len(cartMap) > 1 {
sort.Sort(ByPos(carts))
for _, c := range carts {
if *c != 0 {
cartMap[*c].tick()
}
}
}
return Pos{x: -1, y: -1}, false
for pos := range cartMap {
fmt.Printf("🔵: %0.0f,%0.0f\n", real(pos), imag(pos))
}
}
func printState() {
fmt.Print(ClearScreen)
for y, v := range course {
for x, t := range v {
var foundCarts []*Pos
for _, c := range carts {
if c.x == x && c.y == y {
foundCarts = append(foundCarts, c)
}
}
if len(foundCarts) == 0 {
fmt.Print(string(t))
} else if len(foundCarts) == 1 {
fmt.Print(string(foundCarts[0].getCartByte()))
} else {
fmt.Print("X")
for _, c := range foundCarts {
c.dir = DIR_ERR
}
}
type Cart struct {
pos complex64
dir complex64
nextTurn complex64
}
// Tick the cart, return if it's still alive
func (c *Cart) tick() bool {
// Remove this cart from it's old position
// and figure out it's new one
delete(cartMap, c.pos)
c.pos += c.dir
if cart, ok := cartMap[c.pos]; ok {
fmt.Printf("❌: %0.0f,%0.0f\n", real(c.pos), imag(c.pos))
// Remove the cart we crashed into from the map
delete(cartMap, c.pos)
// And clear the cart's pos, we can ignore them now
cart.pos, c.pos = 0, 0
return false
}
// No crash, figure out the new orientation of the cart
cartMap[c.pos] = c
if getByte(c.pos) == '+' {
c.dir *= c.nextTurn
switch c.nextTurn {
case TURN_L:
c.nextTurn = TURN_S
case TURN_S:
c.nextTurn = TURN_R
case TURN_R:
c.nextTurn = TURN_L
}
fmt.Println("")
} else if getByte(c.pos) == '/' {
// Turn counter-clockwise
c.dir = complex(-imag(c.dir), -real(c.dir))
} else if getByte(c.pos) == '\\' {
// Turn clockwise
c.dir = complex(imag(c.dir), real(c.dir))
}
return true
}
type Pos struct {
x, y int
dir int
nextTurn int
// We have to sort the carts on each tick by y,x position
// y is the imaginary part, x is the real part
type ByPos []*complex64
func (c ByPos) Len() int { return len(c) }
func (c ByPos) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c ByPos) Less(i, j int) bool {
return imag(*c[i]) < imag(*c[j]) ||
(imag(*c[i]) == imag(*c[j]) && real(*c[i]) < real(*c[j]))
}
type ByPos []*Pos
func (b ByPos) Len() int { return len(b) }
func (b ByPos) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b ByPos) Less(i, j int) bool {
return b[i].y < b[j].y || (b[i].y == b[j].y && b[i].x < b[j].x)
}
// Choose a turn based on nextTurn and execute it
func (p *Pos) chooseTurn() {
if p.nextTurn == TURN_S {
p.nextTurn = TURN_R
} else if p.nextTurn == TURN_R {
p.turnRight()
p.nextTurn = TURN_L
} else { // Default to turn left
p.turnLeft()
p.nextTurn = TURN_S
}
}
func (p *Pos) turnLeft() {
p.dir--
if p.dir < DIR_N {
p.dir = DIR_W
}
}
func (p *Pos) turnRight() {
p.dir++
if p.dir > DIR_W {
p.dir = DIR_N
}
}
func (p *Pos) string() string {
return fmt.Sprintf("(%2d,%2d: %s)", p.x, p.y, string(p.getCartByte()))
}
func (p *Pos) getCartByte() byte {
switch p.dir {
case DIR_N:
return '^'
case DIR_E:
return '>'
case DIR_S:
return 'v'
case DIR_W:
return '<'
}
return ' '
}
type Track byte
func (t Track) isEmpty() bool {
return byte(t) == ' '
}
func (t Track) goesUp() bool {
return byte(t) == '|' || byte(t) == '/' || byte(t) == '\\' || byte(t) == '+'
}
func (t Track) goesDown() bool {
return byte(t) == '|' || byte(t) == '/' || byte(t) == '\\' || byte(t) == '+'
}
func (t Track) goesRight() bool {
return byte(t) == '-' || byte(t) == '\\' || byte(t) == '/' || byte(t) == '+'
}
func (t Track) goesLeft() bool {
return byte(t) == '-' || byte(t) == '\\' || byte(t) == '/' || byte(t) == '+'
// getByte pulls a byte from the given position in the input
func getByte(pos complex64) byte {
return input[int(real(pos))+int(imag(pos))*width]
}
/**
* Initialization Functions
*/
// Scrub the track, return the track, sans carts, and all carts
func initScrubTrack(inp []string) ([][]Track, []*Pos) {
var carts []*Pos
var course [][]Track
for y, v := range inp {
var row []Track
for x, b := range v {
p := &Pos{x: x, y: y}
switch b {
case 'v':
p.dir = DIR_S
carts = append(carts, p)
case '^':
p.dir = DIR_N
carts = append(carts, p)
case '>':
p.dir = DIR_E
carts = append(carts, p)
case '<':
p.dir = DIR_W
carts = append(carts, p)
}
row = append(row, initGetTrack(inp, *p))
func setupCarts() {
cartMap = make(map[complex64]*Cart)
for i := 0; i < len(input); i++ {
pos := complex(float32(i%width), float32(i/width))
switch getByte(pos) {
case '^':
cartMap[pos] = &Cart{pos, DIR_N, TURN_L}
case '>':
cartMap[pos] = &Cart{pos, DIR_E, TURN_L}
case 'v':
cartMap[pos] = &Cart{pos, DIR_S, TURN_L}
case '<':
cartMap[pos] = &Cart{pos, DIR_W, TURN_L}
default:
continue // Not a cart
}
course = append(course, row)
carts = append(carts, &cartMap[pos].pos)
}
return course, carts
}
// Get a track position
func initGetTrack(inp []string, p Pos) Track {
if p.y < 0 || p.x < 0 || p.x > len(inp[0]) || p.y > len(inp) {
return Track(' ')
}
var bTop, bBot, bLft, bRgt Track
if initIsCart(inp, p) {
bTop = initGetTrack(inp, Pos{x: p.x, y: p.y - 1})
bBot = initGetTrack(inp, Pos{x: p.x, y: p.y + 1})
bLft = initGetTrack(inp, Pos{x: p.x - 1, y: p.y})
bRgt = initGetTrack(inp, Pos{x: p.x + 1, y: p.y})
if bTop.goesDown() && bLft.goesRight() && bRgt.goesLeft() && bBot.goesUp() {
return Track('+')
}
if (bTop.isEmpty() || bBot.isEmpty()) && bRgt.goesLeft() && bLft.goesRight() {
return Track('-')
}
if (bLft.isEmpty() || bRgt.isEmpty()) && bTop.goesDown() && bBot.goesUp() {
return Track('|')
}
}
return Track(inp[p.y][p.x])
}
// Return if this position is a cart
func initIsCart(inp []string, p Pos) bool {
switch inp[p.y][p.x] {
case 'v', '^', '<', '>':
return true
}
return false
}
func StdinToStringSlice() []string {
var input []string
func stdinToByteSlice() {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
input = append(input, scanner.Text())
data := scanner.Bytes()
if width == 0 {
width = len(data)
}
input = append(input, data...)
}
return input
}