### re: AoC Day 12: Subterranean Sustainability VIEW POST

Here is currently my Python code for today. Took me some time because I did not understand that we had to sum the indeces of the pots(!).

For part 2 I am looking for loops in the evolution, so that if we reach a plant state (regardless of the indeces) that we have reached before, we loop over those same circular steps without recalculation until reaching to the end.

I could clean up a little, especially the code that cleans up the borders. Maybe if I find time I will do it, but I first hope to later make a Golang solution based on my Python code.

``````import re

with open('input') as f:

# Since the rules only take two pots to left and right into account,
# we are guaranteed that adding four pots left and right is sufficient.
plants_initial = "...." + data[0][15:-1] + "...."

rules = {}
for i in range(2, len(data)):
rule_match = re.match(r'([#|\.]+) => ([#|\.])', data[i])
rule, res = rule_match.group(1), rule_match.group(2)
rules[rule] = res

# Evolve generations
def evolve(generations):
seen = {}   # Keep track of loops when evolving
shift = -4  # Added padding must be accounted for by shifting
e = 0       # Initial evolve step
while e < generations:
e += 1  # Evolve step
plants_copy = plants.copy()
for idx, plant in enumerate(plants):
# Ignore outer borders which extend infinitely with empty pots (".")
if idx < 2 or idx > len(plants) - 3:
continue
# For real inputs all rules are defined. But for example we would
# have to use (".") if rule was not defined for this pot.
plants_copy[idx] = rules.get(''.join(plants[idx-2:idx+3]), ".")
if plants_copy[3] == '#':
# If we planted close to the left border, we must extend it:
plants_copy.insert(0, '.')
shift -= 1  # Adjusts the pot index shifting
elif plants_copy[:5] == ['.'] * 5:
# If we have deserted the whole left border, we can remove one pot:
plants_copy.remove('.')
shift += 1  # Adjusts the pot index shifting
if plants_copy[-3] == '#':
# If we planted close to the right border, we must extend it:
plants_copy.append('.')
elif plants_copy[-4:] == ['.'] * 5:
# If we have deserted the whole right border, we can remove one pot:
plants_copy = plants_copy[:-1]
plants = plants_copy
if seen.get(''.join(plants), False):
# We have made a full circle! No need to recalculate next steps.
# We can just warp into the future...
circle_length = e - seen[''.join(plants)][0]  # The steps of this circle
circles = (generations - e) // circle_length  # The circles to make
e += circles * circle_length                  # Warping into future...
shift_length = shift - seen[''.join(plants)][1]  # The shifts this circle performs
shift += circles * shift_length                  # Adjusting shifts
seen[''.join(plants)] = (e, shift)  # Add unseen pattern to dictionary
return plants, shift

# Part 1:
plants, shift = evolve(generations=20)
print('Part 1:')
print(sum(i + shift for i, p in enumerate(plants) if p == '#'))

# Part 2:
plants, shift = evolve(generations=50000000000)
print('Part 2:')
print(sum(i + shift for i, p in enumerate(plants) if p == '#'))
``````

Here is the Golang code. I don't think this is the best way of doing it in Golang, but it does the job!

``````package main

import (
"bufio"
"fmt"
"os"
"reflect"
"regexp"
)

type entry struct {
e     int
shift int
}

// and returns a slice of its lines.
func readLines(path string) ([]string, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()

var lines []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines, scanner.Err()
}

// function to evolve generations.
func evolve(generations int) ([]rune, int) {
seen := make(map[string]entry)
shift := -4
plantsCopy := make([]rune, len(plants))
plantsOld := make([]rune, len(plants))
copy(plantsOld, plants)
for e := 1; e <= generations; e++ {
plantsCopy = make([]rune, len(plantsOld))
copy(plantsCopy, plantsOld)
for idx := range plantsOld {
if idx < 2 || idx > len(plantsOld)-3 {
continue
}
plantsCopy[idx] = rules[string(plantsOld[idx-2:idx+3])]
}
if plantsCopy[3] == '#' {
plantsCopy = append(plantsCopy, 0)
copy(plantsCopy[1:], plantsCopy[0:])
plantsCopy[0] = '.'
shift--
} else if reflect.DeepEqual(plantsCopy[:5], []rune{'.', '.', '.', '.', '.'}) {
plantsCopy = plantsCopy[1:]
shift++
}
if plantsCopy[len(plantsCopy)-3] == '#' {
plantsCopy = append(plantsCopy, '.')
} else if reflect.DeepEqual(plantsCopy[len(plantsCopy)-5:], []rune{'.', '.', '.', '.', '.'}) {
plantsCopy = plantsCopy[:len(plantsCopy)-1]
}
if val, ok := seen[string(plantsCopy)]; ok {
circleLength := e - val.e
circles := (generations - e) / circleLength
e = e + circles*circleLength
shiftLength := shift - val.shift
shift = shift + circles*shiftLength
}
seen[string(plantsCopy)] = entry{e: e, shift: shift}
plantsOld = make([]rune, len(plantsCopy))
copy(plantsOld, plantsCopy)
}
return plantsCopy, shift
}

var plants []rune
var rules map[string]rune

func main() {
if err != nil {
panic(err)
}
plantsInitial := "...." + data[0][15:] + "...."
plants = []rune(plantsInitial)

rules = make(map[string]rune)
r, _ := regexp.Compile("([#|\\.]+) => ([#|\\.])")
for _, d := range data[2:] {
rule := r.FindStringSubmatch(d)[1]
res := r.FindStringSubmatch(d)[2]
rules[rule] = []rune(res)[0]
}

// Part 1:
plants, shift := evolve(20)
fmt.Println("Part 1:")
var sum int
sum = 0
for i, p := range plants {
if p == '#' {
sum += i + shift
}
}
fmt.Println(sum)

// Part 2:
plants, shift = evolve(50000000000)
fmt.Println("Part 2:")
sum = 0
for i, p := range plants {
if p == '#' {
sum += i + shift
}
}
fmt.Println(sum)
}
``````
code of conduct - report abuse