adventofcode/2022/day18/main.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
}