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) }