Files
adventofcode/2023/go/day10/day10.go
2023-12-13 23:54:55 +00:00

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