You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

293 lines
7.0 KiB
Go

package day21
import (
"fmt"
"gitea.paas.celticinfo.fr/oabrivard/aoc2023/utils"
)
type Coord struct {
row int
col int
}
func BFS1(matrix [][]byte, root Coord, maxSteps int) int {
height := len(matrix)
width := len(matrix[0])
queue := utils.NewQueue[*Coord]()
matrix[root.row][root.col] = 'O'
queue.Enqueue(&root)
moves := 0
for moves < maxSteps {
//fmt.Println(moves + 1)
newQueue := utils.NewQueue[*Coord]()
for queue.HasElement() {
coord := queue.Dequeue()
matrix[coord.row][coord.col] = '.'
if coord.row > 0 && matrix[coord.row-1][coord.col] != '#' {
newCoord := Coord{coord.row - 1, coord.col}
newQueue.Enqueue(&newCoord)
matrix[newCoord.row][newCoord.col] = 'O'
}
if coord.row < height-1 && matrix[coord.row+1][coord.col] != '#' {
newCoord := Coord{coord.row + 1, coord.col}
newQueue.Enqueue(&newCoord)
matrix[newCoord.row][newCoord.col] = 'O'
}
if coord.col > 0 && matrix[coord.row][coord.col-1] != '#' {
newCoord := Coord{coord.row, coord.col - 1}
newQueue.Enqueue(&newCoord)
matrix[newCoord.row][newCoord.col] = 'O'
}
if coord.col < width-1 && matrix[coord.row][coord.col+1] != '#' {
newCoord := Coord{coord.row, coord.col + 1}
newQueue.Enqueue(&newCoord)
matrix[newCoord.row][newCoord.col] = 'O'
}
}
queue = newQueue
moves++
}
// count 'O' in matrix
count := 0
for r := range matrix {
for _, b := range matrix[r] {
if b == 'O' {
count++
}
}
}
return count
}
func redrawFromCache(matrix [][]byte, cacheQueue utils.Queue[*Coord]) {
for _, coord := range cacheQueue.GetData() {
if coord != nil {
matrix[coord.row][coord.col] = 'O'
}
}
}
func BFS2(matrix [][]byte, root Coord, maxSteps int) int {
height := len(matrix)
width := len(matrix[0])
queue := utils.NewQueue[*Coord]()
cache := make([][]utils.Queue[*Coord], height)
for r, row := range matrix {
cache[r] = make([]utils.Queue[*Coord], width)
for c := range row {
cache[r][c] = utils.NewQueue[*Coord]()
}
}
matrix[root.row][root.col] = 'O'
queue.Enqueue(&root)
moves := 0
for moves < maxSteps {
fmt.Println(moves + 1)
newQueue := utils.NewQueue[*Coord]()
for queue.HasElement() {
coord := queue.Dequeue()
if cache[coord.row][coord.col].HasElement() {
matrix[coord.row][coord.col] = '.'
redrawFromCache(matrix, cache[coord.row][coord.col])
} else {
cacheQueue := utils.NewQueue[*Coord]()
matrix[coord.row][coord.col] = '.'
if coord.row > 0 && matrix[coord.row-1][coord.col] != '#' {
newCoord := Coord{coord.row - 1, coord.col}
newQueue.Enqueue(&newCoord)
matrix[newCoord.row][newCoord.col] = 'O'
cacheQueue.Enqueue(&newCoord)
}
if coord.row < height-1 && matrix[coord.row+1][coord.col] != '#' {
newCoord := Coord{coord.row + 1, coord.col}
newQueue.Enqueue(&newCoord)
matrix[newCoord.row][newCoord.col] = 'O'
cacheQueue.Enqueue(&newCoord)
}
if coord.col > 0 && matrix[coord.row][coord.col-1] != '#' {
newCoord := Coord{coord.row, coord.col - 1}
newQueue.Enqueue(&newCoord)
matrix[newCoord.row][newCoord.col] = 'O'
cacheQueue.Enqueue(&newCoord)
}
if coord.col < width-1 && matrix[coord.row][coord.col+1] != '#' {
newCoord := Coord{coord.row, coord.col + 1}
newQueue.Enqueue(&newCoord)
matrix[newCoord.row][newCoord.col] = 'O'
cacheQueue.Enqueue(&newCoord)
}
cache[coord.row][coord.col] = cacheQueue
}
}
queue = newQueue
moves++
}
// count 'O' in matrix
count := 0
for r := range matrix {
for _, b := range matrix[r] {
if b == 'O' {
count++
}
}
}
return count
}
func BFS3(matrix [][]byte, root Coord, maxSteps int) int {
height := len(matrix)
width := len(matrix[0])
queue := utils.NewQueue[*Coord]()
matrix[root.row][root.col] = 'O'
queue.Enqueue(&root)
moves := 0
for moves < maxSteps {
//fmt.Println(moves + 1)
newQueue := utils.NewQueue[*Coord]()
coordInQueue := map[Coord]bool{}
for queue.HasElement() {
coord := queue.Dequeue()
matrix[coord.row][coord.col] = '.'
if coord.row > 0 && matrix[coord.row-1][coord.col] != '#' {
newCoord := Coord{coord.row - 1, coord.col}
if !coordInQueue[newCoord] {
newQueue.Enqueue(&newCoord)
matrix[newCoord.row][newCoord.col] = 'O'
coordInQueue[newCoord] = true
}
}
if coord.row < height-1 && matrix[coord.row+1][coord.col] != '#' {
newCoord := Coord{coord.row + 1, coord.col}
if !coordInQueue[newCoord] {
newQueue.Enqueue(&newCoord)
matrix[newCoord.row][newCoord.col] = 'O'
coordInQueue[newCoord] = true
}
}
if coord.col > 0 && matrix[coord.row][coord.col-1] != '#' {
newCoord := Coord{coord.row, coord.col - 1}
if !coordInQueue[newCoord] {
newQueue.Enqueue(&newCoord)
matrix[newCoord.row][newCoord.col] = 'O'
coordInQueue[newCoord] = true
}
}
if coord.col < width-1 && matrix[coord.row][coord.col+1] != '#' {
newCoord := Coord{coord.row, coord.col + 1}
if !coordInQueue[newCoord] {
newQueue.Enqueue(&newCoord)
matrix[newCoord.row][newCoord.col] = 'O'
coordInQueue[newCoord] = true
}
}
}
queue = newQueue
moves++
}
// count 'O' in matrix
count := 0
for r := range matrix {
for _, b := range matrix[r] {
if b == 'O' {
count++
}
}
}
return count
}
func Part1(lines []string, maxSteps int) int {
matrix := make([][]byte, len(lines))
var root Coord
for r, line := range lines {
matrix[r] = make([]byte, len(line))
for c, b := range line {
matrix[r][c] = byte(b)
if b == 'S' {
root.row = r
root.col = c
}
}
}
result := BFS3(matrix, root, maxSteps)
return result
}
// Solved thanks to the post ox aexl on the reddit thread
// https://www.reddit.com/r/adventofcode/comments/18nevo3/2023_day_21_solutions/
// It implies the creation of a specific input to have a grid large enough to simulate it is infinite
// for the 3 iterations.
/*
We need to figure out how many garden plots can be reached after 26501365 steps.
Note that 26501365 = 202300 * 131 + 65, where 131 is the side length of the input grid.
Store how many garden plots can be reached after 65, 65 + 131 and 65 + 2 * 131 steps, let's call these numbers r, r and r.
Now it turns out that the number of garden plots that can be reached after x * 131 + 65 steps is a quadratic function p(x) = ax² + bx + c.
We know that
r = p(0) = c
r = p(1) = a + b + c
r = p(2) = 4a + 2b + c
Solving this linear system of equations for the unknowns a, b and c gives
a = (r + r - 2r) / 2
b = (4r - 3r - r) / 2
c = r
Finally, we just need to evaluate the polynomial p at 202300
*/
func Part2(lines []string) int64 {
nbSteps := 26501365
gridSize := 131 // from puzzle input
modulo := nbSteps % gridSize
leftOver := nbSteps / gridSize
r1 := Part1(lines, modulo)
r2 := Part1(lines, modulo+gridSize)
r3 := Part1(lines, modulo+2*gridSize)
a := (r3 + r1 - 2*r2) / 2
b := (4*r2 - 3*r1 - r3) / 2
c := r1
p := func(x int) int64 { return int64(a*x*x + b*x + c) }
return p(leftOver)
}