231 lines
4.2 KiB
Go
231 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
const FILE_PATH = "./input.txt"
|
|
|
|
//const FILE_PATH = "./sample.txt"
|
|
|
|
func LoadInput(filename string) ([]string, error) {
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer file.Close()
|
|
|
|
var lines []string
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
lines = append(lines, scanner.Text())
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return lines, nil
|
|
}
|
|
|
|
type Vector2 struct {
|
|
x int
|
|
y int
|
|
}
|
|
|
|
type structuredInput = [][]byte
|
|
|
|
func TransformInput(lines []string) structuredInput {
|
|
var result = make([][]byte, 0)
|
|
|
|
for _, line := range lines {
|
|
result = append(result, []byte(line))
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
const (
|
|
UP = iota
|
|
RIGHT = iota
|
|
DOWN = iota
|
|
LEFT = iota
|
|
)
|
|
|
|
var MOVEMENTS = []Vector2{
|
|
{0, -1},
|
|
{1, 0},
|
|
{0, 1},
|
|
{-1, 0},
|
|
}
|
|
|
|
func addVectors(a Vector2, b Vector2) Vector2 {
|
|
return Vector2{a.x + b.x, a.y + b.y}
|
|
}
|
|
|
|
func inBounds(point Vector2, limit Vector2) bool {
|
|
return point.x >= 0 && point.x < limit.x && point.y >= 0 && point.y < limit.y
|
|
}
|
|
|
|
func Part1(input structuredInput) int {
|
|
var result = 0
|
|
|
|
var guard Vector2 = Vector2{}
|
|
var direction int = UP
|
|
var bounds Vector2 = Vector2{len(input[0]), len(input)}
|
|
var visited [][]bool = make([][]bool, len(input))
|
|
var next Vector2
|
|
|
|
for i := range visited {
|
|
visited[i] = make([]bool, len(input[i]))
|
|
}
|
|
|
|
for y := 0; y < len(input); y++ {
|
|
for x := 0; x < len(input[y]); x++ {
|
|
if input[y][x] == '^' {
|
|
guard.x = x
|
|
guard.y = y
|
|
}
|
|
}
|
|
}
|
|
|
|
visited[guard.y][guard.x] = true
|
|
|
|
for inBounds(addVectors(guard, MOVEMENTS[direction]), bounds) {
|
|
next = addVectors(guard, MOVEMENTS[direction])
|
|
|
|
if input[next.y][next.x] == '#' {
|
|
direction = (direction + 1) % 4
|
|
continue
|
|
}
|
|
|
|
guard = next
|
|
visited[guard.y][guard.x] = true
|
|
}
|
|
|
|
for i := range visited {
|
|
for j := range visited[i] {
|
|
if visited[i][j] {
|
|
result++
|
|
}
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
var visited [][][]bool
|
|
|
|
func generateVisited(bounds Vector2) {
|
|
visited = make([][][]bool, 4)
|
|
for i := range visited {
|
|
visited[i] = make([][]bool, bounds.y)
|
|
for j := range visited[i] {
|
|
visited[i][j] = make([]bool, bounds.x)
|
|
}
|
|
}
|
|
}
|
|
|
|
func resetVisited() {
|
|
for i := range visited {
|
|
for j := range visited[i] {
|
|
for m := range visited[i][j] {
|
|
visited[i][j][m] = false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func setPositionAsVisited(position Vector2, direction int) {
|
|
visited[direction][position.y][position.x] = true
|
|
}
|
|
|
|
func hasVisited(position Vector2, direction int) bool {
|
|
return visited[direction][position.y][position.x]
|
|
}
|
|
|
|
func checkForLoop(field [][]byte, startPosition Vector2, bounds Vector2) bool {
|
|
var guard Vector2 = Vector2{startPosition.x, startPosition.y}
|
|
var direction = UP
|
|
var next Vector2
|
|
|
|
resetVisited()
|
|
|
|
setPositionAsVisited(guard, direction)
|
|
|
|
for inBounds(addVectors(guard, MOVEMENTS[direction]), bounds) {
|
|
next = addVectors(guard, MOVEMENTS[direction])
|
|
|
|
if field[next.y][next.x] == '#' {
|
|
direction = (direction + 1) % 4
|
|
continue
|
|
}
|
|
|
|
guard = next
|
|
|
|
if hasVisited(guard, direction) {
|
|
return true
|
|
}
|
|
|
|
setPositionAsVisited(guard, direction)
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func Part2(input structuredInput) int {
|
|
var result = 0
|
|
var guard Vector2 = Vector2{}
|
|
var bounds Vector2 = Vector2{len(input[0]), len(input)}
|
|
|
|
generateVisited(bounds)
|
|
|
|
for y := 0; y < len(input); y++ {
|
|
for x := 0; x < len(input[y]); x++ {
|
|
if input[y][x] == '^' {
|
|
guard.x = x
|
|
guard.y = y
|
|
}
|
|
}
|
|
}
|
|
|
|
for y := 0; y < len(input); y++ {
|
|
for x := 0; x < len(input[y]); x++ {
|
|
if input[y][x] == '.' {
|
|
input[y][x] = '#'
|
|
if checkForLoop(input, guard, bounds) {
|
|
result++
|
|
}
|
|
input[y][x] = '.'
|
|
}
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func main() {
|
|
var start time.Time
|
|
var elapsed time.Duration = 0
|
|
input, err := LoadInput(FILE_PATH)
|
|
if err != nil {
|
|
fmt.Println("Error loading input:", err)
|
|
return
|
|
}
|
|
|
|
structuredInput := TransformInput(input)
|
|
fmt.Println("Structured Input:", structuredInput)
|
|
|
|
start = time.Now()
|
|
fmt.Println("Solution Part 1: ", Part1(structuredInput))
|
|
elapsed = time.Since(start)
|
|
fmt.Printf("Part 1 took %s\n", elapsed)
|
|
|
|
start = time.Now()
|
|
fmt.Println("Solution Part 2: ", Part2(structuredInput))
|
|
elapsed = time.Since(start)
|
|
fmt.Printf("Part 2 took %s\n", elapsed)
|
|
}
|