214 lines
5.7 KiB
Go
214 lines
5.7 KiB
Go
package day22
|
|
|
|
import (
|
|
// "fmt"
|
|
"cmp"
|
|
"log"
|
|
"slices"
|
|
// "time"
|
|
)
|
|
|
|
type Space struct {
|
|
MaxZ uint
|
|
SettledOnZ [][]*Block
|
|
MaxSettledOnXY map[XY]*Block
|
|
UnsettledByZ [][]*Block
|
|
}
|
|
|
|
func NewSpace(blocksByZ [][]*Block) Space {
|
|
return Space{
|
|
UnsettledByZ: blocksByZ,
|
|
MaxZ: uint(len(blocksByZ) - 1),
|
|
MaxSettledOnXY: make(map[XY]*Block),
|
|
SettledOnZ: make([][]*Block, len(blocksByZ)),
|
|
}
|
|
}
|
|
|
|
func (s *Space) AgainCountFreeBlocks() (result int) {
|
|
for _, row := range s.SettledOnZ {
|
|
for _, block := range row {
|
|
thisSupports := block.Supports
|
|
canDisintegrate := true
|
|
for _, blockThisSupports := range thisSupports {
|
|
if len(blockThisSupports.SupportedBy) == 1 {
|
|
// we cannot disintigrate this block
|
|
canDisintegrate = false
|
|
}
|
|
}
|
|
if canDisintegrate {
|
|
result += 1
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (s *Space) InitialCollectGoodToDisintegrate() (result []Block) {
|
|
allBlocks := make(map[*Block]any)
|
|
|
|
for _, row := range s.SettledOnZ {
|
|
for _, block := range row {
|
|
allBlocks[block] = struct{}{}
|
|
if len(block.SupportedBy) == 1 {
|
|
onlySupport := block.SupportedBy[0]
|
|
log.Printf("in block %+v. only support is %+v", block, onlySupport)
|
|
log.Printf("should be NOT OK to remove %+v", onlySupport)
|
|
delete(allBlocks, onlySupport)
|
|
}
|
|
}
|
|
}
|
|
|
|
for block := range allBlocks {
|
|
result = append(result, *block)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Space) CountFreeBlocks() (result int) {
|
|
return len(s.InitialCollectGoodToDisintegrate())
|
|
}
|
|
|
|
func (s *Space) ThirdTimeCollectGoodToDisintegrate() (blocks []Block) {
|
|
// for each block create a new space without it. try to settle and check if 0 moved
|
|
log.Println(">>>>> starting hardcode count <<<<<")
|
|
for rowNum, row := range s.SettledOnZ {
|
|
for blockNum, block := range row {
|
|
// log.Printf(">>> starting for block %+v (supports %+v)\n", block, block.Supports)
|
|
newUnsettled := slices.Clone(s.SettledOnZ)
|
|
for rowNum, row := range newUnsettled {
|
|
newUnsettled[rowNum] = slices.Clone(row)
|
|
}
|
|
newUnsettled[rowNum] = slices.Delete(newUnsettled[rowNum], blockNum, blockNum+1)
|
|
// and now copy the blocks
|
|
for rowNum, row := range newUnsettled {
|
|
for blockNum, block := range row {
|
|
newBlock := *block
|
|
newUnsettled[rowNum][blockNum] = &newBlock
|
|
}
|
|
}
|
|
|
|
newSpace := NewSpace(newUnsettled)
|
|
moved := newSpace.SettleAll()
|
|
if moved > 0 {
|
|
// log.Printf("for block %+v new space has %d moved\n\n", block, moved)
|
|
} else {
|
|
// log.Printf("for block %+v new space has %d moved\n\n", block, moved)
|
|
blocks = append(blocks, *block)
|
|
}
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Space) ThirdTimeCountFreeBlocks() (result int) {
|
|
return len(s.ThirdTimeCollectGoodToDisintegrate())
|
|
}
|
|
|
|
func (s *Space) CountChainReactoins() (result int) {
|
|
for rowNum, row := range s.SettledOnZ {
|
|
for blockNum, _ := range row {
|
|
newUnsettled := slices.Clone(s.SettledOnZ)
|
|
for rowNum, row := range newUnsettled {
|
|
newUnsettled[rowNum] = slices.Clone(row)
|
|
}
|
|
newUnsettled[rowNum] = slices.Delete(newUnsettled[rowNum], blockNum, blockNum+1)
|
|
// and now copy the blocks
|
|
for rowNum, row := range newUnsettled {
|
|
for blockNum, block := range row {
|
|
newBlock := *block
|
|
newUnsettled[rowNum][blockNum] = &newBlock
|
|
}
|
|
}
|
|
|
|
newSpace := NewSpace(newUnsettled)
|
|
moved := newSpace.SettleAll()
|
|
result += moved
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Space) SettleAll() (totalMoved int) {
|
|
for i := uint(1); i <= s.MaxZ; i++ {
|
|
movedAfterLayer := s.SettleZ(i)
|
|
totalMoved += movedAfterLayer
|
|
}
|
|
return
|
|
}
|
|
|
|
// settle all blocks in Z, remove Z from UnsettledByZ
|
|
func (s *Space) SettleZ(z uint) (totalMoved int) {
|
|
blocksToSettle := s.UnsettledByZ[int(z)]
|
|
|
|
for _, block := range blocksToSettle {
|
|
hasMoved := s.SettleBlock(block)
|
|
if hasMoved {
|
|
totalMoved += 1
|
|
}
|
|
}
|
|
|
|
s.UnsettledByZ[int(z)] = nil
|
|
return
|
|
}
|
|
|
|
// for the block:
|
|
// check all XY in MaxSettledOnXY
|
|
// if there are any settled blocks on these XY, find max of their Z
|
|
// for all blocks with that Z - add block to their 'supports'
|
|
// set Z for block to Z+1, settled to true
|
|
// add block as highest settled for all the XY
|
|
// add block to MaxSettledOnXY
|
|
func (s *Space) SettleBlock(block *Block) (hasMoved bool) {
|
|
initialZ := block.Z
|
|
underZMax := uint(0)
|
|
underZBlocks := make([]*Block, 0)
|
|
// fmt.Printf("\n>> for block %s\n", block)
|
|
for _, xy := range block.getXY() {
|
|
underBlock, found := s.MaxSettledOnXY[xy]
|
|
// if block.NameNum
|
|
if found {
|
|
underBlockMaxZ := underBlock.Z + underBlock.ZHeight
|
|
// action := " 'skipping' "
|
|
if underBlockMaxZ > underZMax {
|
|
underZBlocks = []*Block{underBlock}
|
|
underZMax = underBlockMaxZ
|
|
// action = " 'overriding' "
|
|
} else if underBlockMaxZ == underZMax {
|
|
underZBlocks = append(underZBlocks, underBlock)
|
|
// action = " 'adding' "
|
|
}
|
|
// fmt.Printf("checking under coord %+v. found under %+v. (%s) with %d ; maxZ %d\n directly supporting blocks are %+v\n",
|
|
// xy, underBlock, action, underBlockMaxZ, underZMax, underZBlocks)
|
|
} else {
|
|
// fmt.Printf("checking under coord %+v. nothing under\n", xy)
|
|
}
|
|
s.MaxSettledOnXY[xy] = block
|
|
}
|
|
|
|
for _, settledUnderblock := range underZBlocks {
|
|
settledUnderblock.Supports = append(settledUnderblock.Supports, block)
|
|
block.SupportedBy = append(block.SupportedBy, settledUnderblock)
|
|
}
|
|
|
|
block.Z = underZMax + 1
|
|
block.IsSettled = true
|
|
|
|
s.SettledOnZ[block.Z] = append(s.SettledOnZ[block.Z], block)
|
|
// fmt.Printf(">> after settring block %s. supported by %+v\n\n", block, block.SupportedBy)
|
|
|
|
slices.SortFunc(block.SupportedBy, func(a *Block, b *Block) int {
|
|
return cmp.Compare(a.NameNum, b.NameNum)
|
|
})
|
|
block.SupportedBy = slices.Compact(block.SupportedBy)
|
|
|
|
// time.Sleep(500 * time.Millisecond)
|
|
hasMoved = initialZ != block.Z
|
|
// if hasMoved {
|
|
// log.Printf("block %+v moved from %d to %d", block, initialZ, block.Z)
|
|
// }
|
|
return
|
|
}
|