149 lines
3.1 KiB
Go
149 lines
3.1 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"time"
|
||
|
|
||
|
h "git.bullercodeworks.com/brian/adventofcode/helpers"
|
||
|
)
|
||
|
|
||
|
func main() {
|
||
|
inp := h.StdinToCoordMap()
|
||
|
cp := inp.Copy()
|
||
|
m := part1(inp)
|
||
|
part2(*cp, m.FindAll('X'))
|
||
|
}
|
||
|
|
||
|
var debug = false
|
||
|
|
||
|
func part1(m h.CoordByteMap) h.CoordByteMap {
|
||
|
init, err := m.FindFirst('^')
|
||
|
h.CheckErr(err)
|
||
|
for moveGuard(m) {
|
||
|
if debug {
|
||
|
fmt.Println(h.CLEAR_SCREEN)
|
||
|
fmt.Println(m)
|
||
|
guard := m.FindAll('^', '>', 'v', '<')[0]
|
||
|
fmt.Println(guard)
|
||
|
time.Sleep(time.Second / 10)
|
||
|
}
|
||
|
}
|
||
|
fmt.Println()
|
||
|
fmt.Println("# Part 1")
|
||
|
fmt.Printf("Guard moved into %d positions.\n", m.Count('X'))
|
||
|
fmt.Println()
|
||
|
m.Put(init, '^')
|
||
|
return m
|
||
|
}
|
||
|
|
||
|
func part2(clean h.CoordByteMap, p []h.Coordinate) {
|
||
|
// m should have all of the 'X' spots where the guard moved in part 1
|
||
|
init, err := clean.FindFirst('^')
|
||
|
h.CheckErr(err)
|
||
|
var obstacles []h.Coordinate
|
||
|
for i := range p {
|
||
|
fmt.Println(h.CLEAR_SCREEN)
|
||
|
fmt.Print("Progress: ")
|
||
|
h.PrintProgress(i, len(p))
|
||
|
fmt.Printf(" %d/%d\n", i, len(p))
|
||
|
m := *clean.Copy()
|
||
|
// Try putting an obstacle at p[i]
|
||
|
m.Put(p[i], 'O')
|
||
|
history := make(map[h.Coordinate][]string)
|
||
|
guard := m.FindAll('^', '>', 'v', '<')[0]
|
||
|
tst := m.Get(guard)
|
||
|
history[guard] = append(history[guard], string(tst))
|
||
|
var foundLoop bool
|
||
|
for moveGuard(m) && !foundLoop {
|
||
|
// fmt.Print(".")
|
||
|
if debug {
|
||
|
fmt.Println(h.CLEAR_SCREEN)
|
||
|
fmt.Println(m)
|
||
|
guard := m.FindAll('^', '>', 'v', '<')[0]
|
||
|
fmt.Printf("Guard: %s - Testing Obstacle: %s\n", guard, p[i])
|
||
|
fmt.Println(obstacles)
|
||
|
fmt.Println(history)
|
||
|
time.Sleep(time.Second / 10)
|
||
|
}
|
||
|
guard := m.FindAll('^', '>', 'v', '<')[0]
|
||
|
bt := m.Get(guard)
|
||
|
// Check if we already have this position/orientation in our history
|
||
|
for j := range history[guard] {
|
||
|
if history[guard][j] == string(bt) {
|
||
|
if !contains(obstacles, p[i]) {
|
||
|
obstacles = append(obstacles, p[i])
|
||
|
foundLoop = true
|
||
|
if debug {
|
||
|
fmt.Println("Found Loop:", string(history[guard][j]), "==", string(bt))
|
||
|
}
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
history[guard] = append(history[guard], string(bt))
|
||
|
}
|
||
|
m.Put(p[i], '.')
|
||
|
m.Put(init, '^')
|
||
|
}
|
||
|
fmt.Println()
|
||
|
fmt.Println("# Part 2")
|
||
|
fmt.Printf("Obstacle positions to create a loop: %d\n", len(obstacles))
|
||
|
// 583 is too low
|
||
|
}
|
||
|
|
||
|
func checkForLoop(m h.CoordByteMap) {
|
||
|
}
|
||
|
|
||
|
func contains(sl []h.Coordinate, c h.Coordinate) bool {
|
||
|
for i := range sl {
|
||
|
if sl[i].Equals(c) {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func turn(g byte) byte {
|
||
|
switch g {
|
||
|
case '^':
|
||
|
return '>'
|
||
|
case '>':
|
||
|
return 'v'
|
||
|
case 'v':
|
||
|
return '<'
|
||
|
default:
|
||
|
return '^'
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func nextPos(guard h.Coordinate, m h.CoordByteMap) h.Coordinate {
|
||
|
switch m.Get(guard) {
|
||
|
case '>':
|
||
|
return guard.East()
|
||
|
case 'v':
|
||
|
return guard.South()
|
||
|
case '<':
|
||
|
return guard.West()
|
||
|
}
|
||
|
return guard.North()
|
||
|
}
|
||
|
|
||
|
func moveGuard(m h.CoordByteMap) bool {
|
||
|
guard := m.FindAll('^', '>', 'v', '<')[0]
|
||
|
currBt := m.Get(guard)
|
||
|
turn := turn(currBt)
|
||
|
next := nextPos(guard, m)
|
||
|
if !m.ContainsCoord(next) {
|
||
|
m.Put(guard, 'X')
|
||
|
return false
|
||
|
}
|
||
|
tst := m.Get(next)
|
||
|
if tst == '#' || tst == 'O' {
|
||
|
m.Put(guard, turn)
|
||
|
return moveGuard(m)
|
||
|
}
|
||
|
m.Put(guard, 'X')
|
||
|
m.Put(next, currBt)
|
||
|
return true
|
||
|
}
|