Compare commits

...

14 Commits

Author SHA1 Message Date
efim 29528f23ac day22: bug: not using Set - duplicate supports 2023-12-22 12:45:51 +00:00
efim 8be2fa3844 day22, part2 2023-12-22 12:21:49 +00:00
efim 45d03e5ab3 day22, part1, struggle 2023-12-22 12:19:12 +00:00
efim 2b3c7f4ca6 day22, example 2023-12-22 09:59:41 +00:00
efim 3ede691333 day22: initial block setting 2023-12-22 09:24:43 +00:00
efim 7b34b52e5e day22, simple block code 2023-12-22 08:44:10 +00:00
efim 99c2269df8 day21, part2, did not like 2023-12-21 13:44:01 +00:00
efim b10a6250b1 day21: wrong answer AND slow. need to scrap 2023-12-21 12:36:07 +00:00
efim 4cb35dca33 day21: saturation logic, but removing points to early 2023-12-21 12:13:01 +00:00
efim 840773fd16 day21: factor for same coord struct 2023-12-21 10:19:37 +00:00
efim 9a22efd4b3 day21: choking on example 2023-12-21 09:27:41 +00:00
efim f5ea9e725e day21: yuck 2023-12-21 08:39:27 +00:00
efim 6a7378c265 day1, part1 2023-12-21 08:30:44 +00:00
efim 5b0f1ab750 day21: example 2023-12-21 08:29:08 +00:00
15 changed files with 982 additions and 3 deletions

11
day21/example Normal file
View File

@ -0,0 +1,11 @@
...........
.....###.#.
.###.##..#.
..#.#...#..
....#.#....
.##..S####.
.##..#...#.
.......##..
.##.#.####.
.##..##.##.
...........

11
day21/example1 Normal file
View File

@ -0,0 +1,11 @@
...........
...........
...........
...........
...........
.....S.....
...........
...........
...........
...........
...........

105
day21/notes.org Normal file
View File

@ -0,0 +1,105 @@
#+title: Notes
* part 1
so we aren't looking for minimal distance.
but all plots which are end to any path of length 'steps left'
so, i have to follow all possible paths to the end?
or. length of 6 and all even - because i could be doing <- ->
but i could be doing loop around that would increase path len by odd number
let's just make direct recursive thing.
create set of all reachable by n,
* oh, the part 2.
i suppose this 'infinite' garden could be managed with my 'neighbors' work with 'out of field'
fairly easy
but what about sizes of the maps? are we releasing maps of previous iterations?
maybe if i directly pass references to prev and current,
and manually set 'prev' to target new it will be collected?
and then elements after these steps <em>26501365</em> would fit into memory?
** i guess maybe it would help if i had 'fully saturated' field
as my minimal 'skipping' thing
** so. store FieldCoord(fieldRow, fieldCol) for fields which were fully saturated at current step.
filter out neighbors, no need to enter fully saturated fields
when counting
on odd - around the S, on even - with S
but the neighboring fields would potentially (likely?) be in different phases
but i guess they are necessarily in different phases?
or. if width odd - necessarily
if width even - then what?
then S is not in the center
my input is 131 chars of width.
so neighboring are necessarily of different phase.
could compute phase of (0,0)
and adjust from that
** TODO remake 'ReachableBySteps' into 'CountReachableBySteps' returning int
** TODO make it take 'isInitialCountOdd' - to know phase of {0,0} field
current phase can be determined by initial phase and current N
if initial count is odd, and now it's odd number, we made even iterations, so (0,0) is in even state
if initial count is even, and now it's even number, we made even iterations, so (0,0) is in even state
** DONE make neighbors take set of saturated fields
and not produce points on those fields
** DONE for field calculate what would be amount of points in each phase
...........
.....###.#.
.###.##..#.
..#.#...#..
....#.#....
.##..S####.
.##..#...#.
.......##..
.##.#.####.
.##..##.##.
...........
*** getting 39 and 42
let's check
42 is even?
*** hmmm
EOEOEOEOEOE
OEOEO###O#O
E###E##OE#E
OE#E#EOE#EO
EOEO#O#OEOE
O##EOE####O
E##OE#EOE#E
OEOEOEO##EO
E##O#O####E
O##EO##E##O
EOEOEOEOEOE
*** yes, sounds good
** CANCELLED after getting all new points. get coords of all fields we're working on.
( there already should be no points in saturated fields )
for each such field, check if it is saturated.
- can be done by comparing the phase with amount of points on saturated
if field saturated - add the coord into set
and remove all the points
** CANCELLED on the last step, when n is 0
return len(startingAt) + (all saturated fields) * (amount of elems in their phase)
** calculating points in even 7356 and odd 7321 phases
* so need to scrap things and do a more analytics approach.
no blocks on horizontal & vertical from (S)
meaning diamond expands to left & right well
* 26501365 = 202300 * 131 + 65 where 131 is the dimension of the grid
* if there is a formula A*i^2 + B*i + C = D
where i is full iteration
* for initial steps :
2023/12/21 13:25:23 after steps 65. full iter 0. got count 3701
2023/12/21 13:25:24 after steps 196. full iter 1. got count 33108
2023/12/21 13:25:27 after steps 327. full iter 2. got count 91853
2023/12/21 13:25:42 after steps 458. full iter 3. got count 179936
* https://www.dcode.fr/newton-interpolating-polynomial
14669x^2 + 14738*x+3701

