Compare commits
No commits in common. "29528f23acfe595edb0e035cf52bb17a5bce5f4e" and "53930e66ac783a0d7b028d603b8e07d0c481583c" have entirely different histories.
29528f23ac
...
53930e66ac
|
@ -1,11 +0,0 @@
|
|||
...........
|
||||
.....###.#.
|
||||
.###.##..#.
|
||||
..#.#...#..
|
||||
....#.#....
|
||||
.##..S####.
|
||||
.##..#...#.
|
||||
.......##..
|
||||
.##.#.####.
|
||||
.##..##.##.
|
||||
...........
|
|
@ -1,11 +0,0 @@
|
|||
...........
|
||||
...........
|
||||
...........
|
||||
...........
|
||||
...........
|
||||
.....S.....
|
||||
...........
|
||||
...........
|
||||
...........
|
||||
...........
|
||||
...........
|
105
day21/notes.org
105
day21/notes.org
|
@ -1,105 +0,0 @@
|
|||
#+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
|
|
@ -1,211 +0,0 @@
|
|||
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
100
day22/block.go
|
@ -1,100 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
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)
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
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
|
|
@ -1,74 +0,0 @@
|
|||
#+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
|
|
@ -1,38 +0,0 @@
|
|||
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)
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
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
213
day22/space.go
|
@ -1,213 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
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
9
go.mod
|
@ -1,12 +1,3 @@
|
|||
module sunshine.industries/aoc2023
|
||||
|
||||
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
10
go.sum
|
@ -1,10 +0,0 @@
|
|||
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=
|
6
main.go
6
main.go
|
@ -4,15 +4,15 @@ import (
|
|||
"log"
|
||||
"time"
|
||||
|
||||
"sunshine.industries/aoc2023/day22"
|
||||
"sunshine.industries/aoc2023/day20"
|
||||
)
|
||||
|
||||
func main() {
|
||||
startTime := time.Now()
|
||||
log.Print("> starting run:")
|
||||
|
||||
result := day22.Run()
|
||||
log.Printf("\n\nday22 result: %d\n****\n", result)
|
||||
result := day20.Run()
|
||||
log.Printf("\n\nday20 result: %d\n****\n", result)
|
||||
endTime := time.Now()
|
||||
diff := endTime.Sub(startTime)
|
||||
log.Printf("execution took %s", diff.String())
|
||||
|
|
Loading…
Reference in New Issue