206 lines
4.3 KiB
Go
206 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"strings"
|
|
|
|
h "git.bullercodeworks.com/brian/adventofcode/helpers"
|
|
)
|
|
|
|
func main() {
|
|
inp := h.StdinToStringSlice()
|
|
part1(inp)
|
|
part2(inp)
|
|
}
|
|
|
|
func part1(inp []string) {
|
|
scan := ParseSurface(inp)
|
|
var surface int
|
|
for k := range scan {
|
|
for _, chk := range []pos{k.Top(), k.Bottom(), k.Left(), k.Right(), k.Front(), k.Back()} {
|
|
if _, ok := scan[chk]; !ok {
|
|
surface++
|
|
}
|
|
}
|
|
}
|
|
fmt.Println("# Part 1")
|
|
fmt.Println("Surface Area:", surface)
|
|
}
|
|
func part2(inp []string) {
|
|
scan := ParseCubes(inp)
|
|
scanExternal(scan)
|
|
|
|
var surface int
|
|
for _, cb := range scan {
|
|
if cb.top == nil && !cb.topInt {
|
|
surface++
|
|
}
|
|
if cb.bottom == nil && !cb.bottomInt {
|
|
surface++
|
|
}
|
|
if cb.right == nil && !cb.rightInt {
|
|
surface++
|
|
}
|
|
if cb.left == nil && !cb.leftInt {
|
|
surface++
|
|
}
|
|
if cb.front == nil && !cb.frontInt {
|
|
surface++
|
|
}
|
|
if cb.back == nil && !cb.backInt {
|
|
surface++
|
|
}
|
|
}
|
|
fmt.Println("# Part 2")
|
|
fmt.Println("External Surface Area:", surface)
|
|
}
|
|
|
|
func scanExternal(m map[pos]*cube) {
|
|
min := pos{x: math.MaxInt, y: math.MaxInt, z: math.MaxInt}
|
|
max := pos{x: math.MinInt, y: math.MinInt, z: math.MinInt}
|
|
for k := range m {
|
|
min.x = h.Min(min.x, k.x)
|
|
min.y = h.Min(min.y, k.y)
|
|
min.z = h.Min(min.z, k.z)
|
|
max.x = h.Max(max.x, k.x)
|
|
max.y = h.Max(max.y, k.y)
|
|
max.z = h.Max(max.z, k.z)
|
|
}
|
|
edges := make(map[pos]bool)
|
|
for z := min.z - 1; z < max.z+1; z++ {
|
|
for y := min.y - 1; y < max.y+1; y++ {
|
|
for x := min.x - 1; x < max.x+1; x++ {
|
|
edges[pos{min.x, y, z}] = true
|
|
edges[pos{x, min.y, z}] = true
|
|
edges[pos{x, y, min.z}] = true
|
|
edges[pos{max.x, y, z}] = true
|
|
edges[pos{x, max.y, z}] = true
|
|
edges[pos{x, y, max.z}] = true
|
|
}
|
|
}
|
|
}
|
|
for k, v := range m {
|
|
if v.left == nil && !canPath(k.Left(), edges, m) {
|
|
v.leftInt = true
|
|
}
|
|
if v.right == nil && !canPath(k.Right(), edges, m) {
|
|
v.rightInt = true
|
|
}
|
|
if v.top == nil && !canPath(k.Top(), edges, m) {
|
|
v.topInt = true
|
|
}
|
|
if v.bottom == nil && !canPath(k.Bottom(), edges, m) {
|
|
v.bottomInt = true
|
|
}
|
|
if v.front == nil && !canPath(k.Front(), edges, m) {
|
|
v.frontInt = true
|
|
}
|
|
if v.back == nil && !canPath(k.Back(), edges, m) {
|
|
v.backInt = true
|
|
}
|
|
}
|
|
}
|
|
|
|
func canPath(from pos, to map[pos]bool, scan map[pos]*cube) bool {
|
|
queue := []pos{from}
|
|
visit := make(map[pos]bool)
|
|
result := false
|
|
for len(queue) > 0 {
|
|
cur := queue[0]
|
|
queue = queue[1:]
|
|
if _, ok := to[cur]; ok {
|
|
result = true
|
|
break
|
|
}
|
|
if _, ok := visit[cur]; ok {
|
|
continue
|
|
}
|
|
visit[cur] = true
|
|
moves := []pos{
|
|
cur.Top(), cur.Bottom(),
|
|
cur.Left(), cur.Right(),
|
|
cur.Front(), cur.Back(),
|
|
}
|
|
for _, mv := range moves {
|
|
if _, ok := scan[mv]; ok {
|
|
continue
|
|
}
|
|
queue = append(queue, mv)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func ParseSurface(inp []string) map[pos]bool {
|
|
m := make(map[pos]bool)
|
|
for i := range inp {
|
|
pts := strings.Split(inp[i], ",")
|
|
m[pos{
|
|
x: h.Atoi(pts[0]),
|
|
y: h.Atoi(pts[1]),
|
|
z: h.Atoi(pts[2]),
|
|
}] = true
|
|
}
|
|
return m
|
|
}
|
|
func ParseCubes(inp []string) map[pos]*cube {
|
|
m := make(map[pos]*cube)
|
|
for i := range inp {
|
|
pts := strings.Split(inp[i], ",")
|
|
pos := pos{
|
|
x: h.Atoi(pts[0]),
|
|
y: h.Atoi(pts[1]),
|
|
z: h.Atoi(pts[2]),
|
|
}
|
|
m[pos] = &cube{position: pos}
|
|
}
|
|
for k, v := range m {
|
|
if _, ok := m[k.Top()]; ok {
|
|
m[k.Top()].bottom = v
|
|
v.top = m[k.Top()]
|
|
}
|
|
if _, ok := m[k.Bottom()]; ok {
|
|
m[k.Bottom()].top = v
|
|
v.bottom = m[k.Bottom()]
|
|
}
|
|
if _, ok := m[k.Left()]; ok {
|
|
m[k.Left()].right = v
|
|
v.left = m[k.Left()]
|
|
}
|
|
if _, ok := m[k.Right()]; ok {
|
|
m[k.Right()].left = v
|
|
v.right = m[k.Right()]
|
|
}
|
|
if _, ok := m[k.Front()]; ok {
|
|
m[k.Front()].back = v
|
|
v.front = m[k.Front()]
|
|
}
|
|
if _, ok := m[k.Back()]; ok {
|
|
m[k.Back()].front = v
|
|
v.back = m[k.Back()]
|
|
}
|
|
}
|
|
return m
|
|
}
|
|
|
|
type pos struct{ x, y, z int }
|
|
|
|
func (p pos) Top() pos { return pos{x: p.x, y: p.y + 1, z: p.z} }
|
|
func (p pos) Bottom() pos { return pos{x: p.x, y: p.y - 1, z: p.z} }
|
|
func (p pos) Left() pos { return pos{x: p.x - 1, y: p.y, z: p.z} }
|
|
func (p pos) Right() pos { return pos{x: p.x + 1, y: p.y, z: p.z} }
|
|
func (p pos) Front() pos { return pos{x: p.x, y: p.y, z: p.z - 1} }
|
|
func (p pos) Back() pos { return pos{x: p.x, y: p.y, z: p.z + 1} }
|
|
|
|
type cube struct {
|
|
position pos
|
|
top, bottom *cube
|
|
front, back *cube
|
|
left, right *cube
|
|
|
|
topInt, bottomInt bool
|
|
frontInt, backInt bool
|
|
leftInt, rightInt bool
|
|
}
|