2018 day 13 complete!
This commit is contained in:
		@@ -5,311 +5,145 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	DIR_N = iota
 | 
						DIR_N = -1i
 | 
				
			||||||
	DIR_E
 | 
						DIR_E = 1
 | 
				
			||||||
	DIR_S
 | 
						DIR_S = 1i
 | 
				
			||||||
	DIR_W
 | 
						DIR_W = -1
 | 
				
			||||||
	DIR_ERR
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	TURN_L = iota
 | 
						TURN_L = -1i
 | 
				
			||||||
	TURN_S
 | 
						TURN_S = 1
 | 
				
			||||||
	TURN_R
 | 
						TURN_R = 1i
 | 
				
			||||||
	TURN_ERR
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ClearScreen = "\033[H\033[2J"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var course [][]Track
 | 
					var width int
 | 
				
			||||||
var carts []*Pos
 | 
					var input []byte
 | 
				
			||||||
 | 
					var carts []*complex64
 | 
				
			||||||
 | 
					var cartMap map[complex64]*Cart
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	inp := StdinToStringSlice()
 | 
						stdinToByteSlice()
 | 
				
			||||||
	part1(inp)
 | 
						setupCarts()
 | 
				
			||||||
 | 
						//part1()
 | 
				
			||||||
 | 
						part2()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func part1(inp []string) {
 | 
					func part1() {
 | 
				
			||||||
	course, carts = initScrubTrack(inp)
 | 
						var crashed bool
 | 
				
			||||||
	var done bool
 | 
						for !crashed {
 | 
				
			||||||
	for i := 0; ; i++ {
 | 
					 | 
				
			||||||
		printState()
 | 
					 | 
				
			||||||
		tick()
 | 
					 | 
				
			||||||
		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))
 | 
							sort.Sort(ByPos(carts))
 | 
				
			||||||
		for _, c := range carts {
 | 
							for _, c := range carts {
 | 
				
			||||||
		switch c.dir {
 | 
								if !cartMap[*c].tick() {
 | 
				
			||||||
		case DIR_N:
 | 
									return
 | 
				
			||||||
			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
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getTrackAt(x, y int) Track {
 | 
					func part2() {
 | 
				
			||||||
	if x < 0 || y < 0 || y >= len(course) || x >= len(course[0]) {
 | 
						for len(cartMap) > 1 {
 | 
				
			||||||
		return Track(' ')
 | 
							sort.Sort(ByPos(carts))
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	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
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return Pos{x: -1, y: -1}, false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func printState() {
 | 
					 | 
				
			||||||
	fmt.Print(ClearScreen)
 | 
					 | 
				
			||||||
	for y, v := range course {
 | 
					 | 
				
			||||||
		for x, t := range v {
 | 
					 | 
				
			||||||
			var foundCarts []*Pos
 | 
					 | 
				
			||||||
		for _, c := range carts {
 | 
							for _, c := range carts {
 | 
				
			||||||
				if c.x == x && c.y == y {
 | 
								if *c != 0 {
 | 
				
			||||||
					foundCarts = append(foundCarts, c)
 | 
									cartMap[*c].tick()
 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			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
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
		fmt.Println("")
 | 
						for pos := range cartMap {
 | 
				
			||||||
 | 
							fmt.Printf("🔵: %0.0f,%0.0f\n", real(pos), imag(pos))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Pos struct {
 | 
					type Cart struct {
 | 
				
			||||||
	x, y     int
 | 
						pos      complex64
 | 
				
			||||||
	dir      int
 | 
						dir      complex64
 | 
				
			||||||
	nextTurn int
 | 
						nextTurn complex64
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ByPos []*Pos
 | 
					// Tick the cart, return if it's still alive
 | 
				
			||||||
 | 
					func (c *Cart) tick() bool {
 | 
				
			||||||
func (b ByPos) Len() int      { return len(b) }
 | 
						// Remove this cart from it's old position
 | 
				
			||||||
func (b ByPos) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
 | 
						// and figure out it's new one
 | 
				
			||||||
func (b ByPos) Less(i, j int) bool {
 | 
						delete(cartMap, c.pos)
 | 
				
			||||||
	return b[i].y < b[j].y || (b[i].y == b[j].y && b[i].x < b[j].x)
 | 
						c.pos += c.dir
 | 
				
			||||||
}
 | 
						if cart, ok := cartMap[c.pos]; ok {
 | 
				
			||||||
 | 
							fmt.Printf("❌: %0.0f,%0.0f\n", real(c.pos), imag(c.pos))
 | 
				
			||||||
// Choose a turn based on nextTurn and execute it
 | 
							// Remove the cart we crashed into from the map
 | 
				
			||||||
func (p *Pos) chooseTurn() {
 | 
							delete(cartMap, c.pos)
 | 
				
			||||||
	if p.nextTurn == TURN_S {
 | 
							// And clear the cart's pos, we can ignore them now
 | 
				
			||||||
		p.nextTurn = TURN_R
 | 
							cart.pos, c.pos = 0, 0
 | 
				
			||||||
	} else if p.nextTurn == TURN_R {
 | 
							return false
 | 
				
			||||||
		p.turnRight()
 | 
					 | 
				
			||||||
		p.nextTurn = TURN_L
 | 
					 | 
				
			||||||
	} else { // Default to turn left
 | 
					 | 
				
			||||||
		p.turnLeft()
 | 
					 | 
				
			||||||
		p.nextTurn = TURN_S
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
						// No crash, figure out the new orientation of the cart
 | 
				
			||||||
 | 
						cartMap[c.pos] = c
 | 
				
			||||||
func (p *Pos) turnLeft() {
 | 
						if getByte(c.pos) == '+' {
 | 
				
			||||||
	p.dir--
 | 
							c.dir *= c.nextTurn
 | 
				
			||||||
	if p.dir < DIR_N {
 | 
							switch c.nextTurn {
 | 
				
			||||||
		p.dir = DIR_W
 | 
							case TURN_L:
 | 
				
			||||||
 | 
								c.nextTurn = TURN_S
 | 
				
			||||||
 | 
							case TURN_S:
 | 
				
			||||||
 | 
								c.nextTurn = TURN_R
 | 
				
			||||||
 | 
							case TURN_R:
 | 
				
			||||||
 | 
								c.nextTurn = TURN_L
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
}
 | 
						} else if getByte(c.pos) == '/' {
 | 
				
			||||||
 | 
							// Turn counter-clockwise
 | 
				
			||||||
func (p *Pos) turnRight() {
 | 
							c.dir = complex(-imag(c.dir), -real(c.dir))
 | 
				
			||||||
	p.dir++
 | 
						} else if getByte(c.pos) == '\\' {
 | 
				
			||||||
	if p.dir > DIR_W {
 | 
							// Turn clockwise
 | 
				
			||||||
		p.dir = DIR_N
 | 
							c.dir = complex(imag(c.dir), real(c.dir))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *Pos) string() string {
 | 
					// We have to sort the carts on each tick by y,x position
 | 
				
			||||||
	return fmt.Sprintf("(%2d,%2d: %s)", p.x, p.y, string(p.getCartByte()))
 | 
					// 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]))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *Pos) getCartByte() byte {
 | 
					// getByte pulls a byte from the given position in the input
 | 
				
			||||||
	switch p.dir {
 | 
					func getByte(pos complex64) byte {
 | 
				
			||||||
	case DIR_N:
 | 
						return input[int(real(pos))+int(imag(pos))*width]
 | 
				
			||||||
		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) == '+'
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Initialization Functions
 | 
					 * Initialization Functions
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
// Scrub the track, return the track, sans carts, and all carts
 | 
					func setupCarts() {
 | 
				
			||||||
func initScrubTrack(inp []string) ([][]Track, []*Pos) {
 | 
						cartMap = make(map[complex64]*Cart)
 | 
				
			||||||
	var carts []*Pos
 | 
						for i := 0; i < len(input); i++ {
 | 
				
			||||||
	var course [][]Track
 | 
							pos := complex(float32(i%width), float32(i/width))
 | 
				
			||||||
	for y, v := range inp {
 | 
							switch getByte(pos) {
 | 
				
			||||||
		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 '^':
 | 
							case '^':
 | 
				
			||||||
				p.dir = DIR_N
 | 
								cartMap[pos] = &Cart{pos, DIR_N, TURN_L}
 | 
				
			||||||
				carts = append(carts, p)
 | 
					 | 
				
			||||||
		case '>':
 | 
							case '>':
 | 
				
			||||||
				p.dir = DIR_E
 | 
								cartMap[pos] = &Cart{pos, DIR_E, TURN_L}
 | 
				
			||||||
				carts = append(carts, p)
 | 
							case 'v':
 | 
				
			||||||
 | 
								cartMap[pos] = &Cart{pos, DIR_S, TURN_L}
 | 
				
			||||||
		case '<':
 | 
							case '<':
 | 
				
			||||||
				p.dir = DIR_W
 | 
								cartMap[pos] = &Cart{pos, DIR_W, TURN_L}
 | 
				
			||||||
				carts = append(carts, p)
 | 
							default:
 | 
				
			||||||
 | 
								continue // Not a cart
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
			row = append(row, initGetTrack(inp, *p))
 | 
							carts = append(carts, &cartMap[pos].pos)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
		course = append(course, row)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return course, carts
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Get a track position
 | 
					func stdinToByteSlice() {
 | 
				
			||||||
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
 | 
					 | 
				
			||||||
	scanner := bufio.NewScanner(os.Stdin)
 | 
						scanner := bufio.NewScanner(os.Stdin)
 | 
				
			||||||
	for scanner.Scan() {
 | 
						for scanner.Scan() {
 | 
				
			||||||
		input = append(input, scanner.Text())
 | 
							data := scanner.Bytes()
 | 
				
			||||||
 | 
							if width == 0 {
 | 
				
			||||||
 | 
								width = len(data)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							input = append(input, data...)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return input
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										251
									
								
								2018/day13/problem
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								2018/day13/problem
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,251 @@
 | 
				
			|||||||
 | 
					Advent of Code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- Day 13: Mine Cart Madness ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   A crop of this size requires significant logistics to transport produce, soil, fertilizer, and so on. The Elves are very busy pushing
 | 
				
			||||||
 | 
					   things around in carts on some kind of rudimentary system of tracks they've come up with.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Seeing as how cart-and-track systems don't appear in recorded history for another 1000 years, the Elves seem to be making this up as
 | 
				
			||||||
 | 
					   they go along. They haven't even figured out how to avoid collisions yet.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   You map out the tracks (your puzzle input) and see where you can help.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Tracks consist of straight paths (| and -), curves (/ and \), and intersections (+). Curves connect exactly two perpendicular pieces
 | 
				
			||||||
 | 
					   of track; for example, this is a closed loop:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /----\
 | 
				
			||||||
 | 
					 |    |
 | 
				
			||||||
 | 
					 |    |
 | 
				
			||||||
 | 
					 \----/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Intersections occur when two perpendicular paths cross. At an intersection, a cart is capable of turning left, turning right, or
 | 
				
			||||||
 | 
					   continuing straight. Here are two loops connected by two intersections:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /-----\
 | 
				
			||||||
 | 
					 |     |
 | 
				
			||||||
 | 
					 |  /--+--\
 | 
				
			||||||
 | 
					 |  |  |  |
 | 
				
			||||||
 | 
					 \--+--/  |
 | 
				
			||||||
 | 
					    |     |
 | 
				
			||||||
 | 
					    \-----/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Several carts are also on the tracks. Carts always face either up (^), down (v), left (<), or right (>). (On your initial map, the
 | 
				
			||||||
 | 
					   track under each cart is a straight path matching the direction the cart is facing.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Each time a cart has the option to turn (by arriving at any intersection), it turns left the first time, goes straight the second
 | 
				
			||||||
 | 
					   time, turns right the third time, and then repeats those directions starting again with left the fourth time, straight the fifth
 | 
				
			||||||
 | 
					   time, and so on. This process is independent of the particular intersection at which the cart has arrived - that is, the cart has no
 | 
				
			||||||
 | 
					   per-intersection memory.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Carts all move at the same speed; they take turns moving a single step at a time. They do this based on their current location: carts
 | 
				
			||||||
 | 
					   on the top row move first (acting from left to right), then carts on the second row move (again from left to right), then carts on
 | 
				
			||||||
 | 
					   the third row, and so on. Once each cart has moved one step, the process repeats; each of these loops is called a tick.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   For example, suppose there are two carts on a straight track:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 |  |  |  |  |
 | 
				
			||||||
 | 
					 v  |  |  |  |
 | 
				
			||||||
 | 
					 |  v  v  |  |
 | 
				
			||||||
 | 
					 |  |  |  v  X
 | 
				
			||||||
 | 
					 |  |  ^  ^  |
 | 
				
			||||||
 | 
					 ^  ^  |  |  |
 | 
				
			||||||
 | 
					 |  |  |  |  |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   First, the top cart moves. It is facing down (v), so it moves down one square. Second, the bottom cart moves. It is facing up (^), so
 | 
				
			||||||
 | 
					   it moves up one square. Because all carts have moved, the first tick ends. Then, the process repeats, starting with the first cart.
 | 
				
			||||||
 | 
					   The first cart moves down, then the second cart moves up - right into the first cart, colliding with it! (The location of the crash
 | 
				
			||||||
 | 
					   is marked with an X.) This ends the second and last tick.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Here is a longer example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /->-\
 | 
				
			||||||
 | 
					 |   |  /----\
 | 
				
			||||||
 | 
					 | /-+--+-\  |
 | 
				
			||||||
 | 
					 | | |  | v  |
 | 
				
			||||||
 | 
					 \-+-/  \-+--/
 | 
				
			||||||
 | 
					   \------/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /-->\
 | 
				
			||||||
 | 
					 |   |  /----\
 | 
				
			||||||
 | 
					 | /-+--+-\  |
 | 
				
			||||||
 | 
					 | | |  | |  |
 | 
				
			||||||
 | 
					 \-+-/  \->--/
 | 
				
			||||||
 | 
					   \------/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /---v
 | 
				
			||||||
 | 
					 |   |  /----\
 | 
				
			||||||
 | 
					 | /-+--+-\  |
 | 
				
			||||||
 | 
					 | | |  | |  |
 | 
				
			||||||
 | 
					 \-+-/  \-+>-/
 | 
				
			||||||
 | 
					   \------/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /---\
 | 
				
			||||||
 | 
					 |   v  /----\
 | 
				
			||||||
 | 
					 | /-+--+-\  |
 | 
				
			||||||
 | 
					 | | |  | |  |
 | 
				
			||||||
 | 
					 \-+-/  \-+->/
 | 
				
			||||||
 | 
					   \------/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /---\
 | 
				
			||||||
 | 
					 |   |  /----\
 | 
				
			||||||
 | 
					 | /->--+-\  |
 | 
				
			||||||
 | 
					 | | |  | |  |
 | 
				
			||||||
 | 
					 \-+-/  \-+--^
 | 
				
			||||||
 | 
					   \------/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /---\
 | 
				
			||||||
 | 
					 |   |  /----\
 | 
				
			||||||
 | 
					 | /-+>-+-\  |
 | 
				
			||||||
 | 
					 | | |  | |  ^
 | 
				
			||||||
 | 
					 \-+-/  \-+--/
 | 
				
			||||||
 | 
					   \------/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /---\
 | 
				
			||||||
 | 
					 |   |  /----\
 | 
				
			||||||
 | 
					 | /-+->+-\  ^
 | 
				
			||||||
 | 
					 | | |  | |  |
 | 
				
			||||||
 | 
					 \-+-/  \-+--/
 | 
				
			||||||
 | 
					   \------/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /---\
 | 
				
			||||||
 | 
					 |   |  /----<
 | 
				
			||||||
 | 
					 | /-+-->-\  |
 | 
				
			||||||
 | 
					 | | |  | |  |
 | 
				
			||||||
 | 
					 \-+-/  \-+--/
 | 
				
			||||||
 | 
					   \------/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /---\
 | 
				
			||||||
 | 
					 |   |  /---<\
 | 
				
			||||||
 | 
					 | /-+--+>\  |
 | 
				
			||||||
 | 
					 | | |  | |  |
 | 
				
			||||||
 | 
					 \-+-/  \-+--/
 | 
				
			||||||
 | 
					   \------/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /---\
 | 
				
			||||||
 | 
					 |   |  /--<-\
 | 
				
			||||||
 | 
					 | /-+--+-v  |
 | 
				
			||||||
 | 
					 | | |  | |  |
 | 
				
			||||||
 | 
					 \-+-/  \-+--/
 | 
				
			||||||
 | 
					   \------/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /---\
 | 
				
			||||||
 | 
					 |   |  /-<--\
 | 
				
			||||||
 | 
					 | /-+--+-\  |
 | 
				
			||||||
 | 
					 | | |  | v  |
 | 
				
			||||||
 | 
					 \-+-/  \-+--/
 | 
				
			||||||
 | 
					   \------/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /---\
 | 
				
			||||||
 | 
					 |   |  /<---\
 | 
				
			||||||
 | 
					 | /-+--+-\  |
 | 
				
			||||||
 | 
					 | | |  | |  |
 | 
				
			||||||
 | 
					 \-+-/  \-<--/
 | 
				
			||||||
 | 
					   \------/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /---\
 | 
				
			||||||
 | 
					 |   |  v----\
 | 
				
			||||||
 | 
					 | /-+--+-\  |
 | 
				
			||||||
 | 
					 | | |  | |  |
 | 
				
			||||||
 | 
					 \-+-/  \<+--/
 | 
				
			||||||
 | 
					   \------/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /---\
 | 
				
			||||||
 | 
					 |   |  /----\
 | 
				
			||||||
 | 
					 | /-+--v-\  |
 | 
				
			||||||
 | 
					 | | |  | |  |
 | 
				
			||||||
 | 
					 \-+-/  ^-+--/
 | 
				
			||||||
 | 
					   \------/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /---\
 | 
				
			||||||
 | 
					 |   |  /----\
 | 
				
			||||||
 | 
					 | /-+--+-\  |
 | 
				
			||||||
 | 
					 | | |  X |  |
 | 
				
			||||||
 | 
					 \-+-/  \-+--/
 | 
				
			||||||
 | 
					   \------/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   After following their respective paths for a while, the carts eventually crash. To help prevent crashes, you'd like to know the
 | 
				
			||||||
 | 
					   location of the first crash. Locations are given in X,Y coordinates, where the furthest left column is X=0 and the furthest top row
 | 
				
			||||||
 | 
					   is Y=0:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            111
 | 
				
			||||||
 | 
					  0123456789012
 | 
				
			||||||
 | 
					 0/---\
 | 
				
			||||||
 | 
					 1|   |  /----\
 | 
				
			||||||
 | 
					 2| /-+--+-\  |
 | 
				
			||||||
 | 
					 3| | |  X |  |
 | 
				
			||||||
 | 
					 4\-+-/  \-+--/
 | 
				
			||||||
 | 
					 5  \------/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   In this example, the location of the first crash is 7,3.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Your puzzle answer was 83,106.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- Part Two ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   There isn't much you can do to prevent crashes in this ridiculous system. However, by predicting the crashes, the Elves know where to
 | 
				
			||||||
 | 
					   be in advance and instantly remove the two crashing carts the moment any crash occurs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   They can proceed like this for a while, but eventually, they're going to run out of carts. It could be useful to figure out where the
 | 
				
			||||||
 | 
					   last cart that hasn't crashed will end up.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   For example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 />-<\
 | 
				
			||||||
 | 
					 |   |
 | 
				
			||||||
 | 
					 | /<+-\
 | 
				
			||||||
 | 
					 | | | v
 | 
				
			||||||
 | 
					 \>+</ |
 | 
				
			||||||
 | 
					   |   ^
 | 
				
			||||||
 | 
					   \<->/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /---\
 | 
				
			||||||
 | 
					 |   |
 | 
				
			||||||
 | 
					 | v-+-\
 | 
				
			||||||
 | 
					 | | | |
 | 
				
			||||||
 | 
					 \-+-/ |
 | 
				
			||||||
 | 
					   |   |
 | 
				
			||||||
 | 
					   ^---^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /---\
 | 
				
			||||||
 | 
					 |   |
 | 
				
			||||||
 | 
					 | /-+-\
 | 
				
			||||||
 | 
					 | v | |
 | 
				
			||||||
 | 
					 \-+-/ |
 | 
				
			||||||
 | 
					   ^   ^
 | 
				
			||||||
 | 
					   \---/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /---\
 | 
				
			||||||
 | 
					 |   |
 | 
				
			||||||
 | 
					 | /-+-\
 | 
				
			||||||
 | 
					 | | | |
 | 
				
			||||||
 | 
					 \-+-/ ^
 | 
				
			||||||
 | 
					   |   |
 | 
				
			||||||
 | 
					   \---/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   After four very expensive crashes, a tick ends with only one cart remaining; its final location is 6,4.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   What is the location of the last cart at the end of the first tick where it is the only cart left?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Your puzzle answer was 132,26.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Both parts of this puzzle are complete! They provide two gold stars: **
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					References
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Visible links
 | 
				
			||||||
 | 
					   . https://adventofcode.com/
 | 
				
			||||||
 | 
					   . https://adventofcode.com/2018/about
 | 
				
			||||||
 | 
					   . https://adventofcode.com/2018/events
 | 
				
			||||||
 | 
					   . https://adventofcode.com/2018/settings
 | 
				
			||||||
 | 
					   . https://adventofcode.com/2018/auth/logout
 | 
				
			||||||
 | 
					   . Advent of Code Supporter
 | 
				
			||||||
 | 
						https://adventofcode.com/2018/support
 | 
				
			||||||
 | 
					   . https://adventofcode.com/2018
 | 
				
			||||||
 | 
					   . https://adventofcode.com/2018
 | 
				
			||||||
 | 
					   . https://adventofcode.com/2018/support
 | 
				
			||||||
 | 
					   . https://adventofcode.com/2018/sponsors
 | 
				
			||||||
 | 
					   . https://adventofcode.com/2018/leaderboard
 | 
				
			||||||
 | 
					   . https://adventofcode.com/2018/stats
 | 
				
			||||||
 | 
					   . https://adventofcode.com/2018/sponsors
 | 
				
			||||||
 | 
					   . https://adventofcode.com/2018
 | 
				
			||||||
 | 
					   . https://adventofcode.com/2018/day/13/input
 | 
				
			||||||
		Reference in New Issue
	
	Block a user