Skip to content

Commit 4a9162b

Browse files
authored
day17 done
1 parent da0855e commit 4a9162b

File tree

3 files changed

+279
-2
lines changed

3 files changed

+279
-2
lines changed

2020/day17/day17.go

+213-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,218 @@
11
package day17
22

3-
import "fmt"
3+
import (
4+
"fmt"
5+
"math"
6+
)
47

58
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
7218
}

2020/day17/day17_test.go

+58
Original file line numberDiff line numberDiff line change
@@ -1 +1,59 @@
11
package day17
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestNeighbors(t *testing.T) {
10+
doTestNeighbors(t, 26, false)
11+
doTestNeighbors(t, 80, true)
12+
}
13+
14+
func doTestNeighbors(t *testing.T, count int, includeW bool) {
15+
neighbors := getNeighbors(includeW)
16+
require.Equal(t, count, len(neighbors))
17+
for _, cell := range neighbors {
18+
matches := 0
19+
for _, other := range neighbors {
20+
if cell.X == other.X && cell.Y == other.Y && cell.Z == other.Z && cell.W == other.W {
21+
matches++
22+
}
23+
}
24+
require.Equal(t, 1, matches)
25+
}
26+
}
27+
28+
func TestStep(t *testing.T) {
29+
dimension, err := ParseBoard([]string{
30+
".#.",
31+
"..#",
32+
"###",
33+
}, false)
34+
require.NoError(t, err)
35+
require.ElementsMatch(t, []Point{
36+
{1, 0, 0, 0},
37+
{2, 1, 0, 0},
38+
{0, 2, 0, 0},
39+
{1, 2, 0, 0},
40+
{2, 2, 0, 0},
41+
}, dimension.GetActiveCells())
42+
43+
dimension = dimension.Step()
44+
activeCells := dimension.GetActiveCells()
45+
require.Equal(t, 11, len(activeCells))
46+
47+
dimension = dimension.Step()
48+
activeCells = dimension.GetActiveCells()
49+
require.Equal(t, 21, len(activeCells))
50+
51+
dimension = dimension.Step()
52+
activeCells = dimension.GetActiveCells()
53+
require.Equal(t, 38, len(activeCells))
54+
55+
for i := 0; i < 3; i++ {
56+
dimension = dimension.Step()
57+
}
58+
require.Equal(t, 112, len(dimension.GetActiveCells()))
59+
}

2020/day17/input.txt

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
...###.#
2+
#.#.##..
3+
.##.##..
4+
..##...#
5+
.###.##.
6+
.#..##..
7+
.....###
8+
.####..#

0 commit comments

Comments
 (0)