230 lines
3.8 KiB
Go
230 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/jdkaplan/advent-of-code/aoc"
|
|
)
|
|
|
|
func main() {
|
|
lines := aoc.Input().ReadLines("day22.txt")
|
|
steps := parseInput(lines)
|
|
fmt.Println(part1(steps))
|
|
fmt.Println(part2(steps))
|
|
}
|
|
|
|
func parseInput(lines []string) (steps []Step) {
|
|
for _, line := range lines {
|
|
steps = append(steps, NewStep(line))
|
|
}
|
|
return
|
|
}
|
|
|
|
func part1(steps []Step) int {
|
|
r := make(Reactor)
|
|
r.Initialize(steps)
|
|
return r.CountOn()
|
|
}
|
|
|
|
func part2(steps []Step) int {
|
|
return reboot(steps)
|
|
}
|
|
|
|
type Step struct {
|
|
op string
|
|
x1, x2 int
|
|
y1, y2 int
|
|
z1, z2 int
|
|
}
|
|
|
|
func NewStep(line string) (s Step) {
|
|
aoc.MustScan(
|
|
line,
|
|
"%s x=%d..%d,y=%d..%d,z=%d..%d",
|
|
&s.op, &s.x1, &s.x2, &s.y1, &s.y2, &s.z1, &s.z2,
|
|
)
|
|
return
|
|
}
|
|
|
|
type Cell struct{ x, y, z int }
|
|
|
|
type Reactor map[Cell]bool
|
|
|
|
func (r Reactor) Initialize(steps []Step) {
|
|
for _, s := range steps {
|
|
on := s.op == "on"
|
|
for x := max(-50, s.x1); x <= min(50, s.x2); x++ {
|
|
for y := max(-50, s.y1); y <= min(50, s.y2); y++ {
|
|
for z := max(-50, s.z1); z <= min(50, s.z2); z++ {
|
|
r[Cell{x, y, z}] = on
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func min(a, b int) int {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func max(a, b int) int {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func (r Reactor) CountOn() (count int) {
|
|
for _, on := range r {
|
|
if on {
|
|
count++
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func reboot(steps []Step) int {
|
|
var cells []Cuboid
|
|
for _, s := range steps {
|
|
c := Cuboid{
|
|
x: Interval{s.x1, s.x2},
|
|
y: Interval{s.y1, s.y2},
|
|
z: Interval{s.z1, s.z2},
|
|
}
|
|
cells = cut(cells, c)
|
|
if s.op == "on" {
|
|
cells = append(cells, c)
|
|
}
|
|
}
|
|
return countOn(cells)
|
|
}
|
|
|
|
func countOn(cells []Cuboid) (count int) {
|
|
for _, cell := range cells {
|
|
count += cell.Size()
|
|
}
|
|
return
|
|
}
|
|
|
|
func cut(cells []Cuboid, cutter Cuboid) (stillOn []Cuboid) {
|
|
for _, cell := range cells {
|
|
ix := cell.Intersect(cutter)
|
|
if ix.Empty() {
|
|
stillOn = append(stillOn, cell)
|
|
continue
|
|
}
|
|
mm := cell.Minus(*ix)
|
|
stillOn = append(stillOn, mm...)
|
|
}
|
|
return
|
|
}
|
|
|
|
type Cuboid struct {
|
|
x, y, z Interval
|
|
}
|
|
|
|
func (c Cuboid) String() string {
|
|
return fmt.Sprintf("x: %s, y: %s, z: %s", c.x, c.y, c.z)
|
|
}
|
|
|
|
func (c *Cuboid) Empty() bool {
|
|
return c == nil || c.x.Empty() || c.y.Empty() || c.z.Empty()
|
|
}
|
|
|
|
func (c Cuboid) Size() int {
|
|
return c.x.Size() * c.y.Size() * c.z.Size()
|
|
}
|
|
|
|
func (c Cuboid) Intersect(d Cuboid) (intersection *Cuboid) {
|
|
ix := c.x.Intersect(d.x)
|
|
if ix.Empty() {
|
|
return nil
|
|
}
|
|
|
|
iy := c.y.Intersect(d.y)
|
|
if iy.Empty() {
|
|
return nil
|
|
}
|
|
|
|
iz := c.z.Intersect(d.z)
|
|
if iz.Empty() {
|
|
return nil
|
|
}
|
|
|
|
return &Cuboid{ix, iy, iz}
|
|
}
|
|
|
|
func (c Cuboid) Minus(d Cuboid) []Cuboid {
|
|
x0, x1, x2 := c.x.Split(d.x)
|
|
y0, y1, y2 := c.y.Split(d.y)
|
|
z0, z1, z2 := c.z.Split(d.z)
|
|
return nonEmpty([]Cuboid{
|
|
{x0, y0, z0},
|
|
{x0, y0, z1},
|
|
{x0, y0, z2},
|
|
{x0, y1, z0},
|
|
{x0, y1, z1},
|
|
{x0, y1, z2},
|
|
{x0, y2, z0},
|
|
{x0, y2, z1},
|
|
{x0, y2, z2},
|
|
{x1, y0, z0},
|
|
{x1, y0, z1},
|
|
{x1, y0, z2},
|
|
{x1, y1, z0},
|
|
{x1, y1, z2},
|
|
{x1, y2, z0},
|
|
{x1, y2, z1},
|
|
{x1, y2, z2},
|
|
// {x1, y1, z1}, // Don't include the intersection block
|
|
{x2, y0, z0},
|
|
{x2, y0, z1},
|
|
{x2, y0, z2},
|
|
{x2, y1, z0},
|
|
{x2, y1, z1},
|
|
{x2, y1, z2},
|
|
{x2, y2, z0},
|
|
{x2, y2, z1},
|
|
{x2, y2, z2},
|
|
})
|
|
}
|
|
|
|
func nonEmpty(cs []Cuboid) (nonEmpty []Cuboid) {
|
|
for _, c := range cs {
|
|
if !c.Empty() {
|
|
nonEmpty = append(nonEmpty, c)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
type Interval [2]int
|
|
|
|
func (i Interval) String() string {
|
|
return fmt.Sprintf("[%d, %d]", i[0], i[1])
|
|
}
|
|
|
|
func (i Interval) Empty() bool {
|
|
return i.Size() <= 0
|
|
}
|
|
|
|
func (i Interval) Size() int {
|
|
return i[1] - i[0] + 1
|
|
}
|
|
|
|
func (i Interval) Intersect(j Interval) Interval {
|
|
return Interval{
|
|
max(i[0], j[0]),
|
|
min(i[1], j[1]),
|
|
}
|
|
}
|
|
|
|
func (i Interval) Split(j Interval) (prefix, inside, suffix Interval) {
|
|
prefix = Interval{i[0], j[0] - 1}
|
|
inside = Interval{j[0], j[1]}
|
|
suffix = Interval{j[1] + 1, i[1]}
|
|
return
|
|
}
|