163 lines
3.6 KiB
Go
163 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
|
|
// type definitions
|
|
|
|
type Direction int
|
|
const (
|
|
Up Direction = iota + 1
|
|
Down
|
|
Left
|
|
Right
|
|
)
|
|
|
|
type state struct {
|
|
field *[][]rune
|
|
guard_x int
|
|
guard_y int
|
|
direction Direction
|
|
}
|
|
|
|
func new_state(text string) *state {
|
|
lines := strings.Split(text, "\n")
|
|
field := make([][]rune, len(lines))
|
|
guard := [2]int{-1,-1}
|
|
|
|
for y, line := range lines {
|
|
field[y] = []rune(line)
|
|
|
|
for x, r := range field[y] {
|
|
if r == '^' {
|
|
guard = [2]int{x, y}
|
|
field[y][x] = '.'
|
|
}
|
|
}
|
|
}
|
|
|
|
s := state{field: &field, guard_x: guard[0], guard_y: guard[1], direction: Up}
|
|
return &s
|
|
}
|
|
|
|
|
|
// general util
|
|
|
|
func check(e error) {
|
|
if e != nil { panic(e) }
|
|
}
|
|
|
|
|
|
// simulation functions
|
|
|
|
func iter(s *state) *state {
|
|
new_guard_x := s.guard_x
|
|
new_guard_y := s.guard_y
|
|
|
|
// try to move
|
|
switch {
|
|
case s.direction == Up:
|
|
new_guard_y--
|
|
case s.direction == Down:
|
|
new_guard_y++
|
|
case s.direction == Left:
|
|
new_guard_x--
|
|
case s.direction == Right:
|
|
new_guard_x++
|
|
}
|
|
|
|
// if guard is outside of the field
|
|
if new_guard_x < 0 || new_guard_y < 0 || new_guard_x >= len((*s.field)[0]) || new_guard_y >= len(*s.field) {
|
|
new_guard_x = -1
|
|
new_guard_y = -1
|
|
} else if (*s.field)[new_guard_y][new_guard_x] == '#' {
|
|
// correct if path is blocked
|
|
switch {
|
|
case s.direction == Up:
|
|
new_guard_y++
|
|
new_guard_x++
|
|
s.direction = Right
|
|
case s.direction == Down:
|
|
new_guard_y--
|
|
new_guard_x--
|
|
s.direction = Left
|
|
case s.direction == Left:
|
|
new_guard_x++
|
|
new_guard_y--
|
|
s.direction = Up
|
|
case s.direction == Right:
|
|
new_guard_x--
|
|
new_guard_y++
|
|
s.direction = Down
|
|
}
|
|
}
|
|
|
|
// set new guard coordinates
|
|
s.guard_x = new_guard_x
|
|
s.guard_y = new_guard_y
|
|
|
|
return s
|
|
}
|
|
|
|
func will_loop(s *state) bool {
|
|
counter := 0
|
|
// while guard is within the field
|
|
for s.guard_x >= 0 && counter < 10000 {
|
|
// iterate
|
|
s = iter(s)
|
|
counter++
|
|
}
|
|
if s.guard_x < 0 {
|
|
// the guard is no longer in the field, not a loop
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
func print(s *state) {
|
|
out := make([]string, 0, len(*s.field))
|
|
for line := range *s.field {
|
|
out = append(out, string(line))
|
|
}
|
|
fmt.Println(strings.Join(out, "\n"))
|
|
fmt.Println("guard", s.guard_x, s.guard_y)
|
|
}
|
|
|
|
// main loop
|
|
func main() {
|
|
// read in file
|
|
dat, err := os.ReadFile("data.txt")
|
|
check(err)
|
|
dat_str := strings.TrimSpace(string(dat))
|
|
|
|
stateobj := new_state(dat_str)
|
|
start_of_guard := [2]int{stateobj.guard_x, stateobj.guard_y}
|
|
|
|
// check loops
|
|
counter := 0
|
|
for y := range *stateobj.field {
|
|
for x, elem := range (*stateobj.field)[y] {
|
|
// skip already existing borders
|
|
if elem == '#' { continue }
|
|
// because the placement of the start position of the guard is not allowed
|
|
if x == start_of_guard[0] && y == start_of_guard[1] { continue }
|
|
|
|
// set border
|
|
(*stateobj.field)[y][x] = '#'
|
|
|
|
// check for loop
|
|
if will_loop(stateobj) { counter++ }
|
|
|
|
// restore state
|
|
stateobj = new_state(dat_str)
|
|
}
|
|
}
|
|
|
|
fmt.Println(counter)
|
|
}
|