140 lines
3.4 KiB
Go
140 lines
3.4 KiB
Go
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
|
|
}
|