211
day21/stepCounter.go Normal file
View File

@ -0,0 +1,211 @@
package day21
import (
"fmt"
"log"
"os"
"strings"
)
func Run() int {
fmt.Print("hello day21")
filename := "day21/input"
field := ReadField(filename)
log.Print(field)
// for i := 6; i <= 10; i++ {
// reachableBySteps := field.ReachableBySteps(i, map[Coord]any{
// Coord{Row: field.RowStart, Col: field.ColStart}: struct{}{},
// })
// log.Print("reachable after steps : ", i, len(reachableBySteps))
// field.PrintCoord(reachableBySteps, 1)
// }
// initialSolutions := make(map[int]int)
// for fullIter := 0; fullIter < 4; fullIter++ {
// steps := 65 + fullIter * 131
// reachableBySteps := field.ReachableBySteps(steps, map[FieldPoint]any{
// FieldPoint{
// InField: Coord{Row: field.RowStart, Col: field.ColStart},
// }: struct{}{},
// })
// log.Printf("after steps %d. full iter %d. got count %d", steps, fullIter, len(reachableBySteps))
// initialSolutions[fullIter] = len(reachableBySteps)
// }
log.Println("will try to use the values to get coeff of Ax^2 + Bx + C = 0")
log.Println("then solve for x == 202300")
// f(x) = 14714x^2 + 14603x + 3791
// no.
// 14669x^2 + 14738*x+3701
x := 202300
result := 14669*x*x + 14738*x+3701
return result
}
// let's do dijkstra?
// i would need lots of space for edges?
// let's use a map with minimal distances?
// OR. just breath first traversal
type Field struct {
RowStart, ColStart int
symbols [][]rune
}
type Coord struct {
Row, Col int
}
type FieldPoint struct {
InField Coord
MetaField Coord
}
func (f Field) ReachableBySteps(n int, startingAt map[FieldPoint]any) map[FieldPoint]any {
if n%100 == 0 {
log.Println("going step: ", n)
}
if n == 0 {
return startingAt
}
// else collect directly available
oneStepExpanded := make(map[FieldPoint]any)
for cur := range startingAt {
for _, neighbor := range f.Neighbors(cur) {
oneStepExpanded[neighbor] = struct{}{}
}
}
// if n < 4 {
// log.Print("reachable after steps : ", n, len(oneStepExpanded))
// f.PrintCoord(oneStepExpanded, 5)
// }
return f.ReachableBySteps(n-1, oneStepExpanded)
}
func (f Field) Neighbors(c FieldPoint) (resut []FieldPoint) {
closeCoords := []FieldPoint{
{InField: Coord{Row: c.InField.Row + 1, Col: c.InField.Col}, MetaField: c.MetaField},
{InField: Coord{Row: c.InField.Row - 1, Col: c.InField.Col}, MetaField: c.MetaField},
{InField: Coord{Row: c.InField.Row, Col: c.InField.Col + 1}, MetaField: c.MetaField},
{InField: Coord{Row: c.InField.Row, Col: c.InField.Col - 1}, MetaField: c.MetaField},
}
for i, close := range closeCoords {
height := len(f.symbols)
width := len(f.symbols[0])
if close.InField.Row == height {
close.InField.Row = 0
close.MetaField.Row += 1
}
if close.InField.Row == -1 {
close.InField.Row = height - 1
close.MetaField.Row -= 1
}
if close.InField.Col == width {
close.InField.Col = 0
close.MetaField.Col += 1
}
if close.InField.Col == -1 {
// log.Printf("moving COL to lefter field from %d to %d", close.Col, width-1)
close.InField.Col = width - 1
close.MetaField.Col -= 1
}
closeCoords[i] = close
// but this is not it. i need to store the XX and YY
// so that points in other 'fields' would count separately. yuk
}
for _, close := range closeCoords {
if f.ValidCoord(close.InField.Row, close.InField.Col) {
symb := f.symbols[close.InField.Row][close.InField.Col]
if symb == '.' || symb == 'S' {
resut = append(resut, close)
}
}
}
// log.Print("getting neighbors for ", c, resut)
return
}
func (f Field) ValidCoord(row, col int) bool {
// log.Print("check valid ", row, col, row >= 0 && row < len(f.symbols) && col >= 0 && col < len(f.symbols[0]))
valid := row >= 0 && row < len(f.symbols) && col >= 0 && col < len(f.symbols[0])
if !valid {
panic(fmt.Sprint("getting invalid coord: ", row, col))
}
return valid
}
func (f Field) String() (result string) {
result += "\n"
for _, line := range f.symbols {
result += string(line)
result += "\n"
}
return
}
func ReadField(filename string) (result Field) {
bytes, err := os.ReadFile(filename)
if err != nil {
panic(err)
}
text := strings.TrimSpace(string(bytes))
lines := strings.Split(text, "\n")
rows := make([][]rune, len(lines))
for rowNum, line := range lines {
rows[rowNum] = []rune(line)
for colNum, symb := range line {
if symb == 'S' {
result.RowStart = rowNum
result.ColStart = colNum
}
}
}
result.symbols = rows
return
}
func (f Field) PrintCoord(coords map[FieldPoint]any, expandByField int) {
for fieldRow := -expandByField; fieldRow <= expandByField; fieldRow++ {
lines := make([]string, len(f.symbols))
for fieldCol := -expandByField; fieldCol <= expandByField; fieldCol++ {
for rowNum, row := range f.symbols {
for colNum, col := range row {
_, marked := coords[FieldPoint{InField: Coord{Row: rowNum, Col: colNum},
MetaField: Coord{Row: fieldRow, Col: fieldCol}}]
if marked {
lines[rowNum] += "O"
} else {
lines[rowNum] += string(col)
}
}
}
}
for _, line := range lines {
fmt.Println(line)
}
}
return
}

