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