You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
3.9 KiB
Go
187 lines
3.9 KiB
Go
package day22
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"slices"
|
|
"strings"
|
|
|
|
"gitea.paas.celticinfo.fr/oabrivard/aoc2023/utils"
|
|
)
|
|
|
|
type Coord struct {
|
|
x int
|
|
y int
|
|
z int
|
|
}
|
|
|
|
type Brick struct {
|
|
ID int
|
|
start Coord
|
|
end Coord
|
|
supportedBy map[int]*Brick
|
|
supports map[int]*Brick
|
|
}
|
|
|
|
type Tower struct {
|
|
floors [][][]*Brick
|
|
maxFloors int
|
|
floorWidth int
|
|
}
|
|
|
|
// parseBricks returns an array of bricks sorted by height
|
|
func parseBricks(lines []string) []*Brick {
|
|
bricks := []*Brick{}
|
|
ID := 1
|
|
|
|
for _, line := range lines {
|
|
parts := strings.Split(line, "~")
|
|
|
|
start := utils.ParseIntArray(parts[0], ",")
|
|
end := utils.ParseIntArray(parts[1], ",")
|
|
|
|
newBrick := Brick{
|
|
ID: ID,
|
|
start: Coord{start[0], start[1], start[2]},
|
|
end: Coord{end[0], end[1], end[2]},
|
|
supportedBy: map[int]*Brick{},
|
|
supports: map[int]*Brick{},
|
|
}
|
|
|
|
if newBrick.start.x > newBrick.end.x ||
|
|
newBrick.start.y > newBrick.end.y ||
|
|
newBrick.start.z > newBrick.end.z {
|
|
log.Fatalf("Input error for brick %v", newBrick)
|
|
}
|
|
|
|
bricks = append(bricks, &newBrick)
|
|
ID++
|
|
}
|
|
|
|
slices.SortFunc(bricks, func(a, b *Brick) int {
|
|
return a.start.z - b.start.z
|
|
})
|
|
|
|
return bricks
|
|
}
|
|
|
|
// create a tower with an empty floor at level 0
|
|
func NewTower(height, width int) Tower {
|
|
floors := make([][][]*Brick, height+1)
|
|
|
|
for z := range floors {
|
|
floors[z] = make([][]*Brick, width)
|
|
for x := range floors[z] {
|
|
floors[z][x] = make([]*Brick, width)
|
|
}
|
|
}
|
|
|
|
return Tower{floors, height, width}
|
|
}
|
|
|
|
func (tower *Tower) maxHeight() int { return tower.maxFloors }
|
|
|
|
func (tower *Tower) intersections(brick *Brick, currZ int) []*Brick {
|
|
bricks := []*Brick{}
|
|
|
|
for x := brick.start.x; x <= brick.end.x; x++ {
|
|
for y := brick.start.y; y <= brick.end.y; y++ {
|
|
if tower.floors[currZ][x][y] != nil {
|
|
bricks = append(bricks, tower.floors[currZ][x][y])
|
|
}
|
|
}
|
|
}
|
|
|
|
return bricks
|
|
}
|
|
|
|
func (tower *Tower) layout(brick *Brick, currZ int) {
|
|
brickHeight := brick.end.z - brick.start.z + 1
|
|
|
|
for z := currZ; z < currZ+brickHeight; z++ {
|
|
for x := brick.start.x; x <= brick.end.x; x++ {
|
|
for y := brick.start.y; y <= brick.end.y; y++ {
|
|
tower.floors[z][x][y] = brick
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (tower *Tower) addBrick(brick *Brick) {
|
|
currZ := tower.maxHeight()
|
|
|
|
for currZ > 0 {
|
|
intersections := tower.intersections(brick, currZ-1)
|
|
|
|
if len(intersections) > 0 {
|
|
// Layout the current brick on the current floor
|
|
tower.layout(brick, currZ)
|
|
|
|
// add the current brick to the "supports" list of each supporting brick
|
|
// and add the supporting bricks to the "supportedBy" list of the current brick
|
|
for _, supportingBrick := range intersections {
|
|
supportingBrick.supports[brick.ID] = brick
|
|
brick.supportedBy[supportingBrick.ID] = supportingBrick
|
|
}
|
|
|
|
// brick is layed out, exit function
|
|
return
|
|
}
|
|
|
|
currZ--
|
|
}
|
|
|
|
// layout the brick on the first floor
|
|
tower.layout(brick, 1)
|
|
}
|
|
|
|
func (tower *Tower) Print() {
|
|
//for z := tower.maxHeight(); z >= 0; z-- {
|
|
for z := 8; z > 0; z-- {
|
|
for x := 0; x < tower.floorWidth; x++ {
|
|
for y := 0; y < tower.floorWidth; y++ {
|
|
if tower.floors[z][x][y] != nil {
|
|
fmt.Print(tower.floors[z][x][y].ID)
|
|
} else {
|
|
fmt.Print(".")
|
|
}
|
|
}
|
|
fmt.Println(" ")
|
|
}
|
|
fmt.Println(" ")
|
|
}
|
|
fmt.Println(" ")
|
|
}
|
|
|
|
func Part1(lines []string) (int, []*Brick) {
|
|
bricks := parseBricks(lines)
|
|
tower := NewTower(500, 10)
|
|
|
|
for _, brick := range bricks {
|
|
fmt.Printf("Placing brick %d (%v)~(%v)\n", brick.ID, brick.start, brick.end)
|
|
tower.addBrick(brick)
|
|
}
|
|
|
|
count := 0
|
|
for _, brick := range bricks {
|
|
canDisintegrate := true
|
|
|
|
for _, supportedBrick := range brick.supports {
|
|
if len(supportedBrick.supportedBy) == 0 {
|
|
log.Fatalln("Bug ", brick)
|
|
}
|
|
if len(supportedBrick.supportedBy) == 1 {
|
|
canDisintegrate = false
|
|
}
|
|
}
|
|
|
|
if canDisintegrate {
|
|
fmt.Println(brick.ID, string('A'+byte(brick.ID-1)))
|
|
count++
|
|
}
|
|
}
|
|
|
|
tower.Print()
|
|
return count, bricks
|
|
}
|