github.com/mnlphlp/aoc22@v0.0.0-20230330151331-c1dc4bff1b9b/day18/day18.go (about) 1 package day18 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/mnlphlp/aoc22/util" 8 ) 9 10 func parseInput(input string) map[util.Pos3]struct{} { 11 droplet := make(map[util.Pos3]struct{}, 0) 12 for _, line := range strings.Split(input, "\n") { 13 if line == "" { 14 continue 15 } 16 pos := util.Pos3{} 17 fmt.Sscanf(line, "%d,%d,%d", &pos.X, &pos.Y, &pos.Z) 18 droplet[pos] = struct{}{} 19 } 20 return droplet 21 } 22 23 // all 6 directions 24 var directions = []util.Pos3{util.Pos3{1, 0, 0}, util.Pos3{-1, 0, 0}, util.Pos3{0, 1, 0}, util.Pos3{0, -1, 0}, util.Pos3{0, 0, 1}, util.Pos3{0, 0, -1}} 25 26 func surfaceArea(droplet map[util.Pos3]struct{}) int { 27 surface := 0 28 for pos := range droplet { 29 // check how many faces are not covered by other cubes 30 for _, d := range directions { 31 if _, ok := droplet[pos.Add(d)]; !ok { 32 surface++ 33 } 34 } 35 } 36 return surface 37 } 38 39 func pocketSurfaceArea(droplet map[util.Pos3]struct{}) int { 40 // find the smallest and largest x, y and z coordinates 41 min, max := util.Pos3{X: 0, Y: 0, Z: 0}, util.Pos3{X: 0, Y: 0, Z: 0} 42 for pos := range droplet { 43 min = util.MinPos(min, pos) 44 max = util.MaxPos(max, pos) 45 } 46 // find closed pockets in the droplet 47 pockets := make(map[util.Pos3]struct{}, 0) 48 for x := min.X + 1; x < max.X; x++ { 49 for y := min.Y + 1; y < max.Y; y++ { 50 for z := min.Z + 1; z < max.Z; z++ { 51 pos := util.Pos3{X: x, Y: y, Z: z} 52 if _, solid := droplet[pos]; solid { 53 continue 54 } 55 if _, inPocket := pockets[pos]; inPocket { 56 skipDistance := 0 57 for inPocket { 58 skipDistance++ 59 _, inPocket = pockets[pos.Add(util.Pos3{X: 0, Y: 0, Z: skipDistance})] 60 } 61 z += skipDistance - 1 62 continue 63 } 64 // open spot found, might be a pocket 65 // check if the opening is connected to the outside 66 // If not add to pockets 67 pocketTmp := make(map[util.Pos3]struct{}) 68 open := make(map[util.Pos3]struct{}, 0) 69 open[pos] = struct{}{} 70 for len(open) > 0 { 71 // get first element 72 var p util.Pos3 73 for p = range open { 74 delete(open, p) 75 pocketTmp[p] = struct{}{} 76 break 77 } 78 // check if it is connected to the outside 79 if p.X <= min.X || p.X >= max.X || p.Y <= min.Y || p.Y >= max.Y || p.Z <= min.Z || p.Z >= max.Z { 80 pocketTmp = nil 81 break 82 } 83 // add all neighbors to open 84 for _, d := range directions { 85 n := p.Add(d) 86 if _, solid := droplet[n]; !solid { 87 if _, inPocket := pocketTmp[n]; !inPocket { 88 open[n] = struct{}{} 89 } 90 } 91 } 92 } 93 for p := range pocketTmp { 94 pockets[p] = struct{}{} 95 } 96 97 } 98 } 99 } 100 // calculate surface area of pockets 101 return surfaceArea(pockets) 102 } 103 104 func Solve(input string, debug bool, task int) (string, string) { 105 res1, res2 := 0, 0 106 droplet := parseInput(input) 107 if debug { 108 fmt.Println("Droplet: ", droplet) 109 } 110 if task != 2 { 111 res1 = surfaceArea(droplet) 112 fmt.Println("Result 1: ", res1) 113 } 114 if task != 1 { 115 if res1 == 0 { 116 res1 = surfaceArea(droplet) 117 } 118 pocketArea := pocketSurfaceArea(droplet) 119 res2 = res1 - pocketArea 120 fmt.Println("Result 2: ", res2) 121 } 122 123 return fmt.Sprint(res1), fmt.Sprint(res2) 124 }