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