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

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
}
*/