aoc/2024/day-06/main.go
2024-12-06 14:17:39 +01:00

171 lines
3.8 KiB
Go

package main
import (
"bufio"
"fmt"
"log"
"os"
)
type Position struct {
X, Y int
}
var (
Up = Position{-1, 0}
Down = Position{1, 0}
Left = Position{0, -1}
Right = Position{0, 1}
)
func main() {
file, err := os.Open("./input.txt")
if err != nil {
log.Fatalf("failed to open file: %s\n", err)
}
defer file.Close()
s := bufio.NewScanner(file)
var board [][]rune
var coveredPositions map[Position]bool
var guardCurrentPosition Position
var guardCurrentDirection Position
coveredPositions = make(map[Position]bool)
i := 0
for s.Scan() {
var boardLine []rune
j := 0
for _, char := range s.Text() {
boardLine = append(boardLine, char)
if char == '^' {
guardCurrentDirection = Up
}
if char == '>' {
guardCurrentDirection = Right
}
if char == 'v' {
guardCurrentDirection = Down
}
if char == '<' {
guardCurrentDirection = Left
}
if char == '#' || char == '.' {
j++
continue
}
currentPos := Position{i, j}
guardCurrentPosition = currentPos
coveredPositions[currentPos] = true
j++
}
board = append(board, boardLine)
i++
}
solvePart1(board, &coveredPositions, guardCurrentPosition, guardCurrentDirection)
fmt.Println("result part1:", len(coveredPositions))
result2 := 0
for pos := range coveredPositions {
if checkBlockWorks(board, pos, guardCurrentPosition, guardCurrentDirection) {
result2++
}
}
fmt.Println("result part2:", result2)
}
func solvePart1(board [][]rune, coveredPositions *map[Position]bool, guardCurrentPosition Position, guardCurrentDirection Position) {
for {
next, err := getNextByDirection(board, guardCurrentPosition, guardCurrentDirection)
if err != nil {
return
}
if next == '#' || next == 'O' {
if guardCurrentDirection == Up {
guardCurrentDirection = Right
continue
}
if guardCurrentDirection == Right {
guardCurrentDirection = Down
continue
}
if guardCurrentDirection == Down {
guardCurrentDirection = Left
continue
}
if guardCurrentDirection == Left {
guardCurrentDirection = Up
continue
}
}
guardCurrentPosition.X += guardCurrentDirection.X
guardCurrentPosition.Y += guardCurrentDirection.Y
(*coveredPositions)[Position{guardCurrentPosition.X, guardCurrentPosition.Y}] = true
}
}
func checkBlockWorks(board [][]rune, blockPos Position, guardCurrentPosition Position, guardCurrentDirection Position) bool {
localPos := Position{guardCurrentPosition.X, guardCurrentPosition.Y}
localDirection := Position{guardCurrentDirection.X, guardCurrentDirection.Y}
// this is the starting point
if (localPos.X == blockPos.X) && (localPos.Y == blockPos.Y) {
return false
}
var coveredPositions map[Position]int
coveredPositions = make(map[Position]int)
coveredPositions[localPos] = 1
for {
if coveredPositions[Position{localPos.X, localPos.Y}] == 10 {
return true
}
next, err := getNextByDirection(board, localPos, localDirection)
if err != nil {
return false
}
nextIsObstacle := (localPos.X+localDirection.X == blockPos.X) && (localPos.Y+localDirection.Y == blockPos.Y)
if next == '#' || nextIsObstacle {
if localDirection == Up {
localDirection = Right
continue
}
if localDirection == Right {
localDirection = Down
continue
}
if localDirection == Down {
localDirection = Left
continue
}
if localDirection == Left {
localDirection = Up
continue
}
}
localPos.X += localDirection.X
localPos.Y += localDirection.Y
coveredPositions[Position{localPos.X, localPos.Y}]++
}
}
func getNextByDirection(board [][]rune, pos Position, dir Position) (rune, error) {
newX := pos.X + dir.X
newY := pos.Y + dir.Y
if newX < 0 || newX >= len(board) || newY < 0 || newY >= len(board[0]) {
return 0, fmt.Errorf("position out of bounds: (%d, %d)", newX, newY)
}
return board[newX][newY], nil
}