105 lines
		
	
	
		
			2.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			105 lines
		
	
	
		
			2.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package day22
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"log"
 | |
| 	"os"
 | |
| 	"regexp"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"github.com/deckarep/golang-set/v2"
 | |
| )
 | |
| 
 | |
| type XY struct {
 | |
| 	X, Y uint
 | |
| }
 | |
| 
 | |
| type Block struct {
 | |
| 	NameNum     int
 | |
| 	XMin, XMax  uint
 | |
| 	YMin, YMax  uint
 | |
| 	Z           uint
 | |
| 	IsSettled   bool
 | |
| 	ZHeight     uint
 | |
| 	Supports    mapset.Set[*Block]
 | |
| 	SupportedBy mapset.Set[*Block]
 | |
| }
 | |
| 
 | |
| func (b *Block) String() string {
 | |
| 	return fmt.Sprintf("[Block %d - x:%d-%d, y:%d-%d, z:%d, h:%d, isSettled %t]",
 | |
| 		b.NameNum, b.XMin, b.XMax, b.YMin, b.YMax, b.Z, b.ZHeight, b.IsSettled)
 | |
| }
 | |
| 
 | |
| func AtoIOrPanic(a string) int {
 | |
| 	n, err := strconv.Atoi(a)
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	return n
 | |
| }
 | |
| 
 | |
| func ReadBlock(line string, num int) (b Block) {
 | |
| 	b.NameNum = num
 | |
| 	re := regexp.MustCompile(`(\d+),(\d+),(\d+)~(\d+),(\d+),(\d+)`)
 | |
| 	matches := re.FindStringSubmatch(line)
 | |
| 
 | |
| 	x1, x2 := AtoIOrPanic(matches[1]), AtoIOrPanic(matches[4])
 | |
| 	y1, y2 := AtoIOrPanic(matches[2]), AtoIOrPanic(matches[5])
 | |
| 	z1, z2 := AtoIOrPanic(matches[3]), AtoIOrPanic(matches[6])
 | |
| 
 | |
| 	b.XMax = uint(max(x1, x2))
 | |
| 	b.XMin = uint(min(x1, x2))
 | |
| 	b.YMax = uint(max(y1, y2))
 | |
| 	b.YMin = uint(min(y1, y2))
 | |
| 	b.Z = uint(min(z1, z2))
 | |
| 	b.ZHeight = uint(max(z1, z2)) - b.Z
 | |
| 
 | |
| 	b.Supports = mapset.NewSet[*Block]()
 | |
| 	b.SupportedBy = mapset.NewSet[*Block]()
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func (b *Block) getXY() (coords []XY) {
 | |
| 	for x := b.XMin; x <= b.XMax; x++ {
 | |
| 		for y := b.YMin; y <= b.YMax; y++ {
 | |
| 			coords = append(coords, XY{X: x, Y: y})
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func ReadBlockFile(filename string) (blocks []*Block) {
 | |
| 	bytes, err := os.ReadFile(filename)
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 
 | |
| 	text := strings.TrimSpace(string(bytes))
 | |
| 	for i, line := range strings.Split(text, "\n") {
 | |
| 		block := ReadBlock(line, i)
 | |
| 		blocks = append(blocks, &block)
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func BlocksByZ(blocks []*Block) [][]*Block {
 | |
| 	maxZ := uint(0)
 | |
| 	for _, block := range blocks {
 | |
| 		if block.Z > maxZ {
 | |
| 			maxZ = block.Z
 | |
| 		}
 | |
| 	}
 | |
| 	log.Print("found max z: ", maxZ)
 | |
| 
 | |
| 	result := make([][]*Block, maxZ+1)
 | |
| 
 | |
| 	for _, block := range blocks {
 | |
| 		result[block.Z] = append(result[block.Z], block)
 | |
| 	}
 | |
| 
 | |
| 	return result
 | |
| }
 |