From a723fc113a15a41d07e97175aa8e2acc1f52207f Mon Sep 17 00:00:00 2001 From: Parnic Date: Thu, 21 Jul 2022 09:37:39 -0500 Subject: [PATCH] Day 24 solution This one was pretty straightforward and completes the "Game of Life" AoC bingo square. Part 2 felt a little tedious insofar as there wasn't much problem solving to do, just push the part 1 solution into a map/dictionary and implement the new adjacency logic per the spec. --- days/24.go | 289 ++++++++++++++++++++++++++++++++++++++++++++++++ inputs/24p.txt | 5 + inputs/24s1.txt | 5 + inputs/24s2.txt | 5 + main.go | 1 + 5 files changed, 305 insertions(+) create mode 100644 days/24.go create mode 100644 inputs/24p.txt create mode 100644 inputs/24s1.txt create mode 100644 inputs/24s2.txt diff --git a/days/24.go b/days/24.go new file mode 100644 index 0000000..fd4ea41 --- /dev/null +++ b/days/24.go @@ -0,0 +1,289 @@ +package days + +import ( + "fmt" + "math" + + u "parnic.com/aoc2019/utilities" +) + +var ( + day24AdjacentOffsets = []u.Vec2i{ + {X: -1, Y: 0}, + {X: 1, Y: 0}, + {X: 0, Y: -1}, + {X: 0, Y: 1}, + } +) + +type Day24 struct { + grid [][]bool +} + +func (d *Day24) Parse() { + lines := u.GetStringLines("24p") + d.grid = make([][]bool, len(lines)) + for i, line := range lines { + d.grid[i] = make([]bool, len(line)) + for j, ch := range line { + d.grid[i][j] = ch == '#' + } + } +} + +func (d Day24) Num() int { + return 24 +} + +func (d Day24) calcActivatedNeighbors(grid [][]bool, i, j int) int { + activatedNeighbors := 0 + for _, o := range day24AdjacentOffsets { + newI := i + o.X + newJ := j + o.Y + if newI < 0 || newI >= len(grid) || newJ < 0 || newJ >= len(grid[i]) { + continue + } + if grid[newI][newJ] { + activatedNeighbors++ + } + } + + return activatedNeighbors +} + +func (d Day24) recursiveCalcActivatedNeighbors(gridMap map[int][][]bool, mapIdx, i, j int) int { + activatedNeighbors := 0 + numNeighbors := 0 + thisGrid := gridMap[mapIdx] + for _, o := range day24AdjacentOffsets { + newI := i + o.X + newJ := j + o.Y + if newI < 0 || newI >= len(thisGrid) || newJ < 0 || newJ >= len(thisGrid[i]) { + continue + } + if newI == 2 && newJ == 2 { + continue + } + numNeighbors++ + if thisGrid[newI][newJ] { + activatedNeighbors++ + } + } + + checkLower := (i == 1 && j == 2) || + (i == 2 && (j == 1 || j == 3)) || + (i == 3 && j == 2) + if checkLower { + if lowerGrid, exists := gridMap[mapIdx+1]; exists { + if i == 1 { + for _, b := range lowerGrid[0] { + numNeighbors++ + if b { + activatedNeighbors++ + } + } + } else if i == 2 { + if j == 1 { + for _, r := range lowerGrid { + numNeighbors++ + if r[0] { + activatedNeighbors++ + } + } + } else if j == 3 { + for _, r := range lowerGrid { + numNeighbors++ + if r[len(lowerGrid[0])-1] { + activatedNeighbors++ + } + } + } + } else if i == 3 { + for _, b := range lowerGrid[len(lowerGrid)-1] { + numNeighbors++ + if b { + activatedNeighbors++ + } + } + } + } + } + + checkUpper := (i == 0) || (i == len(thisGrid)-1) || + ((i != 0 && i != len(thisGrid)) && (j == 0 || j == len(thisGrid[0])-1)) + if checkUpper { + if upperGrid, exists := gridMap[mapIdx-1]; exists { + if i == 0 { + numNeighbors++ + if upperGrid[1][2] { + activatedNeighbors++ + } + } else if i == len(thisGrid)-1 { + numNeighbors++ + if upperGrid[3][2] { + activatedNeighbors++ + } + } + if j == 0 { + numNeighbors++ + if upperGrid[2][1] { + activatedNeighbors++ + } + } else if j == len(thisGrid[0])-1 { + numNeighbors++ + if upperGrid[2][3] { + activatedNeighbors++ + } + } + } + } + + return activatedNeighbors +} + +func (d Day24) calcRating(grid [][]bool) int { + rating := 0 + for i, r := range grid { + for j := range r { + pow := (i * len(r)) + j + if grid[i][j] { + result := int(math.Pow(2, float64(pow))) + rating += result + } + } + } + return rating +} + +func (d Day24) getNumBugs(gridMap map[int][][]bool) int { + ret := 0 + for _, v := range gridMap { + for _, r := range v { + for _, b := range r { + if b { + ret++ + } + } + } + } + return ret +} + +func copy2d[T comparable](dest [][]T, src [][]T) { + for i, r := range src { + copy(dest[i], r) + } +} + +func (d Day24) Draw(grid [][]bool) { + for _, r := range grid { + for _, c := range r { + if c { + fmt.Print("#") + } else { + fmt.Print(".") + } + } + fmt.Println() + } + fmt.Println() +} + +func (d *Day24) Part1() string { + grid := make([][]bool, len(d.grid)) + scratch := make([][]bool, len(grid)) + for i, g := range d.grid { + grid[i] = make([]bool, len(g)) + scratch[i] = make([]bool, len(g)) + copy(grid[i], d.grid[i]) + } + + found := false + answer := 0 + seenRatings := make([]int, 0) + for i := 1; !found; i++ { + // d.Draw(grid) + for i, r := range grid { + for j := range r { + numActivated := d.calcActivatedNeighbors(grid, i, j) + if grid[i][j] { + scratch[i][j] = numActivated == 1 + } else { + scratch[i][j] = numActivated == 1 || numActivated == 2 + } + } + } + + rating := d.calcRating(scratch) + if u.ArrayContains(seenRatings, rating) { + found = true + // d.Draw(scratch) + answer = rating + } + seenRatings = append(seenRatings, rating) + copy2d(grid, scratch) + } + + return fmt.Sprintf("First repeated biodiversity rating is %s%d%s", u.TextBold, answer, u.TextReset) +} + +func (d *Day24) Part2() string { + makeGrid := func(initialGrid [][]bool) ([][]bool, [][]bool) { + grid := make([][]bool, len(d.grid)) + scratch := make([][]bool, len(grid)) + for i, g := range d.grid { + grid[i] = make([]bool, len(g)) + scratch[i] = make([]bool, len(g)) + if initialGrid != nil { + copy(grid[i], initialGrid[i]) + } + } + + return grid, scratch + } + + gridMap := make(map[int][][]bool) + scratchMap := make(map[int][][]bool) + gridMap[0], scratchMap[0] = makeGrid(d.grid) + + min := 0 + max := 0 + + for i := 0; i < 200; i++ { + gridMap[min-1], scratchMap[min-1] = makeGrid(nil) + gridMap[max+1], scratchMap[max+1] = makeGrid(nil) + min, max = min-1, max+1 + + // if i == 10 { + // keys := u.MapKeys(gridMap) + // sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) + // for _, k := range keys { + // fmt.Println("Depth", k) + // d.Draw(gridMap[k]) + // } + // fmt.Println("# bugs:", d.numBugs(gridMap)) + // } + + for depth, grid := range gridMap { + for i, r := range grid { + for j := range r { + if i == 2 && j == 2 { + continue + } + numActivated := d.recursiveCalcActivatedNeighbors(gridMap, depth, i, j) + if grid[i][j] { + scratchMap[depth][i][j] = numActivated == 1 + } else { + scratchMap[depth][i][j] = numActivated == 1 || numActivated == 2 + } + } + } + } + + for d := range gridMap { + copy2d(gridMap[d], scratchMap[d]) + } + } + + return fmt.Sprintf("Bugs present after 200 minutes: %s%d%s", u.TextBold, d.getNumBugs(gridMap), u.TextReset) +} diff --git a/inputs/24p.txt b/inputs/24p.txt new file mode 100644 index 0000000..23ed2f6 --- /dev/null +++ b/inputs/24p.txt @@ -0,0 +1,5 @@ +.#.## +...#. +....# +.#... +..#.. \ No newline at end of file diff --git a/inputs/24s1.txt b/inputs/24s1.txt new file mode 100644 index 0000000..949fe7c --- /dev/null +++ b/inputs/24s1.txt @@ -0,0 +1,5 @@ +....# +#..#. +#..## +..#.. +#.... \ No newline at end of file diff --git a/inputs/24s2.txt b/inputs/24s2.txt new file mode 100644 index 0000000..b95bc74 --- /dev/null +++ b/inputs/24s2.txt @@ -0,0 +1,5 @@ +..... +..... +..... +#.... +.#... \ No newline at end of file diff --git a/main.go b/main.go index 9564d8e..8f04338 100644 --- a/main.go +++ b/main.go @@ -57,6 +57,7 @@ var dayMap = []day{ &days.Day21{}, &days.Day22{}, &days.Day23{}, + &days.Day24{}, } func main() {