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.
463 lines
11 KiB
Go
463 lines
11 KiB
Go
package day22
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"slices"
|
|
"strings"
|
|
|
|
"gitea.paas.celticinfo.fr/oabrivard/aoc2023/utils"
|
|
)
|
|
|
|
const BASE_MATRIX_SIZE = 10
|
|
|
|
type Coord struct {
|
|
row int
|
|
col int
|
|
height int
|
|
}
|
|
|
|
type Brick struct {
|
|
ID int
|
|
start Coord
|
|
end Coord
|
|
supportCount int
|
|
supports map[int]*Brick
|
|
}
|
|
|
|
func (a *Brick) CmpHeight(b *Brick) int {
|
|
return min(a.start.height, a.end.height) - min(b.start.height, b.end.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]},
|
|
supportCount: 0,
|
|
supports: map[int]*Brick{},
|
|
}
|
|
|
|
bricks = append(bricks, &newBrick)
|
|
ID++
|
|
}
|
|
|
|
return bricks
|
|
}
|
|
|
|
/*
|
|
func place(brick *Brick, tower [][][]int, heightMatrix [][]int, bricks map[int]*Brick) {
|
|
if brick.start.height != brick.end.height {
|
|
// standup brick
|
|
brickSize := brick.end.height - brick.start.height + 1
|
|
|
|
// insert brick in tower starting at the correct height
|
|
heightStart := heightMatrix[brick.start.row][brick.start.col]
|
|
for h := heightStart; h < heightStart+brickSize; h++ {
|
|
tower[h][brick.start.row][brick.start.col] = brick.ID
|
|
}
|
|
|
|
if heightStart > 0 {
|
|
// find the brick below
|
|
belowID := tower[heightStart-1][brick.start.row][brick.start.col]
|
|
|
|
belowBrick := bricks[belowID]
|
|
|
|
// indicate to the below brick that it supports the current brick
|
|
belowBrick.supports[brick.ID] = brick
|
|
|
|
// increase the number of bricks supporting the current brick
|
|
brick.supportCount++
|
|
}
|
|
|
|
// set new height for (row,col)
|
|
heightMatrix[brick.start.row][brick.start.col] = heightStart + brickSize
|
|
|
|
return
|
|
}
|
|
|
|
if brick.start.row != brick.end.row {
|
|
// vertical line
|
|
|
|
// find the height at which the brick will land
|
|
heightStart := 0
|
|
for r := brick.start.row; r <= brick.end.row; r++ {
|
|
if heightMatrix[r][brick.start.col] > heightStart {
|
|
heightStart = heightMatrix[r][brick.start.col]
|
|
}
|
|
}
|
|
|
|
// insert brick in tower starting at the correct height
|
|
belowID := 0
|
|
for r := brick.start.row; r <= brick.end.row; r++ {
|
|
tower[heightStart][r][brick.start.col] = brick.ID
|
|
|
|
if heightStart > 0 {
|
|
// find the brick below
|
|
if tower[heightStart-1][r][brick.start.col] != belowID {
|
|
belowID = tower[heightStart-1][r][brick.start.col]
|
|
|
|
if belowID > 0 {
|
|
belowBrick := bricks[belowID]
|
|
|
|
// indicate to the below brick that it supports the current brick
|
|
belowBrick.supports[brick.ID] = brick
|
|
|
|
// increase the number of bricks supporting the current brick
|
|
brick.supportCount++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// set new height for (row,col)
|
|
for r := brick.start.row; r <= brick.end.row; r++ {
|
|
heightMatrix[r][brick.start.col] = heightStart + 1
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if brick.start.col != brick.end.col {
|
|
// horizontal line
|
|
|
|
// find the height at which the brick will land
|
|
heightStart := 0
|
|
for c := brick.start.col; c <= brick.end.col; c++ {
|
|
if heightMatrix[brick.start.row][c] > heightStart {
|
|
heightStart = heightMatrix[brick.start.row][c]
|
|
}
|
|
}
|
|
|
|
// insert brick in tower starting at the correct height
|
|
belowID := 0
|
|
for c := brick.start.col; c <= brick.end.col; c++ {
|
|
tower[heightStart][brick.start.row][c] = brick.ID
|
|
|
|
if heightStart > 0 {
|
|
// find the brick below
|
|
if tower[heightStart-1][brick.start.row][c] != belowID {
|
|
belowID = tower[heightStart-1][brick.start.row][c]
|
|
|
|
if belowID > 0 {
|
|
belowBrick := bricks[belowID]
|
|
|
|
// indicate to the below brick that it supports the current brick
|
|
belowBrick.supports[brick.ID] = brick
|
|
|
|
// increase the number of bricks supporting the current brick
|
|
brick.supportCount++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// set new height for (row,col)
|
|
for c := brick.start.col; c <= brick.end.col; c++ {
|
|
heightMatrix[brick.start.row][c] = heightStart + 1
|
|
}
|
|
|
|
return
|
|
}
|
|
}
|
|
|
|
func Part1(lines []string) (int, []*Brick) {
|
|
heightMatrix := make([][]int, BASE_MATRIX_SIZE)
|
|
|
|
for r := range heightMatrix {
|
|
heightMatrix[r] = make([]int, BASE_MATRIX_SIZE)
|
|
}
|
|
|
|
orderedBricks := parseBricks(lines)
|
|
|
|
maxHeight := 0
|
|
for _, b := range orderedBricks {
|
|
if b.start.height > b.end.height {
|
|
log.Fatalln("Bug with ", b)
|
|
}
|
|
|
|
maxHeight += b.end.height
|
|
}
|
|
|
|
slices.SortFunc(orderedBricks, func(a, b *Brick) int { return a.CmpHeight(b) })
|
|
|
|
tower := make([][][]int, maxHeight)
|
|
for h := range tower {
|
|
tower[h] = make([][]int, BASE_MATRIX_SIZE)
|
|
for r := range tower[h] {
|
|
tower[h][r] = make([]int, BASE_MATRIX_SIZE)
|
|
}
|
|
}
|
|
|
|
bricks := map[int]*Brick{}
|
|
for _, brick := range orderedBricks {
|
|
bricks[brick.ID] = brick
|
|
}
|
|
|
|
for _, brick := range orderedBricks {
|
|
fmt.Println("Placing brick ", brick.ID)
|
|
place(brick, tower, heightMatrix, bricks)
|
|
}
|
|
|
|
count := 0
|
|
for _, brick := range bricks {
|
|
canDisintegrate := true
|
|
|
|
for _, supportedBrick := range brick.supports {
|
|
if supportedBrick.supportCount == 0 {
|
|
log.Fatalln("Bug ", brick)
|
|
}
|
|
if supportedBrick.supportCount == 1 {
|
|
canDisintegrate = false
|
|
}
|
|
}
|
|
|
|
if canDisintegrate {
|
|
fmt.Println(brick.ID, string('A'+byte(brick.ID-1)))
|
|
count++
|
|
}
|
|
}
|
|
|
|
return count, orderedBricks
|
|
}
|
|
*/
|
|
|
|
func dropBrick(brick *Brick, heights [][]int, highest [][]*Brick, bricks map[int]*Brick) {
|
|
if brick.start.height != brick.end.height {
|
|
// standup brick
|
|
brickSize := brick.end.height - brick.start.height + 1
|
|
height := heights[brick.start.row][brick.start.col]
|
|
|
|
if height > 0 {
|
|
// find the brick below
|
|
belowBrick := highest[brick.start.row][brick.start.col]
|
|
|
|
// indicate to the below brick that it supports the current brick
|
|
belowBrick.supports[brick.ID] = brick
|
|
|
|
// increase the number of bricks supporting the current brick
|
|
brick.supportCount++
|
|
}
|
|
|
|
// set new height and the new highest brick for (row,col)
|
|
heights[brick.start.row][brick.start.col] = height + brickSize
|
|
highest[brick.start.row][brick.start.col] = brick
|
|
|
|
return
|
|
}
|
|
|
|
if brick.start.row != brick.end.row {
|
|
// vertical line
|
|
|
|
// find the height at which the brick will land
|
|
height := 0
|
|
for r := brick.start.row; r <= brick.end.row; r++ {
|
|
if heights[r][brick.start.col] > height {
|
|
height = heights[r][brick.start.col]
|
|
}
|
|
}
|
|
|
|
// Drop brick at the correct height
|
|
var belowBrick *Brick
|
|
for r := brick.start.row; r <= brick.end.row; r++ {
|
|
// the brick can be supported by multiple other bricks, with spaces in between
|
|
if height > 0 && heights[r][brick.start.col] == height {
|
|
// find the brick below, but do not count it as a support multiple times
|
|
if highest[r][brick.start.col] != belowBrick {
|
|
belowBrick = highest[r][brick.start.col]
|
|
|
|
// indicate to the below brick that it supports the current brick
|
|
belowBrick.supports[brick.ID] = brick
|
|
|
|
// increase the number of bricks supporting the current brick
|
|
brick.supportCount++
|
|
}
|
|
}
|
|
|
|
// set new height and the new highest brick for (row,col)
|
|
heights[r][brick.start.col] = height + 1
|
|
highest[r][brick.start.col] = brick
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if brick.start.col != brick.end.col {
|
|
// horizontal line
|
|
|
|
// find the height at which the brick will land
|
|
height := 0
|
|
for c := brick.start.col; c <= brick.end.col; c++ {
|
|
if heights[brick.start.row][c] > height {
|
|
height = heights[brick.start.row][c]
|
|
}
|
|
}
|
|
|
|
// Drop brick at the correct height
|
|
var belowBrick *Brick
|
|
for c := brick.start.col; c <= brick.end.col; c++ {
|
|
// the brick can be supported by multiple other bricks, with spaces in between
|
|
if height > 0 && heights[brick.start.row][c] == height {
|
|
// find the brick below, but do not count it as a support multiple times
|
|
if highest[brick.start.row][c] != belowBrick {
|
|
belowBrick = highest[brick.start.row][c]
|
|
|
|
// indicate to the below brick that it supports the current brick
|
|
belowBrick.supports[brick.ID] = brick
|
|
|
|
// increase the number of bricks supporting the current brick
|
|
brick.supportCount++
|
|
}
|
|
}
|
|
|
|
// set new height and the new highest brick for (row,col)
|
|
heights[brick.start.row][c] = height + 1
|
|
highest[brick.start.row][c] = brick
|
|
}
|
|
|
|
return
|
|
}
|
|
}
|
|
|
|
func Part1(lines []string) (int, []*Brick) {
|
|
heights := make([][]int, BASE_MATRIX_SIZE)
|
|
highest := make([][]*Brick, BASE_MATRIX_SIZE)
|
|
|
|
for r := range heights {
|
|
heights[r] = make([]int, BASE_MATRIX_SIZE)
|
|
highest[r] = make([]*Brick, BASE_MATRIX_SIZE)
|
|
}
|
|
|
|
orderedBricks := parseBricks(lines)
|
|
|
|
slices.SortFunc(orderedBricks, func(a, b *Brick) int { return a.CmpHeight(b) })
|
|
|
|
bricks := map[int]*Brick{}
|
|
for _, brick := range orderedBricks {
|
|
bricks[brick.ID] = brick
|
|
}
|
|
|
|
for _, brick := range orderedBricks {
|
|
fmt.Println("Placing brick ", brick.ID)
|
|
dropBrick(brick, heights, highest, bricks)
|
|
}
|
|
|
|
count := 0
|
|
for _, brick := range bricks {
|
|
canDisintegrate := true
|
|
|
|
for _, supportedBrick := range brick.supports {
|
|
if supportedBrick.supportCount == 0 {
|
|
log.Fatalln("Bug ", brick)
|
|
}
|
|
if supportedBrick.supportCount == 1 {
|
|
canDisintegrate = false
|
|
}
|
|
}
|
|
|
|
if canDisintegrate {
|
|
fmt.Println(brick.ID, string('A'+byte(brick.ID-1)))
|
|
count++
|
|
}
|
|
}
|
|
|
|
return count, orderedBricks
|
|
}
|
|
|
|
/*
|
|
type Brick struct {
|
|
x, y, z int
|
|
x2, y2, z2 int
|
|
}
|
|
|
|
func (b *Brick) String() string {
|
|
return fmt.Sprintf("%d,%d,%d~%d,%d,%d", b.x, b.y, b.z, b.x2, b.y2, b.z2)
|
|
}
|
|
|
|
func (b *Brick) WouldCollide(otherBrick *Brick) bool {
|
|
if b.z <= otherBrick.z2 && b.z2 >= otherBrick.z {
|
|
if b.y <= otherBrick.y2 && b.y2 >= otherBrick.y {
|
|
if b.x <= otherBrick.x2 && b.x2 >= otherBrick.x {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func stack(bricks []Brick) int {
|
|
moves := make(map[int]int)
|
|
for i := 0; i < len(bricks); i++ {
|
|
// While the brick isn't on the floor keep checking the levels below
|
|
for bricks[i].z > 1 {
|
|
newBrick := bricks[i]
|
|
newBrick.z--
|
|
newBrick.z2--
|
|
wouldCollide := false
|
|
// See if moving the brick down a Z level would hit another brick
|
|
for j := i - 1; j > -1; j-- {
|
|
if bricks[j].WouldCollide(&newBrick) {
|
|
wouldCollide = true
|
|
break
|
|
}
|
|
}
|
|
if wouldCollide {
|
|
break
|
|
}
|
|
moves[i]++
|
|
bricks[i] = newBrick
|
|
}
|
|
}
|
|
return len(moves)
|
|
}
|
|
|
|
func process(input []string) int {
|
|
|
|
// Build the bricks
|
|
var bricks []Brick
|
|
for _, row := range input {
|
|
items := strings.FieldsFunc(row, func(r rune) bool {
|
|
return r == ',' || r == '~'
|
|
})
|
|
brick := Brick{
|
|
x: func(item string) int { i, _ := strconv.Atoi(item); return i }(items[0]),
|
|
y: func(item string) int { i, _ := strconv.Atoi(item); return i }(items[1]),
|
|
z: func(item string) int { i, _ := strconv.Atoi(item); return i }(items[2]),
|
|
x2: func(item string) int { i, _ := strconv.Atoi(item); return i }(items[3]),
|
|
y2: func(item string) int { i, _ := strconv.Atoi(item); return i }(items[4]),
|
|
z2: func(item string) int { i, _ := strconv.Atoi(item); return i }(items[5]),
|
|
}
|
|
bricks = append(bricks, brick)
|
|
}
|
|
// Input isn't in Z order so sort
|
|
slices.SortFunc(bricks, func(a, b Brick) int {
|
|
return min(a.z, a.z2) - min(b.z, b.z2)
|
|
})
|
|
|
|
// Stack the bricks
|
|
stack(bricks)
|
|
|
|
// Now take each brick out, and see it if it was supporting anything
|
|
noneSupportingBricks := 0
|
|
for i := 0; i < len(bricks); i++ {
|
|
stackCopy := make([]Brick, len(bricks))
|
|
copy(stackCopy, bricks)
|
|
stackCopy = append(stackCopy[:i], stackCopy[i+1:]...)
|
|
changes := stack(stackCopy)
|
|
if changes == 0 {
|
|
noneSupportingBricks++
|
|
}
|
|
}
|
|
return noneSupportingBricks
|
|
}
|
|
*/
|