package day22 import ( // "fmt" "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) CountFreeBlocks() (result int) { 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) } } } result = len(allBlocks) return } func (s *Space) ThirdTimeCountFreeBlocks() (result int) { // 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) result += 1 } } } 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) // 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 }