Skip to content

Commit

Permalink
2023 day 22, lucianoq
Browse files Browse the repository at this point in the history
  • Loading branch information
lucianoq committed Dec 22, 2023
1 parent d7c6a0e commit 1672165
Show file tree
Hide file tree
Showing 8 changed files with 1,877 additions and 0 deletions.
17 changes: 17 additions & 0 deletions 2023/22/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
main1:
go build -o main1 main1.go common.go

main2:
go build -o main2 main2.go common.go

.PHONY: run1 run2 clean

run1: main1
./main1 <input

run2: main2
./main2 <input

clean:
rm -f main1 main2

193 changes: 193 additions & 0 deletions 2023/22/assignment
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
--- Day 22: Sand Slabs ---

Enough sand has fallen; it can finally filter water for Snow Island.

Well, almost .

The sand has been falling as large compacted bricks of sand, piling up
to form an impressive stack here near the edge of Island Island. In
order to make use of the sand to filter water, some of the bricks will
need to be broken apart - nay, disintegrated - back into freely flowing
sand.

The stack is tall enough that you'll have to be careful about choosing
which bricks to disintegrate; if you disintegrate the wrong brick,
large portions of the stack could topple, which sounds pretty
dangerous.

The Elves responsible for water filtering operations took a snapshot of
the bricks while they were still falling (your puzzle input) which
should let you work out which bricks are safe to disintegrate. For
example:

1,0,1~1,2,1
0,0,2~2,0,2
0,2,3~2,2,3
0,0,4~0,2,4
2,0,5~2,2,5
0,1,6~2,1,6
1,1,8~1,1,9

Each line of text in the snapshot represents the position of a single
brick at the time the snapshot was taken. The position is given as two
x,y,z coordinates - one for each end of the brick - separated by a
tilde ( ~ ). Each brick is made up of a single straight line of cubes,
and the Elves were even careful to choose a time for the snapshot that
had all of the free-falling bricks at integer positions above the
ground , so the whole snapshot is aligned to a three-dimensional cube
grid.

A line like 2,2,2~2,2,2 means that both ends of the brick are at the
same coordinate - in other words, that the brick is a single cube.

Lines like 0,0,10~1,0,10 or 0,0,10~0,1,10 both represent bricks that
are two cubes in volume, both oriented horizontally. The first brick
extends in the x direction, while the second brick extends in the y
direction.

A line like 0,0,1~0,0,10 represents a ten-cube brick which is oriented
vertically . One end of the brick is the cube located at 0,0,1 , while
the other end of the brick is located directly above it at 0,0,10 .

The ground is at z=0 and is perfectly flat; the lowest z value a brick
can have is therefore 1 . So, 5,5,1~5,6,1 and 0,2,1~0,2,5 are both
resting on the ground, but 3,3,2~3,3,3 was above the ground at the time
of the snapshot.

Because the snapshot was taken while the bricks were still falling,
some bricks will still be in the air ; you'll need to start by figuring
out where they will end up. Bricks are magically stabilized, so they
never rotate , even in weird situations like where a long horizontal
brick is only supported on one end. Two bricks cannot occupy the same
position, so a falling brick will come to rest upon the first other
brick it encounters.

Here is the same example again, this time with each brick given a
letter so it can be marked in diagrams:

1,0,1~1,2,1 <- A
0,0,2~2,0,2 <- B
0,2,3~2,2,3 <- C
0,0,4~0,2,4 <- D
2,0,5~2,2,5 <- E
0,1,6~2,1,6 <- F
1,1,8~1,1,9 <- G

At the time of the snapshot, from the side so the x axis goes left to
right, these bricks are arranged like this:

x
012
.G. 9
.G. 8
... 7
FFF 6
..E 5 z
D.. 4
CCC 3
BBB 2
.A. 1
--- 0

Rotating the perspective 90 degrees so the y axis now goes left to
right, the same bricks are arranged like this:

y
012
.G. 9
.G. 8
... 7
.F. 6
EEE 5 z
DDD 4
..C 3
B.. 2
AAA 1
--- 0

Once all of the bricks fall downward as far as they can go, the stack
looks like this, where ? means bricks are hidden behind other bricks at
that location:

x
012
.G. 6
.G. 5
FFF 4
D.E 3 z
??? 2
.A. 1
--- 0

Again from the side:

y
012
.G. 6
.G. 5
.F. 4
??? 3 z
B.C 2
AAA 1
--- 0

Now that all of the bricks have settled, it becomes easier to tell
which bricks are supporting which other bricks:
* Brick A is the only brick supporting bricks B and C .
* Brick B is one of two bricks supporting brick D and brick E .
* Brick C is the other brick supporting brick D and brick E .
* Brick D supports brick F .
* Brick E also supports brick F .
* Brick F supports brick G .
* Brick G isn't supporting any bricks.

Your first task is to figure out which bricks are safe to disintegrate
. A brick can be safely disintegrated if, after removing it, no other
bricks would fall further directly downward. Don't actually
disintegrate any bricks - just determine what would happen if, for each
brick, only that brick were disintegrated. Bricks can be disintegrated
even if they're completely surrounded by other bricks; you can squeeze
between bricks if you need to.

