mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-06-06 04:08:27 +02:00
day 10 part 2
This commit is contained in:
+155
-44
@@ -71,46 +71,30 @@ func pipeMaze(input string, part int) int {
|
||||
}
|
||||
}
|
||||
|
||||
// need to find which directions are connected to the initial cell
|
||||
// could traverse along the four directions and see which two lead back to
|
||||
// the start... all pipes only connect two coordinates so it's actually
|
||||
// fairly easy to traverse without creating a huge search space
|
||||
fillGridLocation(grid, r, c)
|
||||
|
||||
// note this soultion was overkill for part 1
|
||||
// inputs are nice and exactly two adjacent cells that connect to S
|
||||
var loopCoords map[[2]int]bool
|
||||
for pipeType := range pipes {
|
||||
// assign the start square to a random pipeType, then see if it will loop
|
||||
grid[r][c] = pipeType
|
||||
|
||||
seen := map[[2]int]bool{}
|
||||
toAnalyze := [][2]int{
|
||||
{r, c},
|
||||
}
|
||||
didLoop := false
|
||||
for len(toAnalyze) > 0 {
|
||||
coords := toAnalyze[0]
|
||||
if seen[coords] {
|
||||
didLoop = true
|
||||
break
|
||||
}
|
||||
seen[coords] = true
|
||||
toAnalyze = toAnalyze[1:]
|
||||
|
||||
if diffs, ok := pipes[grid[coords[0]][coords[1]]]; ok {
|
||||
for _, diff := range diffs {
|
||||
nextRow, nextCol := coords[0]+diff[0], coords[1]+diff[1]
|
||||
if isInRange(grid, nextRow, nextCol) && !seen[[2]int{nextRow, nextCol}] {
|
||||
toAnalyze = append(toAnalyze, [2]int{nextRow, nextCol})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if didLoop {
|
||||
loopCoords = seen
|
||||
// traverse entire loop to determine length
|
||||
loopCoords := map[[2]int]bool{}
|
||||
toVisit := [][2]int{
|
||||
{r, c},
|
||||
}
|
||||
for len(toVisit) > 0 {
|
||||
coords := toVisit[0]
|
||||
if loopCoords[coords] {
|
||||
break
|
||||
}
|
||||
loopCoords[coords] = true
|
||||
toVisit = toVisit[1:]
|
||||
|
||||
// assumes loop is well formed, will cause a panic if not
|
||||
diffs := pipes[grid[coords[0]][coords[1]]]
|
||||
for _, diff := range diffs {
|
||||
nextRow, nextCol := coords[0]+diff[0], coords[1]+diff[1]
|
||||
if isInRange(grid, nextRow, nextCol) && !loopCoords[[2]int{nextRow, nextCol}] {
|
||||
toVisit = append(toVisit, [2]int{nextRow, nextCol})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if part == 1 {
|
||||
@@ -119,26 +103,153 @@ func pipeMaze(input string, part int) int {
|
||||
|
||||
// part 2
|
||||
|
||||
newGrid := make([][]string, len(grid))
|
||||
// create copy of grid with all non-loop spots replaced with a period
|
||||
reducedGrid := make([][]string, len(grid))
|
||||
for i := 0; i < len(grid); i++ {
|
||||
for j := 0; j < len(grid[0]); j++ {
|
||||
if loopCoords[[2]int{i, j}] {
|
||||
newGrid[i] = append(newGrid[i], grid[i][j])
|
||||
reducedGrid[i] = append(reducedGrid[i], grid[i][j])
|
||||
} else {
|
||||
newGrid[i] = append(newGrid[i], ".")
|
||||
reducedGrid[i] = append(reducedGrid[i], ".")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range newGrid {
|
||||
fmt.Println(r)
|
||||
// expand grid to double plus 2 in both dimensions to account for squeezing between pipes
|
||||
// the plus two is to add an empty row/column on each side for easier traversing from the outside
|
||||
expandedGrid := [][]string{}
|
||||
expandedGrid = append(expandedGrid, make([]string, len(reducedGrid[0])*2+2))
|
||||
|
||||
for r, rows := range reducedGrid {
|
||||
expandedGrid = append(expandedGrid, make([]string, len(reducedGrid[0])*2+2))
|
||||
for c, val := range rows {
|
||||
expandedGrid[r*2+1][c*2+1] = val
|
||||
}
|
||||
// empty row
|
||||
expandedGrid = append(expandedGrid, make([]string, len(reducedGrid[0])*2+2))
|
||||
}
|
||||
|
||||
return -1
|
||||
// fill gaps between loop coords so we have an encased area again
|
||||
// we can naively try to fill in every empty spot because only ones with two valid connecting
|
||||
// pipes will be filled
|
||||
for r, rows := range expandedGrid {
|
||||
for c, val := range rows {
|
||||
if val == "" {
|
||||
fillGridLocation(expandedGrid, r, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// replacing empty strings with spaces makes the printout human readable
|
||||
for r, rows := range expandedGrid {
|
||||
for c, val := range rows {
|
||||
if val == "" {
|
||||
expandedGrid[r][c] = " "
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toVisit = [][2]int{
|
||||
{0, 0},
|
||||
}
|
||||
seen := map[[2]int]bool{}
|
||||
for len(toVisit) > 0 {
|
||||
coords := toVisit[0]
|
||||
toVisit = toVisit[1:]
|
||||
if seen[coords] {
|
||||
continue
|
||||
}
|
||||
seen[coords] = true
|
||||
|
||||
// delete reachable dots
|
||||
if expandedGrid[coords[0]][coords[1]] == "." {
|
||||
expandedGrid[coords[0]][coords[1]] = " "
|
||||
}
|
||||
|
||||
for _, diff := range [][2]int{
|
||||
{-1, 0},
|
||||
{1, 0},
|
||||
{0, -1},
|
||||
{0, 1},
|
||||
} {
|
||||
nextRow := coords[0] + diff[0]
|
||||
nextCol := coords[1] + diff[1]
|
||||
if isInRange(expandedGrid, nextRow, nextCol) {
|
||||
if expandedGrid[nextRow][nextCol] == "." || expandedGrid[nextRow][nextCol] == " " {
|
||||
toVisit = append(toVisit, [2]int{nextRow, nextCol})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// count remaining dots
|
||||
var ans int
|
||||
for _, rows := range expandedGrid {
|
||||
for _, val := range rows {
|
||||
if val == "." {
|
||||
ans++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for _, rows := range expandedGrid {
|
||||
// fmt.Println(rows)
|
||||
// }
|
||||
|
||||
return ans
|
||||
}
|
||||
|
||||
func fillGridLocation(grid [][]string, r, c int) {
|
||||
// inputs are nice and exactly two adjacent cells that connect to S
|
||||
// check four directions from start
|
||||
leftCol := c - 1
|
||||
rightCol := c + 1
|
||||
upRow := r - 1
|
||||
downRow := r + 1
|
||||
|
||||
var combinedString string
|
||||
// check left for inRange and possible valid pipe types
|
||||
if isInRange(grid, r, leftCol) &&
|
||||
(grid[r][leftCol] == "-" || grid[r][leftCol] == "L" || grid[r][leftCol] == "F") {
|
||||
combinedString += "left"
|
||||
}
|
||||
// right
|
||||
if isInRange(grid, r, rightCol) &&
|
||||
(grid[r][rightCol] == "-" || grid[r][rightCol] == "J" || grid[r][rightCol] == "7") {
|
||||
combinedString += "right"
|
||||
}
|
||||
// up
|
||||
if isInRange(grid, upRow, c) &&
|
||||
(grid[upRow][c] == "|" || grid[upRow][c] == "7" || grid[upRow][c] == "F") {
|
||||
combinedString += "up"
|
||||
}
|
||||
if isInRange(grid, downRow, c) &&
|
||||
(grid[downRow][c] == "|" || grid[downRow][c] == "J" || grid[downRow][c] == "L") {
|
||||
combinedString += "down"
|
||||
}
|
||||
|
||||
switch combinedString {
|
||||
case "leftup":
|
||||
grid[r][c] = "J"
|
||||
case "leftdown":
|
||||
grid[r][c] = "7"
|
||||
case "rightup":
|
||||
grid[r][c] = "L"
|
||||
case "rightdown":
|
||||
grid[r][c] = "F"
|
||||
case "leftright":
|
||||
grid[r][c] = "-"
|
||||
case "updown":
|
||||
grid[r][c] = "|"
|
||||
// default:
|
||||
// do not panic so we can use this function more naively for the expanded grid
|
||||
// could return an error instead and choose to check it for part1 where we NEED it to find a result
|
||||
// panic("ineligible configuration: " + combinedString)
|
||||
}
|
||||
}
|
||||
|
||||
func isInRange(grid [][]string, row, col int) bool {
|
||||
return row >= 0 && row < len(grid) && col >= 0 && col <= len(grid)
|
||||
return row >= 0 && row < len(grid) && col >= 0 && col < len(grid[0])
|
||||
}
|
||||
|
||||
func parseInput(input string) (ans [][]string) {
|
||||
|
||||
@@ -106,12 +106,12 @@ func Test_pipeMaze(t *testing.T) {
|
||||
part: 2,
|
||||
want: 10,
|
||||
},
|
||||
// {
|
||||
// name: "actual part 2",
|
||||
// input: input,
|
||||
// part: 2,
|
||||
// want: 0,
|
||||
// },
|
||||
{
|
||||
name: "actual part 2",
|
||||
input: input,
|
||||
part: 2,
|
||||
want: 493,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user