2018 day 13 complete!

This commit is contained in:
Brian Buller 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
}

251
2018/day13/problem Normal file
View 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