package main import ( "fmt" "strings" ) type Direction uint8 const ( UP Direction = iota RIGHT DOWN LEFT ) func (d Direction) Next() Direction { return (d + 1) % 4 } func (d Direction) NextPoint(p *Point) *Point { if d == UP { return &Point{p.x, p.y - 1} } if d == DOWN { return &Point{p.x, p.y + 1} } if d == LEFT { return &Point{p.x - 1, p.y} } return &Point{p.x + 1, p.y} } type Point struct { x, y int } func (p *Point) String() string { return fmt.Sprintf("(%d, %d)", p.x, p.y) } type Map struct { data []rune width int height int } func (m *Map) isInbound(point *Point) bool { return point.x >= 0 && point.x < m.width && point.y >= 0 && point.y < m.width } func (m *Map) Get(point *Point) (rune, bool) { if !m.isInbound(point) { return 0, false } idx := point.x + m.width*point.y return m.data[idx], true } func (m *Map) Set(point *Point, r rune) { idx := point.x + m.width*point.y m.data[idx] = r } type Guard struct { position *Point direction Direction } func (g *Guard) String() string { return fmt.Sprintf("%s[%d]", g.position, g.direction) } func FindGuard(input string) *Guard { m := NewMap(input) input = strings.ReplaceAll(input, "\n", "") rawIndex := strings.Index(input, "^") x := rawIndex % m.width y := rawIndex / m.width return &Guard{&Point{x, y}, UP} } func NewMap(input string) *Map { if !strings.HasSuffix(input, "\n") { input += "\n" } input = strings.ReplaceAll(input, "^", ".") width := strings.Index(input, "\n") height := strings.Count(input, "\n") data := []rune(strings.ReplaceAll(input, "\n", "")) return &Map{data, width, height} } func isWall(v rune) bool { return v == '#' } func nextGuard(m *Map, guard *Guard) (*Guard, bool) { nextPosition := guard.direction.NextPoint(guard.position) if v, ok := m.Get(nextPosition); !ok { return nil, false } else if isWall(v) { return &Guard{guard.position, guard.direction.Next()}, true } else { return &Guard{nextPosition, guard.direction}, true } } func WalkGuard(m *Map, guard *Guard) chan *Guard { out := make(chan *Guard) go func() { for guard, ok := guard, true; ok; guard, ok = nextGuard(m, guard) { out <- guard } close(out) }() return out } func UniquePoints(guards chan *Guard) chan *Guard { out := make(chan *Guard) m := make(map[int]map[int]bool) go func() { for guard := range guards { p := guard.position if v, ok := m[p.x]; ok { if _, ok2 := v[p.y]; ok2 { continue } else { m[p.x][p.y] = true } } else { m[p.x] = make(map[int]bool) m[p.x][p.y] = true } out <- guard } close(out) }() return out } func Question6A(input string) int { m := NewMap(input) guard := FindGuard(input) i := 0 for _ = range UniquePoints(WalkGuard(m, guard)) { i += 1 } return i } func checkIfGuardMakesALoop(m *Map, guard *Guard) <-chan bool { out := make(chan bool) go func() { out <- isGuardMakingALoop(m, guard) close(out) }() return out } func Question6B(input string) int { m := NewMap(input) guard := FindGuard(input) chans := make([]<-chan bool, 0) possibleWallPositions := Rest(GetPoints(UniquePoints(WalkGuard(m, guard)))) for point := range possibleWallPositions { newMap := placeBlock(m, point) chans = append(chans, checkIfGuardMakesALoop(newMap, guard)) } sum := 0 for _, f := range chans { if <-f { sum++ } } return sum } func isGuardMakingALoop(m *Map, guard *Guard) bool { visited := make(map[int]map[int]map[Direction]bool) for guard := range WalkGuard(m, guard) { if _, ok := visited[guard.position.x]; ok { if _, ok2 := visited[guard.position.x][guard.position.y]; ok2 { if _, ok3 := visited[guard.position.x][guard.position.y][guard.direction]; ok3 { return true } else { visited[guard.position.x][guard.position.y][guard.direction] = true } } else { visited[guard.position.x][guard.position.y] = make(map[Direction]bool) visited[guard.position.x][guard.position.y][guard.direction] = true } } else { visited[guard.position.x] = make(map[int]map[Direction]bool) visited[guard.position.x][guard.position.y] = make(map[Direction]bool) visited[guard.position.x][guard.position.y][guard.direction] = true } } return false } func placeBlock(m *Map, point *Point) *Map { data := make([]rune, len(m.data)) copy(data, m.data) ret := &Map{data, m.width, m.height} ret.Set(point, rune('#')) return ret } func Rest(points <-chan *Point) <-chan *Point { out := make(chan *Point) go func() { first := true for p := range points { if first { first = false continue } out <- p } close(out) }() return out } func GetPoints(guards <-chan *Guard) <-chan *Point { out := make(chan *Point) go func() { for guard := range guards { out <- guard.position } close(out) }() return out } func main() { //answer6A := Question6A(realInput) //fmt.Printf("The answer to question 6A is %d\n", answer6A) answer6B := Question6B(realInput) fmt.Printf("The answer to question 6A is %d\n", answer6B) }