mirror of
https://github.com/SebastianStork/advent-of-code.git
synced 2026-01-21 13:21:34 +01:00
141 lines
3 KiB
Go
141 lines
3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
type position struct {
|
|
row int
|
|
col int
|
|
}
|
|
|
|
type state struct {
|
|
row int
|
|
col int
|
|
direction int
|
|
}
|
|
|
|
const (
|
|
up = 0
|
|
right = 1
|
|
down = 2
|
|
left = 3
|
|
)
|
|
|
|
func readInput() (area [][]rune, start state, err error) {
|
|
content, err := os.ReadFile("input")
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
lines := strings.Split(strings.TrimSpace(string(content)), "\n")
|
|
area = make([][]rune, len(lines))
|
|
|
|
for rowIndex, line := range lines {
|
|
for colIndex, symbol := range line {
|
|
if symbol == '^' {
|
|
start = state{row: rowIndex, col: colIndex, direction: up}
|
|
}
|
|
}
|
|
|
|
area[rowIndex] = []rune(line)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func stateToPos(s state) position {
|
|
return position{s.row, s.col}
|
|
}
|
|
|
|
func isOutOfBounds(area [][]rune, pos position) bool {
|
|
return (pos.row < 0 || pos.row >= len(area)) || (pos.col < 0 || pos.col >= len(area[0]))
|
|
}
|
|
|
|
func isObstacle(area [][]rune, pos position) bool {
|
|
return area[pos.row][pos.col] == '#'
|
|
}
|
|
|
|
func takeStep(area [][]rune, currentState state) (bool, state) {
|
|
directions := map[int]position{up: {-1, 0}, right: {0, 1}, down: {1, 0}, left: {0, -1}}
|
|
|
|
newState := state{
|
|
row: currentState.row + directions[currentState.direction].row,
|
|
col: currentState.col + directions[currentState.direction].col,
|
|
direction: currentState.direction,
|
|
}
|
|
|
|
if isOutOfBounds(area, stateToPos(newState)) {
|
|
return false, newState
|
|
}
|
|
|
|
if isObstacle(area, stateToPos(newState)) {
|
|
currentState.direction = (currentState.direction + 1) % 4
|
|
return true, currentState
|
|
}
|
|
|
|
return true, newState
|
|
}
|
|
|
|
func findDistinctPositions(area [][]rune, currentState state) map[position]bool {
|
|
distinctPositions := make(map[position]bool)
|
|
|
|
for ok := true; ok; {
|
|
distinctPositions[stateToPos(currentState)] = true
|
|
|
|
ok, currentState = takeStep(area, currentState)
|
|
}
|
|
|
|
return distinctPositions
|
|
}
|
|
|
|
func isLoopCausingObstruction(area [][]rune, currentState state) bool {
|
|
path := make(map[state]bool)
|
|
|
|
for ok := true; ok; {
|
|
if _, isLoop := path[currentState]; isLoop {
|
|
return true
|
|
}
|
|
path[currentState] = true
|
|
|
|
ok, currentState = takeStep(area, currentState)
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func findLoopCausingObstructions(area [][]rune, start state, distinctPositions map[position]bool) []position {
|
|
var loopObstructions []position
|
|
|
|
for obstaclePos := range distinctPositions {
|
|
if obstaclePos == stateToPos(start) {
|
|
continue
|
|
}
|
|
|
|
area[obstaclePos.row][obstaclePos.col] = '#'
|
|
if isLoopCausingObstruction(area, start) {
|
|
loopObstructions = append(loopObstructions, obstaclePos)
|
|
}
|
|
area[obstaclePos.row][obstaclePos.col] = '.'
|
|
}
|
|
|
|
return loopObstructions
|
|
}
|
|
|
|
func main() {
|
|
area, start, err := readInput()
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
distinctPositions := findDistinctPositions(area, start)
|
|
loopObstructions := findLoopCausingObstructions(area, start, distinctPositions)
|
|
|
|
// Part one
|
|
fmt.Println("Number of distinct positions:", len(distinctPositions))
|
|
// Part two
|
|
fmt.Println("Number of potential obstructions:", len(loopObstructions))
|
|
}
|