package common import ( "bufio" "math" "strconv" "strings" ) type Cube struct { x, y, z int } type Grid struct { cubes map[Cube]struct{} outsideCubes map[Cube]struct{} exposedFaces int insideFaces int minX, maxX, minY, maxY, minZ, maxZ int } func (grid *Grid) AddCube(x, y, z int) { grid.cubes[Cube{x, y, z}] = struct{}{} grid.exposedFaces += 6 grid.exposedFaces -= 2 * grid.GetCoveredFaces(x, y, z) if x <= grid.minX { grid.minX = x - 1 } if x >= grid.maxX { grid.maxX = x + 1 } if y <= grid.minY { grid.minY = y - 1 } if y >= grid.maxY { grid.maxY = y + 1 } if z <= grid.minZ { grid.minZ = z - 1 } if z >= grid.maxZ { grid.maxZ = z + 1 } } func (grid *Grid) PropagateWater(cube Cube, visited map[Cube]struct{}) { grid.outsideCubes[cube] = struct{}{} visited2 := make(map[Cube]struct{}) visited2[cube] = struct{}{} cubes := []Cube{{cube.x-1, cube.y, cube.z}, {cube.x+1, cube.y, cube.z}, {cube.x, cube.y-1, cube.z}, {cube.x, cube.y+1, cube.z}, {cube.x, cube.y, cube.z-1}, {cube.x, cube.y, cube.z+1}} for _, cube2 := range cubes { if _, ok := grid.cubes[cube2]; !ok && cube2.x >= grid.minX && cube2.x <= grid.maxX && cube2.y >= grid.minY && cube2.y <= grid.maxY && cube2.z >= grid.minZ && cube2.z <= grid.maxZ { if _, ok := visited[cube2]; !ok { if _, ok := grid.outsideCubes[cube2]; !ok { for c := range(visited) { visited2[c] = visited[c] } grid.PropagateWater(cube2, visited2) } } } } } func (grid *Grid) GetInsideFaces(cube Cube) int { cubes := []Cube{{cube.x-1, cube.y, cube.z}, {cube.x+1, cube.y, cube.z}, {cube.x, cube.y-1, cube.z}, {cube.x, cube.y+1, cube.z}, {cube.x, cube.y, cube.z-1}, {cube.x, cube.y, cube.z+1}} nFaces := 0 for _, cube := range cubes { if _, ok := grid.cubes[cube]; !ok { if _, ok := grid.outsideCubes[cube]; !ok { nFaces++ } } } return nFaces } func (grid *Grid) GetCoveredFaces(x, y, z int) int { coveredFaces := 0 if _, found := grid.cubes[Cube{x-1, y, z}]; found { coveredFaces += 1 } if _, found := grid.cubes[Cube{x+1, y, z}]; found { coveredFaces += 1 } if _, found := grid.cubes[Cube{x, y-1, z}]; found { coveredFaces += 1 } if _, found := grid.cubes[Cube{x, y+1, z}]; found { coveredFaces += 1 } if _, found := grid.cubes[Cube{x, y, z-1}]; found { coveredFaces += 1 } if _, found := grid.cubes[Cube{x, y, z+1}]; found { coveredFaces += 1 } return coveredFaces } func (grid *Grid) GetTotalExposedFaces() int { return grid.exposedFaces } func (grid *Grid) GetTotalOutsideArea() int { return grid.exposedFaces - grid.insideFaces } func Parse(scanner bufio.Scanner) Grid { grid := Grid{make(map[Cube]struct{}), make(map[Cube]struct{}), 0, 0, math.MaxInt32, math.MinInt32, math.MaxInt32, math.MinInt32, math.MaxInt32, math.MinInt32} for scanner.Scan() { line := scanner.Text() splittedLine := strings.Split(line, ",") x, _ := strconv.Atoi(splittedLine[0]) y, _ := strconv.Atoi(splittedLine[1]) z, _ := strconv.Atoi(splittedLine[2]) grid.AddCube(x, y, z) } grid.PropagateWater(Cube{grid.minX, grid.minY, grid.minZ}, make(map[Cube]struct{})) for cube := range(grid.cubes) { grid.insideFaces += grid.GetInsideFaces(cube) } return grid }