|
1 | 1 | package day17
|
2 | 2 |
|
3 |
| -import "fmt" |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + "math" |
| 6 | +) |
4 | 7 |
|
5 | 8 | func Run(lines []string) error {
|
6 |
| - return fmt.Errorf("not yet implemented") |
| 9 | + dimension, err := ParseBoard(lines, false) |
| 10 | + if err != nil { |
| 11 | + return err |
| 12 | + } |
| 13 | + |
| 14 | + for i := 0; i < 6; i++ { |
| 15 | + dimension = dimension.Step() |
| 16 | + } |
| 17 | + |
| 18 | + activeCells := dimension.GetActiveCells() |
| 19 | + fmt.Println("Part 1:", len(activeCells)) |
| 20 | + |
| 21 | + dimension, err = ParseBoard(lines, true) |
| 22 | + if err != nil { |
| 23 | + return err |
| 24 | + } |
| 25 | + |
| 26 | + for i := 0; i < 6; i++ { |
| 27 | + dimension = dimension.Step() |
| 28 | + } |
| 29 | + |
| 30 | + activeCells = dimension.GetActiveCells() |
| 31 | + fmt.Println("Part 2:", len(activeCells)) |
| 32 | + return nil |
| 33 | +} |
| 34 | + |
| 35 | +type hyperCubeMap map[int]cubeMap |
| 36 | +type cubeMap map[int]planeMap |
| 37 | +type planeMap map[int]rowMap |
| 38 | +type rowMap map[int]bool |
| 39 | +type Point struct{ X, Y, Z, W int } |
| 40 | + |
| 41 | +type Range struct { |
| 42 | + Min int |
| 43 | + Max int |
| 44 | +} |
| 45 | + |
| 46 | +var EmptyRange = Range{math.MaxInt32, math.MinInt32} |
| 47 | + |
| 48 | +type PocketDimension struct { |
| 49 | + cubes hyperCubeMap |
| 50 | + xRange Range |
| 51 | + yRange Range |
| 52 | + zRange Range |
| 53 | + wRange Range |
| 54 | + includeW bool |
| 55 | +} |
| 56 | + |
| 57 | +func NewPocketDimension(includeW bool) PocketDimension { |
| 58 | + wRange := Range{0, 0} |
| 59 | + if includeW { |
| 60 | + wRange = Range{-1, 1} |
| 61 | + } |
| 62 | + |
| 63 | + return PocketDimension{ |
| 64 | + cubes: make(hyperCubeMap), |
| 65 | + xRange: Range{-1, 1}, |
| 66 | + yRange: Range{-1, 1}, |
| 67 | + zRange: Range{-1, 1}, |
| 68 | + wRange: wRange, |
| 69 | + includeW: includeW, |
| 70 | + } |
| 71 | +} |
| 72 | + |
| 73 | +func ParseBoard(lines []string, includeW bool) (PocketDimension, error) { |
| 74 | + dimension := NewPocketDimension(includeW) |
| 75 | + for y, line := range lines { |
| 76 | + for x, cell := range line { |
| 77 | + switch cell { |
| 78 | + case '.': |
| 79 | + dimension.SetCell(x, y, 0, 0, false) |
| 80 | + case '#': |
| 81 | + dimension.SetCell(x, y, 0, 0, true) |
| 82 | + default: |
| 83 | + return dimension, fmt.Errorf("invalid cell '%v'", cell) |
| 84 | + } |
| 85 | + } |
| 86 | + } |
| 87 | + return dimension, nil |
| 88 | +} |
| 89 | + |
| 90 | +func (p *PocketDimension) Step() PocketDimension { |
| 91 | + newDimension := NewPocketDimension(p.includeW) |
| 92 | + p.eachCell(func(x, y, z, w int) { |
| 93 | + val := p.stepCell(x, y, z, w) |
| 94 | + newDimension.SetCell(x, y, z, w, val) |
| 95 | + }) |
| 96 | + // fmt.Println("===") |
| 97 | + return newDimension |
| 98 | +} |
| 99 | +func (p *PocketDimension) GetActiveCells() []Point { |
| 100 | + activeCells := []Point{} |
| 101 | + p.eachCell(func(x, y, z, w int) { |
| 102 | + if p.GetCell(x, y, z, w) { |
| 103 | + activeCells = append(activeCells, Point{x, y, z, w}) |
| 104 | + } |
| 105 | + }) |
| 106 | + return activeCells |
| 107 | +} |
| 108 | + |
| 109 | +func (p *PocketDimension) stepCell(x, y, z, w int) bool { |
| 110 | + current := p.GetCell(x, y, z, w) |
| 111 | + activeNeighbors := 0 |
| 112 | + for _, neighbor := range getNeighbors(p.includeW) { |
| 113 | + if p.GetCell(x+neighbor.X, y+neighbor.Y, z+neighbor.Z, w+neighbor.W) { |
| 114 | + activeNeighbors++ |
| 115 | + } |
| 116 | + } |
| 117 | + |
| 118 | + // fmt.Printf("(%d, %d, %d) has %d active neighbors => ", x, y, z, activeNeighbors) |
| 119 | + if activeNeighbors == 3 || (current && activeNeighbors == 2) { |
| 120 | + // fmt.Println("active") |
| 121 | + return true |
| 122 | + } |
| 123 | + // fmt.Println("inactive") |
| 124 | + return false |
| 125 | +} |
| 126 | + |
| 127 | +func (p *PocketDimension) eachCell(fn func(x, y, z, w int)) { |
| 128 | + for z := p.zRange.Min; z <= p.zRange.Max; z++ { |
| 129 | + for y := p.yRange.Min; y <= p.yRange.Max; y++ { |
| 130 | + for x := p.xRange.Min; x <= p.xRange.Max; x++ { |
| 131 | + for w := p.wRange.Min; w <= p.wRange.Max; w++ { |
| 132 | + fn(x, y, z, w) |
| 133 | + } |
| 134 | + } |
| 135 | + } |
| 136 | + } |
| 137 | +} |
| 138 | + |
| 139 | +func (p *PocketDimension) SetCell(x, y, z, w int, value bool) { |
| 140 | + if !p.includeW && w != 0 { |
| 141 | + panic("tried to set non-zero w in 3d-cube!") |
| 142 | + } |
| 143 | + |
| 144 | + cube, ok := p.cubes[w] |
| 145 | + if !ok { |
| 146 | + cube = make(cubeMap) |
| 147 | + p.cubes[w] = cube |
| 148 | + } |
| 149 | + |
| 150 | + plane, ok := cube[z] |
| 151 | + if !ok { |
| 152 | + plane = make(planeMap) |
| 153 | + cube[z] = plane |
| 154 | + } |
| 155 | + |
| 156 | + row, ok := plane[y] |
| 157 | + if !ok { |
| 158 | + row = make(rowMap) |
| 159 | + plane[y] = row |
| 160 | + } |
| 161 | + |
| 162 | + row[x] = value |
| 163 | + p.zRange = updateRange(p.zRange, z, 1) |
| 164 | + p.yRange = updateRange(p.yRange, y, 1) |
| 165 | + p.xRange = updateRange(p.xRange, x, 1) |
| 166 | + |
| 167 | + if p.includeW { |
| 168 | + p.wRange = updateRange(p.wRange, w, 1) |
| 169 | + } |
| 170 | +} |
| 171 | + |
| 172 | +func updateRange(rg Range, val int, delta int) Range { |
| 173 | + if val <= rg.Min { |
| 174 | + return Range{Min: val - delta, Max: rg.Max} |
| 175 | + } else if val >= rg.Max { |
| 176 | + return Range{Min: rg.Min, Max: val + delta} |
| 177 | + } else { |
| 178 | + return rg |
| 179 | + } |
| 180 | +} |
| 181 | + |
| 182 | +func (p *PocketDimension) GetCell(x, y, z, w int) bool { |
| 183 | + if !p.includeW && w != 0 { |
| 184 | + panic("tried to access non-zero w in 3d cube!") |
| 185 | + } |
| 186 | + |
| 187 | + if cube, ok := p.cubes[w]; ok { |
| 188 | + if plane, ok := cube[z]; ok { |
| 189 | + if row, ok := plane[y]; ok { |
| 190 | + if cell, ok := row[x]; ok { |
| 191 | + return cell |
| 192 | + } |
| 193 | + } |
| 194 | + } |
| 195 | + } |
| 196 | + return false |
| 197 | +} |
| 198 | + |
| 199 | +func getNeighbors(includeW bool) []Point { |
| 200 | + startW, maxW := 0, 0 |
| 201 | + if includeW { |
| 202 | + startW, maxW = -1, 1 |
| 203 | + } |
| 204 | + |
| 205 | + result := []Point{} |
| 206 | + for x := -1; x <= 1; x++ { |
| 207 | + for y := -1; y <= 1; y++ { |
| 208 | + for z := -1; z <= 1; z++ { |
| 209 | + for w := startW; w <= maxW; w++ { |
| 210 | + if x != 0 || y != 0 || z != 0 || w != 0 { |
| 211 | + result = append(result, Point{x, y, z, w}) |
| 212 | + } |
| 213 | + } |
| 214 | + } |
| 215 | + } |
| 216 | + } |
| 217 | + return result |
7 | 218 | }
|
0 commit comments