Completed part 1 of Day 22 puzzle (with a lot of pain, and I still don't understand why the 1st algo does not work

main
oabrivard 2 years ago
parent 62f9bf31f5
commit e30e6e8d0a

@ -0,0 +1,462 @@
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
}
*/

@ -0,0 +1,186 @@
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
}

@ -0,0 +1,37 @@
package day22
import (
"testing"
"gitea.paas.celticinfo.fr/oabrivard/aoc2023/utils"
)
func TestPart1(t *testing.T) {
lines := []string{
"1,0,1~1,2,1", // 1
"0,0,2~2,0,2", // 2
"0,2,3~2,2,3", // 3
"0,0,4~0,2,4", // 4
"2,0,5~2,2,5", // 5
"0,1,6~2,1,6", // 6
"1,1,8~1,1,9", // 7
}
result, _ := Part1(lines)
//result := process(lines)
if result != 5 {
t.Fatalf("expected 5, got %d", result)
}
}
func TestPart1WithInput(t *testing.T) {
lines := utils.ReadLines("input.txt")
result, _ := Part1(lines)
//result := process(lines)
if result != 428 {
t.Fatalf("expected 428, got %d", result)
}
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save