package main import ( "fmt" "io/ioutil" "log" "strings" h "git.bullercodeworks.com/brian/adventofcode/helpers" ) type Layout3d struct { Area map[h.Coordinate3d]bool TopLeft h.Coordinate3d BottomRight h.Coordinate3d } func NewLayout3d(filename string) *Layout3d { ret := Layout3d{ Area: make(map[h.Coordinate3d]bool), TopLeft: h.Coordinate3d{X: 0, Y: 0, Z: 0}, BottomRight: h.Coordinate3d{X: 0, Y: 0, Z: 0}, } input, err := ioutil.ReadFile(filename) if err != nil { log.Fatal(err.Error()) } lines := make([]string, 0) for _, line := range strings.Split(string(input), "\n") { lines = append(lines, line) } for y := 0; y < len(lines); y++ { if len(lines[y]) < ret.BottomRight.X { break } ret.BottomRight.Y = y for x := 0; x < len(lines[y]); x++ { ret.BottomRight.X = x ret.Area[h.Coordinate3d{X: x, Y: y}] = lines[y][x] == '#' } } return &ret } func (l *Layout3d) Step() { a := make(map[h.Coordinate3d]bool) for k, v := range l.Area { a[k] = v } middle := h.Coordinate{X: l.BottomRight.X / 2, Y: l.BottomRight.Y / 2} for z := l.TopLeft.Z - 1; z <= l.BottomRight.Z+1; z++ { for y := l.TopLeft.Y; y <= l.BottomRight.Y; y++ { for x := l.TopLeft.X; x <= l.BottomRight.X; x++ { c := h.Coordinate3d{X: x, Y: y, Z: z} if x == middle.X && y == middle.Y { a[c] = false continue } neighbors := l.GetNeighborCount(c) if l.Area[c] { if neighbors != 1 { a[c] = false } } else { if neighbors == 1 || neighbors == 2 { a[c] = true // If c is on a new 'z', we need to expand our TopLeft/BottomRight if c.Z < l.TopLeft.Z { l.TopLeft.Z = c.Z } else if c.Z > l.BottomRight.Z { l.BottomRight.Z = c.Z } } } } } } for k, v := range a { l.Area[k] = v } } func (l *Layout3d) GetNeighborCount(c h.Coordinate3d) int { middle := h.Coordinate3d{X: l.BottomRight.X / 2, Y: l.BottomRight.Y / 2, Z: c.Z} var neighbors []h.Coordinate3d if c.Y == l.TopLeft.Y { neighbors = append(neighbors, middle.Up().North()) neighbors = append(neighbors, c.South()) if c.X == l.TopLeft.X { neighbors = append(neighbors, middle.Up().West()) neighbors = append(neighbors, c.East()) } else if c.X == l.BottomRight.X { neighbors = append(neighbors, middle.Up().East()) neighbors = append(neighbors, c.West()) } else { neighbors = append(neighbors, c.West()) neighbors = append(neighbors, c.East()) } } else if c.Y == l.BottomRight.Y { neighbors = append(neighbors, middle.Up().South()) neighbors = append(neighbors, c.North()) if c.X == l.TopLeft.X { neighbors = append(neighbors, middle.Up().West()) neighbors = append(neighbors, c.East()) } else if c.X == l.BottomRight.X { neighbors = append(neighbors, middle.Up().East()) neighbors = append(neighbors, c.West()) } else { neighbors = append(neighbors, c.West()) neighbors = append(neighbors, c.East()) } } else if c.X == l.TopLeft.X { neighbors = append(neighbors, middle.Up().West()) neighbors = append(neighbors, c.East()) if c.Y == l.TopLeft.Y { neighbors = append(neighbors, middle.Up().North()) neighbors = append(neighbors, c.South()) } else if c.Y == l.BottomRight.Y { neighbors = append(neighbors, middle.Up().South()) neighbors = append(neighbors, c.North()) } else { neighbors = append(neighbors, c.South()) neighbors = append(neighbors, c.North()) } } else if c.X == l.BottomRight.X { neighbors = append(neighbors, middle.Up().East()) neighbors = append(neighbors, c.West()) if c.Y == l.TopLeft.Y { neighbors = append(neighbors, middle.Up().North()) neighbors = append(neighbors, c.South()) } else if c.Y == l.BottomRight.Y { neighbors = append(neighbors, middle.Up().South()) neighbors = append(neighbors, c.North()) } else { neighbors = append(neighbors, c.South()) neighbors = append(neighbors, c.North()) } } else if c.Y == middle.Y-1 && c.X == middle.X { neighbors = append(neighbors, c.North()) neighbors = append(neighbors, c.East()) neighbors = append(neighbors, c.West()) // South is the North row of the lower grid for x := l.TopLeft.X; x <= l.BottomRight.X; x++ { neighbors = append(neighbors, h.Coordinate3d{X: x, Y: l.TopLeft.Y, Z: c.Down().Z}) } } else if c.Y == middle.Y+1 && c.X == middle.X { neighbors = append(neighbors, c.East()) neighbors = append(neighbors, c.South()) neighbors = append(neighbors, c.West()) // North is the South row of the lower grid for x := l.TopLeft.X; x <= l.BottomRight.X; x++ { neighbors = append(neighbors, h.Coordinate3d{X: x, Y: l.BottomRight.Y, Z: c.Down().Z}) } } else if c.X == middle.X-1 && c.Y == middle.Y { neighbors = append(neighbors, c.North()) neighbors = append(neighbors, c.South()) neighbors = append(neighbors, c.West()) // East is the West row of the lower grid for y := l.TopLeft.Y; y <= l.BottomRight.Y; y++ { neighbors = append(neighbors, h.Coordinate3d{X: l.TopLeft.X, Y: y, Z: c.Down().Z}) } } else if c.X == middle.X+1 && c.Y == middle.Y { neighbors = append(neighbors, c.North()) neighbors = append(neighbors, c.East()) neighbors = append(neighbors, c.South()) // West is the East row of the lower grid for y := l.TopLeft.Y; y <= l.BottomRight.Y; y++ { neighbors = append(neighbors, h.Coordinate3d{X: l.BottomRight.X, Y: y, Z: c.Down().Z}) } } else { neighbors = append(neighbors, c.North()) neighbors = append(neighbors, c.East()) neighbors = append(neighbors, c.South()) neighbors = append(neighbors, c.West()) } ret := 0 for _, v := range neighbors { if n, ok := l.Area[v]; ok && n { ret++ } } return ret } func (l *Layout3d) CountBugs() int { var ret int for _, v := range l.Area { if v { ret++ } } return ret } func (l Layout3d) String() string { var ret string middle := h.Coordinate{X: l.BottomRight.X / 2, Y: l.BottomRight.Y / 2} for z := l.TopLeft.Z; z <= l.BottomRight.Z; z++ { ret = fmt.Sprintf("%s\nDepth %d:\n", ret, z) for y := l.TopLeft.Y; y <= l.BottomRight.Y; y++ { for x := l.TopLeft.X; x <= l.BottomRight.X; x++ { if x == middle.X && y == middle.Y { ret = ret + "?" } else if l.Area[h.Coordinate3d{X: x, Y: y, Z: z}] { ret = ret + "#" } else { ret = ret + "." } } ret = ret + "\n" } } return ret }