In this example, the bricks can be disintegrated as follows:
* Brick A cannot be disintegrated safely; if it were disintegrated,
bricks B and C would both fall.
* Brick B can be disintegrated; the bricks above it ( D and E ) would
still be supported by brick C .
* Brick C can be disintegrated; the bricks above it ( D and E ) would
still be supported by brick B .
* Brick D can be disintegrated; the brick above it ( F ) would still
be supported by brick E .
* Brick E can be disintegrated; the brick above it ( F ) would still
be supported by brick D .
* Brick F cannot be disintegrated; the brick above it ( G ) would
fall.
* Brick G can be disintegrated; it does not support any other bricks.

So, in this example, 5 bricks can be safely disintegrated.

Figure how the blocks will settle based on the snapshot. Once they've
settled, consider disintegrating a single brick; how many bricks could
be safely chosen as the one to get disintegrated?

--- Part Two ---

Disintegrating bricks one at a time isn't going to be fast enough.
While it might sound dangerous, what you really need is a chain
reaction .

You'll need to figure out the best brick to disintegrate. For each
brick, determine how many other bricks would fall if that brick were
disintegrated.

Using the same example as above:
* Disintegrating brick A would cause all 6 other bricks to fall.
* Disintegrating brick F would cause only 1 other brick, G , to fall.

Disintegrating any other brick would cause no other bricks to fall. So,
in this example, the sum of the number of other bricks that would fall
as a result of disintegrating each brick is 7 .

For each brick, determine how many other bricks would fall if that
brick were disintegrated. What is the sum of the number of other bricks
that would fall?
136 changes: 136 additions & 0 deletions 2023/22/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package main

import (
"bufio"
"fmt"
"os"
"strings"
)

type P struct{ X, Y, Z int }

type Brick struct {
ID int
Start, End P
}

type Space struct {
Map map[P]int
Bricks map[int]*Brick
}

func parse() *Space {
s := NewSpace()
scanner := bufio.NewScanner(os.Stdin)
for i := 0; scanner.Scan(); i++ {
ff := strings.Split(scanner.Text(), "~")
var start, end P
_, _ = fmt.Sscanf(ff[0], "%d,%d,%d", &start.X, &start.Y, &start.Z)
_, _ = fmt.Sscanf(ff[1], "%d,%d,%d", &end.X, &end.Y, &end.Z)
s.Add(&Brick{i, start, end})
}
return s
}

func NewSpace() *Space {
return &Space{
Map: make(map[P]int),
Bricks: make(map[int]*Brick),
}
}

func (s *Space) Add(b *Brick) {
s.Bricks[b.ID] = b
for x := b.Start.X; x <= b.End.X; x++ {
for y := b.Start.Y; y <= b.End.Y; y++ {
for z := b.Start.Z; z <= b.End.Z; z++ {
s.Map[P{x, y, z}] = b.ID
}
}
}
}

func (s *Space) Fall() int {
fallen := map[int]struct{}{}
for changed := true; changed; {
changed = false
for _, b := range s.Bricks {
for s.EmptyBelow(b) {
s.Move(b, P{0, 0, -1})
changed = true
fallen[b.ID] = struct{}{}
}
}
}
return len(fallen)
}

func (s *Space) EmptyBelow(b *Brick) bool {
if b.Start.Z == 1 {
return false
}
for x := b.Start.X; x <= b.End.X; x++ {
for y := b.Start.Y; y <= b.End.Y; y++ {
if _, full := s.Map[P{x, y, b.Start.Z - 1}]; full {
return false
}
}
}
return true
}

func (s *Space) Move(b *Brick, p P) {
for x := b.Start.X; x <= b.End.X; x++ {
for y := b.Start.Y; y <= b.End.Y; y++ {
for z := b.Start.Z; z <= b.End.Z; z++ {
delete(s.Map, P{x, y, z})
}
}
}
for x := b.Start.X + p.X; x <= b.End.X+p.X; x++ {
for y := b.Start.Y + p.Y; y <= b.End.Y+p.Y; y++ {
for z := b.Start.Z + p.Z; z <= b.End.Z+p.Z; z++ {
s.Map[P{x, y, z}] = b.ID
}
}
}

s.Bricks[b.ID].Start.X += p.X
s.Bricks[b.ID].Start.Y += p.Y
s.Bricks[b.ID].Start.Z += p.Z
s.Bricks[b.ID].End.X += p.X
s.Bricks[b.ID].End.Y += p.Y
s.Bricks[b.ID].End.Z += p.Z
}

func (s *Space) TopOf(b *Brick) []*Brick {
var onTop []*Brick
for x := b.Start.X; x <= b.End.X; x++ {
for y := b.Start.Y; y <= b.End.Y; y++ {
if id, ok := s.Map[P{x, y, b.End.Z + 1}]; ok {
onTop = append(onTop, s.Bricks[id])
}
}
}
return onTop
}

func (s *Space) SafeToDisintegrate(b *Brick) bool {
for _, topB := range s.TopOf(b) {
if s.DependsOn(topB, b) {
return false
}
}
return true
}

func (s *Space) DependsOn(topB, botB *Brick) bool {
for x := topB.Start.X; x <= topB.End.X; x++ {
for y := topB.Start.Y; y <= topB.End.Y; y++ {
if id, full := s.Map[P{x, y, topB.Start.Z - 1}]; full && id != botB.ID {
return false
}
}
}
return true
}

0 comments on commit 1672165

Please sign in to comment.