100
day22/block.go Normal file
View File

@ -0,0 +1,100 @@
package day22
import (
"fmt"
"log"
"os"
"regexp"
"strconv"
"strings"
)
type XY struct {
X, Y uint
}
type Block struct {
NameNum int
XMin, XMax uint
YMin, YMax uint
Z uint
IsSettled bool
ZHeight uint
Supports []*Block
SupportedBy []*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
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
}

37
day22/block_test.go Normal file
View File

@ -0,0 +1,37 @@
package day22
import (
"strings"
"testing"
)
func TestReadBlock(t *testing.T) {
lines := `1,0,1~1,2,1
0,0,2~2,0,2
0,2,3~2,2,3
0,0,4~0,2,4
2,0,5~2,2,5
0,1,6~2,1,6
1,1,8~1,1,9`
for _, line := range strings.Split(lines, "\n") {
b := ReadBlock(line, 0)
t.Logf("read %s into block %+v", line, b)
t.Logf("XY coords for %+v are : %+v", b, b.getXY())
}
}
func TestReadFile(t *testing.T) {
filename := "example"
// filename := "input"
blocks := ReadBlockFile(filename)
byZ := BlocksByZ(blocks)
for z, zBlocks := range byZ {
zBlocksLine := ""
for _, block := range zBlocks {
zBlocksLine += block.String()
}
t.Logf("for level %d blocks %+v", z, zBlocksLine)
}
}

7
day22/example Normal file
View File

@ -0,0 +1,7 @@
1,0,1~1,2,1
0,0,2~2,0,2
0,2,3~2,2,3
0,0,4~0,2,4
2,0,5~2,2,5
0,1,6~2,1,6
1,1,8~1,1,9

74
day22/notes.org Normal file
View File

