package main import ( "bufio" "fmt" "os" "sort" "time" ) const ( gridX = 150 gridY = 150 ) var gridSize = gridX * gridY type Cart struct { X, Y int Dir byte LastTurn int Dead bool } func (c *Cart) Turn() { switch c.LastTurn { case 0: switch c.Dir { case '>': c.Dir = '^' case '<': c.Dir = 'v' case '^': c.Dir = '<' case 'v': c.Dir = '>' } c.LastTurn++ case 1: c.LastTurn++ case 2: switch c.Dir { case '>': c.Dir = 'v' case '<': c.Dir = '^' case '^': c.Dir = '>' case 'v': c.Dir = '<' } c.LastTurn = 0 } } func (c *Cart) Move(b byte) { switch b { case '\\': switch c.Dir { case '>': c.Dir = 'v' case '<': c.Dir = '^' case '^': c.Dir = '<' case 'v': c.Dir = '>' } case '/': switch c.Dir { case '>': c.Dir = '^' case '<': c.Dir = 'v' case '^': c.Dir = '>' case 'v': c.Dir = '<' } case '+': c.Turn() } switch c.Dir { case '>': c.X++ case '<': c.X-- case '^': c.Y-- case 'v': c.Y++ } } type ByCoord []Cart func (b ByCoord) Len() int { return len(b) } func (b ByCoord) Swap(i, j int) { b[i], b[j] = b[j], b[i] } func (b ByCoord) Less(i, j int) bool { ia := (b[i].Y * gridX) + b[i].X ib := (b[j].Y * gridX) + b[j].X return ia < ib } type Collision struct { Ca, Cb int X, Y int } type Puzzle struct { Playfield []byte Carts []Cart } func (p *Puzzle) Tick() []Collision { sort.Sort(ByCoord(p.Carts)) collisions := make([]Collision, 0, 2) for t, c := range p.Carts { if c.Dead { continue } b := p.Playfield[(c.Y*gridX)+c.X] p.Carts[t].Move(b) for u, cb := range p.Carts { if cb.Dead || t == u { continue } if p.Carts[t].X == cb.X && p.Carts[t].Y == cb.Y { p.Carts[t].Dead = true p.Carts[u].Dead = true collisions = append(collisions, Collision{ Ca: t, Cb: u, X: p.Carts[t].X, Y: p.Carts[t].Y, }) break } } } return collisions } func (p *Puzzle) Solution1(pretty bool) (int, int) { grid := make([]byte, gridSize) collisions := make([]Collision, 0, 10) for { count := 0 for _, c := range p.Carts { if c.Dead { count++ } } if count >= len(p.Carts)-1 { break } c := p.Tick() if len(c) > 0 { collisions = append(collisions, c...) } if pretty { copy(grid, p.Playfield) for _, c := range p.Carts { if c.Dead { continue } i := (c.Y * gridX) + c.X grid[i] = c.Dir } for y := 0; y < gridY; y++ { fmt.Println(string(grid[y*gridX : (y*gridX)+gridX])) } time.Sleep(100 * time.Millisecond) } } copy(grid, p.Playfield) for _, c := range collisions { i := (c.Y * gridX) + c.X grid[i] = 'X' } for _, c := range p.Carts { if c.Dead { continue } i := (c.Y * gridX) + c.X grid[i] = c.Dir } for y := 0; y < gridY; y++ { fmt.Println(string(grid[y*gridX : (y*gridX)+gridX])) } var aX, aY int for _, c := range p.Carts { if c.Dead { fmt.Println("dead", c.X, c.Y) continue } fmt.Println("alive", c.X, c.Y) aX = c.X aY = c.Y } return aX, aY } func main() { input, err := os.Open("input.txt") if err != nil { panic(err.Error()) } defer input.Close() scanner := bufio.NewScanner(input) p := Puzzle{ Playfield: make([]byte, gridSize), Carts: make([]Cart, 0, 2), } for t := range p.Playfield { p.Playfield[t] = ' ' } lines := 0 for scanner.Scan() { line := []byte(scanner.Text()) for x, c := range line { i := (lines * gridX) + x if c == '>' || c == 'v' || c == '<' || c == '^' { p.Carts = append(p.Carts, Cart{ X: x, Y: lines, Dir: c, LastTurn: 0, }) // Seems there's never a train started on an intersection if c == '>' || c == '<' { c = '-' } else { c = '|' } } p.Playfield[i] = c } lines++ } if err := scanner.Err(); err != nil { panic(err.Error()) } /* palette := []color.Color{ color.RGBA{0, 0, 0, 0xff}, color.RGBA{0xff, 0xff, 0xff, 0xff}, } size := image.Rect(0, 0, 128, 128) images := make([]*image.Paletted, 10) delays := make([]int, 10) for t := 0; t < 10; t++ { images[t] = image.NewPaletted(size, palette) for y := 0; y < 128; y++ { for x := 0; x < 128; x++ { c := byte((x + t) ^ (y + t)) images[t].Set(x, y, color.RGBA{ c, c, c, 0xff, }) } } } f, err := os.OpenFile("out.gif", os.O_WRONLY|os.O_CREATE, 0666) if err != nil { panic(err) } defer f.Close() gif.EncodeAll(f, &gif.GIF{ Image: images, Delay: delays, }) */ fmt.Println(p.Solution1(false)) }