235 lines
4.7 KiB
Go
235 lines
4.7 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
|
|
}
|
|
|
|
func add(a Vector2, b Vector2) Vector2 {
|
|
return Vector2{a.x + b.x, a.y + b.y}
|
|
}
|
|
|
|
var MOVEMENTS = []Vector2{
|
|
{0, 1},
|
|
{1, 0},
|
|
{0, -1},
|
|
{-1, 0},
|
|
}
|
|
|
|
const (
|
|
EMPTY = iota
|
|
WALL = iota
|
|
)
|
|
|
|
type Maze struct {
|
|
field [][]int
|
|
start Vector2
|
|
end Vector2
|
|
}
|
|
|
|
type structuredInput = Maze
|
|
|
|
func TransformInput(lines []string) structuredInput {
|
|
var field = make([][]int, len(lines))
|
|
var start Vector2
|
|
var end Vector2
|
|
|
|
for y := 0; y < len(lines); y++ {
|
|
field[y] = make([]int, len(lines[y]))
|
|
for x := 0; x < len(lines[y]); x++ {
|
|
if lines[y][x] == '#' {
|
|
field[y][x] = WALL
|
|
} else {
|
|
field[y][x] = EMPTY
|
|
}
|
|
|
|
if lines[y][x] == 'S' {
|
|
start = Vector2{x, y}
|
|
} else if lines[y][x] == 'E' {
|
|
end = Vector2{x, y}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Maze{field, start, end}
|
|
}
|
|
|
|
func getLowestIndex(score [][]int, poss []Vector2) int {
|
|
var index = 0
|
|
var value = score[poss[0].y][poss[0].x]
|
|
|
|
for i := 1; i < len(poss); i++ {
|
|
if score[poss[i].y][poss[i].x] < value {
|
|
index = i
|
|
value = score[poss[i].y][poss[i].x]
|
|
}
|
|
}
|
|
|
|
return index
|
|
}
|
|
|
|
func findShortestLength(field [][]int, start Vector2, end Vector2) int {
|
|
if start == end {
|
|
return 0
|
|
}
|
|
|
|
var poss = []Vector2{start}
|
|
var score = make([][]int, len(field))
|
|
for y := 0; y < len(field); y++ {
|
|
score[y] = make([]int, len(field[y]))
|
|
for x := 0; x < len(field[y]); x++ {
|
|
score[y][x] = -1
|
|
}
|
|
}
|
|
score[start.y][start.x] = 0
|
|
|
|
var index int
|
|
var point Vector2
|
|
var next Vector2
|
|
for len(poss) > 0 {
|
|
index = getLowestIndex(score, poss)
|
|
point = poss[index]
|
|
poss = append(poss[:index], poss[index+1:]...)
|
|
|
|
for i := 0; i < 4; i++ {
|
|
next = add(point, MOVEMENTS[i])
|
|
if field[next.y][next.x] == EMPTY && score[next.y][next.x] == -1 {
|
|
if next == end {
|
|
return score[point.y][point.x] + 1
|
|
}
|
|
score[next.y][next.x] = score[point.y][point.x] + 1
|
|
poss = append(poss, next)
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1
|
|
}
|
|
|
|
func Part1(input structuredInput) int {
|
|
var result = 0
|
|
|
|
var baseSpeed = findShortestLength(input.field, input.start, input.end)
|
|
var tempSpeed int
|
|
|
|
for y := 1; y < len(input.field[y])-1; y++ {
|
|
for x := 1; x < len(input.field[x])-1; x++ {
|
|
if input.field[y][x] == WALL {
|
|
input.field[y][x] = EMPTY
|
|
tempSpeed = findShortestLength(input.field, input.start, input.end)
|
|
|
|
if baseSpeed-tempSpeed >= 100 {
|
|
result++
|
|
}
|
|
|
|
input.field[y][x] = WALL
|
|
}
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
const RANGE = 20
|
|
|
|
func iabs(value int) int {
|
|
if value < 0 {
|
|
return value * -1
|
|
}
|
|
return value
|
|
}
|
|
|
|
func Part2(input structuredInput) int {
|
|
var result = 0
|
|
|
|
var baseSpeed = findShortestLength(input.field, input.start, input.end)
|
|
|
|
var lengthToEnd = make([][]int, len(input.field))
|
|
var lengthToPoint = make([][]int, len(input.field))
|
|
|
|
for y := 0; y < len(input.field); y++ {
|
|
lengthToEnd[y] = make([]int, len(input.field[y]))
|
|
lengthToPoint[y] = make([]int, len(input.field[y]))
|
|
for x := 0; x < len(input.field[y]); x++ {
|
|
if input.field[y][x] == EMPTY {
|
|
lengthToEnd[y][x] = findShortestLength(input.field, Vector2{x, y}, input.end)
|
|
lengthToPoint[y][x] = findShortestLength(input.field, input.start, Vector2{x, y})
|
|
}
|
|
}
|
|
}
|
|
|
|
var speedToFinish int
|
|
|
|
for y1 := 0; y1 < len(input.field); y1++ {
|
|
for x1 := 0; x1 < len(input.field[y1]); x1++ {
|
|
if input.field[y1][x1] == EMPTY {
|
|
for y2 := max(y1-RANGE, 0); y2 < min(y1+RANGE+1, len(input.field)); y2++ {
|
|
for x2 := max(x1-RANGE, 0); x2 < min(x1+RANGE+1, len(input.field[y2])); x2++ {
|
|
if iabs(y2-y1)+iabs(x2-x1) <= RANGE && input.field[y2][x2] == EMPTY {
|
|
speedToFinish = lengthToPoint[y1][x1] + iabs(y2-y1) + iabs(x2-x1) + lengthToEnd[y2][x2]
|
|
if baseSpeed-speedToFinish >= 100 {
|
|
result++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|