@ -0,0 +1,74 @@
#+title: Notes
* ok. let's try this.
i'd want to have block type
with function to get it's XY coords
i'd want to settle blocks first.
but if i store enough data, for example block.supports slice i'll be able to anser first task.
(settledOnZ) i would want [][]*Block per level from 0 to up. with references to blocks that settled on that level
(maxSettledXY) and for going from 0 up i'll want XY of the top block settled with it's level. i guess i could store settled level in the block as well
then for settling blocks, i will need (sorted map if data is sparse?) go from 0 up,
order of processing for blocks on same z level is not important.
for each block get it's XY, check maxSettledXY if there's a block check it's Z,
for all block XY coords, find maximal settled Z, and refs to all blocks that are directly under with that same Z.
for the block set settledZ to Z+1, and for all those blocks add the block to 'supports'
add block to settledOnZ[Z+1]
for the second part, i can scan all the blocks, don't even need the settledOnZ, just check if it's 'supports' is empty
** DONE block type
store z, and have 'settledZ', maybe with default -1?
** DONE coords type, func to get XY coords of the block
** DONE now i guess what? do i want a sorted map? or just map from height to blocks on that hight?
let's read file, and calc max height present?
i suppose funciton to read file could also be initially entered via test, right?
** DONE now go through the z levels, block by block, doing setting.
i suppose i could organize setting methods around Space?
it will store (settledOnZ) and (maxSettledOnXY)
** DONE [#A] when i settle single block. the maxSettledOnXY - should use (z + height)
** i can already imagine secon part? what is the most volume that can be disintegrated? or what? most volume is just all
* part 1, wrong answer.
i guess try to go, setting the input? block after block and try to check the calculations?
what i want to check:
how maxSettledOnXY works, how linking works. maybe i'll find a problem in few steps =C
** can't see anything just glancing around.
maybe then trying to pick a block and track what's under it?
* ok. let's try to brute force?
for each block, remove it?
create new space and try to settle it
** this is shit. why blocks move up?
2023/12/22 12:12:24 >>> starting for block [Block 1 - x:0-2, y:0-0, z:1, h:0, isSettled true] (supports [[Block 3 - x:0-0, y:0-2, z:3, h:0, isSettled true] [Block 4 - x:2-2, y:0-2, z:3, h:0, isSettled true] [Block 3 - x:0-0, y:0-2, z:2, h:0, isSettled true] [Block 4 - x:2-2, y:0-2, z:2, h:0, isSettled true]])
2023/12/22 12:12:24 block [Block 2 - x:0-2, y:2-2, z:2, h:0, isSettled true] moved from 1 to 2
2023/12/22 12:12:24 block [Block 3 - x:0-0, y:0-2, z:3, h:0, isSettled true] moved from 2 to 3
2023/12/22 12:12:24 block [Block 4 - x:2-2, y:0-2, z:3, h:0, isSettled true] moved from 2 to 3
2023/12/22 12:12:24 block [Block 5 - x:0-2, y:1-1, z:4, h:0, isSettled true] moved from 3 to 4
2023/12/22 12:12:24 block [Block 6 - x:1-1, y:1-1, z:5, h:1, isSettled true] moved from 4 to 5
2023/12/22 12:12:24 for block [Block 1 - x:0-2, y:0-0, z:1, h:0, isSettled true] new space has 5 moved
* ok. brute force with copying slices worked.
now i want to debug.
for each brick, when there is 0 falling, i want to check what are it's surroundings
** my initial was : 567
** checking example of badly determined:
>> for block [Block 291 - x:6-8, y:7-7, z:75, h:0, isSettled false]
checking under coord {X:6 Y:7}. found under [Block 698 - x:6-8, y:7-7, z:35, h:0, isSettled true]. ( 'overriding' ) with 35 ; maxZ 35
directly supporting blocks are [[Block 698 - x:6-8, y:7-7, z:35, h:0, isSettled true]]
checking under coord {X:7 Y:7}. found under [Block 698 - x:6-8, y:7-7, z:35, h:0, isSettled true]. ( 'adding' ) with 35 ; maxZ 35
directly supporting blocks are [[Block 698 - x:6-8, y:7-7, z:35, h:0, isSettled true] [Block 698 - x:6-8, y:7-7, z:35, h:0, isSettled true]]
checking under coord {X:8 Y:7}. found under [Block 698 - x:6-8, y:7-7, z:35, h:0, isSettled true]. ( 'adding' ) with 35 ; maxZ 35
directly supporting blocks are [[Block 698 - x:6-8, y:7-7, z:35, h:0, isSettled true] [Block 698 - x:6-8, y:7-7, z:35, h:0, isSettled true] [Block 698 - x:6-8, y:7-7, z:35, h:0, isSettled true]]
>> after settring block [Block 291 - x:6-8, y:7-7, z:36, h:0, isSettled true]. supported by [[Block 698 - x:6-8, y:7-7, z:35, h:0, isSettled true] [Block 698 - x:6-8, y:7-7, z:35, h:0, isSettled true] [Block 698 - x:6-8, y:7-7, z:35, h:0, isSettled true]]
** ouch. duplicates in slices. because there's no easy set thingy
not doing this was my bug.
#+begin_src go
slices.SortFunc(block.SupportedBy, func(a *Block, b *Block) int {
return cmp.Compare(a.NameNum, b.NameNum)
})
block.SupportedBy = slices.Compact(block.SupportedBy)
#+end_src

38
day22/printingSpace.go Normal file
View File

@ -0,0 +1,38 @@
package day22
import (
"math"
"github.com/tidwall/pinhole"
)
func TestPinhole() {
p := pinhole.New()
p.DrawCube(-0.3, -0.3, -0.3, 0.3, 0.3, 0.3)
p.Rotate(math.Pi/3, math.Pi/2, 0)
p.SavePNG("cube.png", 500, 500, nil)
}
func PrintSpace(s Space, filename string) {
// pinhole is from -1 to 1. let's use from 0 to 1.
// so coord should be divided by max height, and let's hope that they are not too wide
rotation := []float64{math.Pi/3, math.Pi/6, 0}
p := pinhole.New()
p.DrawRect(-1, -1, 1, 1, 0)
for _, zLevel := range s.SettledOnZ {
for _, block := range zLevel {
p.DrawCube(float64(block.XMin) / float64(s.MaxZ),
float64(block.YMin) / float64(s.MaxZ),
float64(block.Z) / float64(s.MaxZ),
float64(block.XMax + 1) / float64(s.MaxZ),
float64(block.YMax + 1) / float64(s.MaxZ),
float64(block.Z + block.ZHeight + 1) / float64(s.MaxZ))
}
}
p.Rotate(rotation[0], rotation[1], rotation[2])
p.SavePNG(filename, 1920, 1080, nil)
}

18
day22/sandSlabs.go Normal file
View File

@ -0,0 +1,18 @@
package day22
import (
"fmt"
)
func Run() int {
fmt.Print("oi, hello day 22")
filename := "day22/example"
blocks := ReadBlockFile(filename)
byZ := BlocksByZ(blocks)
space := NewSpace(byZ)
space.SettleAll()
result := space.CountFreeBlocks()
return result
}

213
day22/space.go Normal file
View File

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

135
day22/space_test.go Normal file
View File

@ -0,0 +1,135 @@
package day22
import (
"slices"
"testing"
)
func TestSpaceSettleSingle(t *testing.T) {
filename := "example"
blocks := ReadBlockFile(filename)
byZ := BlocksByZ(blocks)
space := NewSpace(byZ)
t.Logf("read space %+v", space)
block := blocks[2]
t.Logf("block before setting %+v", block)
space.SettleBlock(block)
t.Logf("space after settings %+v:\n%+v", block, space)
}
func TestSpaceSettleSecondNearby(t *testing.T) {
filename := "example"
blocks := ReadBlockFile(filename)
byZ := BlocksByZ(blocks)
space := NewSpace(byZ)
t.Logf("read space %+v", space)
block1 := blocks[0]
block2 := blocks[3]
t.Logf("block 1 before setting %+v", block1)
space.SettleBlock(block1)
t.Logf("space after settling block 1 %+v", space)
t.Logf("block 2 before setting %+v", block2)
space.SettleBlock(block2)
t.Logf("space after settling block 2 %+v", space)
t.Logf("space after settling %+v", space)
}
func TestSpaceSettleThirdOnTopFirst(t *testing.T) {
filename := "example"
blocks := ReadBlockFile(filename)
byZ := BlocksByZ(blocks)
space := NewSpace(byZ)
t.Logf("read space %+v", space)
block1 := blocks[0]
block2 := blocks[3]
block3 := blocks[2] // should overlap X & Y coords of block 1
t.Logf("block 1 before setting %+v", block1)
space.SettleBlock(block1)
t.Logf("space after settling block 1 %+v", space)
t.Logf("block 2 before setting %+v", block2)
space.SettleBlock(block2)
t.Logf("space after settling block 2 %+v", space)
t.Logf("block 3 before setting %+v", block3)
space.SettleBlock(block3)
t.Logf("space after settling block 3 %+v", space)
t.Logf("space after settling %+v", space)
t.Logf("blocks 1 & 3 should support it: %+v , %+v", block1.Supports, block2.Supports)
// because block 3 is 0-2, 2-2
// and that overlaps 1-1, 0-2 AND 0-0, 0-2
t.Logf("other blocks should not supt %+v", block3.Supports)
}
func TestSpaceExampleSettleAll(t *testing.T) {
filename := "example"
blocks := ReadBlockFile(filename)
byZ := BlocksByZ(blocks)
space := NewSpace(byZ)
space.SettleAll()
t.Logf("settled space %+v", space)
// maybe i can check via console.
i := 2
t.Logf("level %d is : %+v", i, space.SettledOnZ[i])
// it looks ok for the example.
// let's hope?
t.Logf("for example, free blocks amount is %d", space.CountFreeBlocks())
// oh, i need 'supported'?
// how do i need to count the task question
// i guess we can start with set of all blocks, then?
// run over all, if some block is only supported by some underBlock - remove that underblock
}
func TestPinholeStart(t *testing.T) {
TestPinhole()
}
func TestExampleSpacePrint(t *testing.T) {
filename := "example"
blocks := ReadBlockFile(filename)
byZ := BlocksByZ(blocks)
space := NewSpace(byZ)
// PrintSpace(space, "before-settping.png")
space.SettleAll()
PrintSpace(space, "after-settping.png")
}
func TestCompareInitialAndBruteforce(t *testing.T) {
filename := "input"
blocks := ReadBlockFile(filename)
byZ := BlocksByZ(blocks)
space := NewSpace(byZ)
space.SettleAll()
initialBlocks := space.InitialCollectGoodToDisintegrate()
correct := space.ThirdTimeCollectGoodToDisintegrate()
t.Log("len of initial solution : ", len(initialBlocks))
t.Log("len of correct solution : ", len(correct))
for _, disintegratableInInitial := range initialBlocks {
indexInCorrect := slices.IndexFunc(correct, func(e Block) bool {
return e.NameNum == disintegratableInInitial.NameNum
})
if indexInCorrect == -1 {
t.Logf("> found %+v. falsly marked as disintegratable\n\n", disintegratableInInitial)
}
}
}

9
go.mod
View File

@ -1,3 +1,12 @@
module sunshine.industries/aoc2023 module sunshine.industries/aoc2023
go 1.21.4 go 1.21.4
require github.com/tidwall/pinhole v0.0.0-20210130162507-d8644a7c3d19
require (
github.com/fogleman/gg v1.3.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/google/btree v1.1.2 // indirect
golang.org/x/image v0.14.0 // indirect
)

10
go.sum Normal file
View File

@ -0,0 +1,10 @@
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/tidwall/pinhole v0.0.0-20210130162507-d8644a7c3d19 h1:PH18rfaiwA/34DAtuREBTrrByvZeLHqhfYh4SG7jYg4=
github.com/tidwall/pinhole v0.0.0-20210130162507-d8644a7c3d19/go.mod h1:5VfbOBfzaI6Y0XiGSkz7hiXgKtwYaDBI3plwKGsLonM=
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=

View File

@ -4,15 +4,15 @@ import (
"log" "log"
"time" "time"
"sunshine.industries/aoc2023/day20" "sunshine.industries/aoc2023/day22"
) )
func main() { func main() {
startTime := time.Now() startTime := time.Now()
log.Print("> starting run:") log.Print("> starting run:")
result := day20.Run() result := day22.Run()
log.Printf("\n\nday20 result: %d\n****\n", result) log.Printf("\n\nday22 result: %d\n****\n", result)
endTime := time.Now() endTime := time.Now()
diff := endTime.Sub(startTime) diff := endTime.Sub(startTime)
log.Printf("execution took %s", diff.String()) log.Printf("execution took %s", diff.String())