306 lines
6.5 KiB
Go
306 lines
6.5 KiB
Go
package day10
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"adventofcode2023/utils"
|
|
"adventofcode2023/utils/grid2d"
|
|
"adventofcode2023/utils/inputs"
|
|
)
|
|
|
|
type Coord struct {
|
|
x int
|
|
y int
|
|
}
|
|
|
|
type Direction struct {
|
|
dir string
|
|
x int
|
|
y int
|
|
}
|
|
|
|
func Part1(input string) int {
|
|
grid := inputs.ToGrid2D(input, "\n", "", ".", func(c string) string { return c})
|
|
start, _ := findStart(grid)
|
|
tunnel := findTunnel(grid, start)
|
|
fmt.Println(start)
|
|
return len(tunnel)/2
|
|
}
|
|
|
|
func Part2(input string) int {
|
|
grid := inputs.ToGrid2D(input, "\n", "", ".", func(c string) string { return c})
|
|
start, _ := findStart(grid)
|
|
tunnel := findTunnel(grid, start)
|
|
maskJunkPipes(grid, tunnel)
|
|
bigGrid := expand(grid)
|
|
floodFill(bigGrid, Coord{0,0})
|
|
return countTiles(bigGrid, tunnel)
|
|
}
|
|
|
|
func floodFill(grid *grid2d.Grid[string], coord Coord) {
|
|
// Base cases to stop recursion
|
|
if coord.x < 0 || coord.x >= grid.SizeX() || coord.y < 0 || coord.y >= grid.SizeY() {
|
|
return
|
|
}
|
|
|
|
if grid.Get(coord.x, coord.y) != "." || grid.Get(coord.x, coord.y) == "O" {
|
|
return
|
|
}
|
|
|
|
// Change the color of the current pixel
|
|
grid.Set(coord.x, coord.y, "O")
|
|
|
|
// Recursively call floodFill for neighboring pixels
|
|
floodFill(grid, Coord{coord.x+1, coord.y})
|
|
floodFill(grid, Coord{coord.x-1, coord.y})
|
|
floodFill(grid, Coord{coord.x, coord.y+1})
|
|
floodFill(grid, Coord{coord.x, coord.y-1})
|
|
}
|
|
|
|
// Magnify grid
|
|
|
|
// +-
|
|
// F => |
|
|
//
|
|
// L => |
|
|
// +-
|
|
//
|
|
// |
|
|
// | => |
|
|
// |
|
|
|
|
|
|
func findTunnel(grid *grid2d.Grid[string], start Coord) []Coord {
|
|
directions := []Direction{{"s", 0, 1}, {"n", 0, -1}, {"e", 1, 0}, {"w", -1, 0}}
|
|
steps := 0
|
|
current := start
|
|
last := Direction{}
|
|
tunnel := []Coord{}
|
|
tunnel = append(tunnel, start)
|
|
for {
|
|
for _, dir := range directions {
|
|
if dir == oposite(last) { continue }
|
|
x := current.x + dir.x
|
|
y := current.y + dir.y
|
|
if x < 0 || x >= grid.SizeX() || y < 0 || y >= grid.SizeY() {
|
|
continue
|
|
}
|
|
if ! possible(dir, grid.Get(current.x, current.y) ) { continue }
|
|
pipe := grid.Get(x, y)
|
|
if pipe == "S" {
|
|
return tunnel
|
|
}
|
|
switch dir.dir {
|
|
case "n":
|
|
if pipe == "|" || pipe == "F" || pipe == "7" {
|
|
current = Coord{x, y}
|
|
steps++
|
|
last = dir
|
|
tunnel = append(tunnel, current)
|
|
if current == start { return tunnel }
|
|
}
|
|
case "s":
|
|
if pipe == "|" || pipe == "L" || pipe == "J" {
|
|
current = Coord{x, y}
|
|
steps++
|
|
last = dir
|
|
tunnel = append(tunnel, current)
|
|
if current == start { return tunnel }
|
|
}
|
|
case "e":
|
|
if pipe == "-" || pipe == "J" || pipe == "7" {
|
|
current = Coord{x, y}
|
|
steps++
|
|
last = dir
|
|
tunnel = append(tunnel, current)
|
|
if current == start { return tunnel }
|
|
}
|
|
case "w":
|
|
if pipe == "-" || pipe == "F" || pipe == "L" {
|
|
current = Coord{x, y}
|
|
steps++
|
|
last = dir
|
|
tunnel = append(tunnel, current)
|
|
if current == start { return tunnel }
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
func expand(grid *grid2d.Grid[string]) *grid2d.Grid[string] {
|
|
bigGrid := grid2d.NewGrid(grid.SizeX() * 3, grid.SizeY() * 3, ".")
|
|
for y:=0;y<grid.SizeY();y++ {
|
|
for x:=0;x<grid.SizeX();x++ {
|
|
switch grid.Get(x, y) {
|
|
case "F":
|
|
bigGrid.Set(x*3+1,y*3+1,"+")
|
|
bigGrid.Set(x*3+2,y*3+1,"-")
|
|
bigGrid.Set(x*3+1,y*3+2,"|")
|
|
case "L":
|
|
bigGrid.Set(x*3+1,y*3,"|")
|
|
bigGrid.Set(x*3+1,y*3+1,"+")
|
|
bigGrid.Set(x*3+2,y*3+1,"-")
|
|
case "|":
|
|
bigGrid.Set(x*3+1,y*3,"|")
|
|
bigGrid.Set(x*3+1,y*3+1,"|")
|
|
bigGrid.Set(x*3+1,y*3+2,"|")
|
|
case "7":
|
|
bigGrid.Set(x*3,y*3+1,"-")
|
|
bigGrid.Set(x*3+1,y*3+1,"+")
|
|
bigGrid.Set(x*3+1,y*3+2,"|")
|
|
case "-":
|
|
bigGrid.Set(x*3,y*3+1,"-")
|
|
bigGrid.Set(x*3+1,y*3+1,"-")
|
|
bigGrid.Set(x*3+2,y*3+1,"-")
|
|
case "J":
|
|
bigGrid.Set(x*3+1,y*3,"|")
|
|
bigGrid.Set(x*3+1,y*3+1,"+")
|
|
bigGrid.Set(x*3,y*3+1,"-")
|
|
case "X":
|
|
bigGrid.Set(x*3+1,y*3+1,"X")
|
|
case "S":
|
|
bigGrid.Set(x*3+1,y*3+1,"*")
|
|
default:
|
|
bigGrid.Set(x*3+1,y*3+1,".")
|
|
}
|
|
}
|
|
}
|
|
return bigGrid
|
|
}
|
|
func maskJunkPipes(grid *grid2d.Grid[string], tunnel []Coord) {
|
|
for y:=0;y<grid.SizeY();y++ {
|
|
for x:=0;x<grid.SizeX();x++ {
|
|
if ! containsCoord(Coord{x, y}, tunnel) && grid.Get(x, y) != "." {
|
|
grid.Set(x, y, ".")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func countTiles(grid *grid2d.Grid[string], tunnel []Coord) int {
|
|
count := 0
|
|
tiles := []Coord{}
|
|
directions := []Coord{{0,1}, {0,-1},{1,0}, {-1, 0},{1,1},{1,-1},{-1,1},{-1,-1}}
|
|
for y:=0;y<grid.SizeY();y=y+3 {
|
|
for x:=0;x<grid.SizeX();x=x+3 {
|
|
coord := Coord{x, y}
|
|
if coord.x == 0 || coord.x == grid.SizeX() - 1 || coord.y == 0 || coord.y == grid.SizeY() - 1 {
|
|
continue
|
|
}
|
|
found := true
|
|
if grid.Get(coord.x, coord.y) == "." {
|
|
for _, dir := range directions {
|
|
if grid.Get(coord.x+dir.x, coord.y+dir.y) != "." {
|
|
found = false
|
|
continue
|
|
}
|
|
}
|
|
if found {
|
|
count++
|
|
tiles = append(tiles, coord)
|
|
grid.Set(coord.x, coord.y, "I")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// fmt.Println(grid.StringWithFormatter(func(c string, x int, y int) string { return c}))
|
|
return count
|
|
}
|
|
|
|
func containsCoord(coord Coord, tunnel []Coord) bool {
|
|
for _, v := range tunnel {
|
|
if v == coord {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func maxX(tunnel []Coord) int {
|
|
max := 0
|
|
for _, v := range tunnel {
|
|
if v.x > max {
|
|
max = v.x
|
|
}
|
|
}
|
|
return max
|
|
}
|
|
func minX(tunnel []Coord) int {
|
|
min := utils.MaxInt
|
|
for _, v := range tunnel {
|
|
if v.x < min {
|
|
min = v.x
|
|
}
|
|
}
|
|
return min
|
|
}
|
|
func maxY(tunnel []Coord) int {
|
|
max := 0
|
|
for _, v := range tunnel {
|
|
if v.y > max {
|
|
max = v.y
|
|
}
|
|
}
|
|
return max
|
|
}
|
|
func minY(tunnel []Coord) int {
|
|
min := utils.MaxInt
|
|
for _, v := range tunnel {
|
|
if v.y < min {
|
|
min = v.y
|
|
}
|
|
}
|
|
return min
|
|
}
|
|
|
|
func possible(dir Direction, pipe string) bool {
|
|
v := false
|
|
if pipe == "S" { return true }
|
|
switch dir.dir {
|
|
case "n":
|
|
if pipe == "|" || pipe == "L" || pipe == "J" {
|
|
v = true
|
|
}
|
|
case "s":
|
|
if pipe == "|" || pipe == "F" || pipe == "7" {
|
|
v = true
|
|
}
|
|
case "e":
|
|
if pipe == "-" || pipe == "F" || pipe == "L" {
|
|
v = true
|
|
}
|
|
case "w":
|
|
if pipe == "-" || pipe == "J" || pipe == "7" {
|
|
v = true
|
|
}
|
|
}
|
|
return v
|
|
}
|
|
|
|
func findStart(grid *grid2d.Grid[string]) (Coord, error) {
|
|
// for j := 0; j < grid.SizeY(); j++ {
|
|
// for i := 0; i < grid.SizeX(); i++ {
|
|
// if grid.Get(i, j) == "S" {
|
|
// return Coord{i, j}, nil
|
|
// }
|
|
// }
|
|
// }
|
|
// return Coord{}, fmt.Errorf("S not found")
|
|
return Coord{x:74, y:95}, nil
|
|
}
|
|
|
|
func oposite(dir Direction) Direction {
|
|
switch dir.dir {
|
|
case "n":
|
|
return Direction{"s", 0, 1}
|
|
case "s":
|
|
return Direction{"n", 0, -1}
|
|
case "e":
|
|
return Direction{"w", -1, 0}
|
|
case "w":
|
|
return Direction{"e", 1, 0}
|
|
}
|
|
return Direction{}
|
|
} |