<jupyter_start><jupyter_text>Part 2This is another optimisation problem. Running this for 50 billion generations would take... a while. Let's assume that since the question is framed like this, there will be a shortcut. Most obvious one, if you've ever programmed [game of life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) is the emergence of [stable patterns](https://en.wikipedia.org/wiki/Still_life_(cellular_automaton)).Let's see if we simply see if any emerging repetitions appear.<jupyter_code>state=initial_state

# Let's check the first 10000 states to see if we have any
# patterns emerging
previous_states = set()
for gen in tqdm(range(0,100000)):
    state = apply_rules(state)
    # Remove empty plants from the ends
    plant_containing_state = state.strip(".")
    if plant_containing_state in previous_states:
        print("LOOP DETECTED IN GENERATION", gen)
        break
    else:
        previous_states.add(plant_containing_state)
# So it looks like we have similar states, 
# are they in subsequent generations?

state=initial_state
last_state = ""
for gen in tqdm(range(0,100000)):
    state = apply_rules(state)
    # Remove empty plants from the ends
    plant_containing_state = state.strip(".")
    if plant_containing_state == last_state:
        print("LOOP DETECTED IN GENERATION", gen)
        break
    else:
        last_state = plant_containing_state<jupyter_output><empty_output><jupyter_text>Perfect, in both the sample data, and my data, there are stable patterns emerging. Hopefully in yours as well.Let's assume the patterns are 'drifting', so we just need to understand the drift.<jupyter_code>state=initial_state
last_state = None
last_full_state = None
first_repeat_state = None
first_repeat_state_gen = None
for gen in tqdm(range(0,100000)):
    state = apply_rules(state)
    # Remove empty plants from the ends
    plant_containing_state = state.strip(".")
    if plant_containing_state == last_state:
        first_repeat_state = state
        first_repeat_state_gen = gen
        print("LOOP DETECTED IN GENERATION", first_repeat_state_gen)
        break
    else:
        last_state = plant_containing_state
        last_full_state = state

display(last_full_state)
display(first_repeat_state)

# Calculate the positions of the first plant containing pots for each state
state1_pos = last_full_state.index("#") - int((len(last_full_state)-1)/2)
state2_pos = first_repeat_state.index("#") - int((len(first_repeat_state)-1)/2)
drift = state2_pos - state1_pos

print("The drift of the plant containing pattern is", drift)



# We now have what we need to figure out where the plants are going to be after fifty billion (50000000000) generations
plant_pattern = first_repeat_state.strip(".")
plant_pattern_start = state2_pos + (50000000000*drift) - first_repeat_state_gen

# Slightly different than before - this 
def score_plants_with_start_position(state, start_position):
    plant_sum = 0 
    for ix,pot in enumerate(state):
        pot_number = start_position + ix
        if pot == "#":
            plant_sum += pot_number
    return plant_sum

# To test we score up the full 'first repeat state' and compare it with the 'forwarded score' of just the plant containing part
# They should match
print("Score for first repeat state",score_plants(first_repeat_state))
print("Score for repeat pattern at repeat generation", score_plants_with_start_position(plant_pattern, state2_pos))

# The previous generation should have been
print("Score for original repeat state",score_plants(last_full_state))
print("Score for repeat pattern at previous generation", score_plants_with_start_position(plant_pattern, state2_pos-drift))


# Let's test to make sure it works by playing forward 2500 generations
test_generation = 2500
state=initial_state
for gen in range(0,test_generation):
    state = apply_rules(state)
score_plants(state)
# That should match our 'fast forwarded' pattern score
target_generation = test_generation - first_repeat_state_gen
target_position = state2_pos - 1 + target_generation * drift
score_plants_with_start_position(plant_pattern, target_position)
# We can now calculate score at fifty billion generations
target_generation = 50000000000 - first_repeat_state_gen
target_position = state2_pos - 1 + target_generation * drift

score_plants_with_start_position(plant_pattern, target_position)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.07368660450307027, 'fraction_numerical': 0.020241073459176712, 'mean_word_length': 4.911290322580645, 'pattern_counts': {'":': 1, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 3, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '580497', 'license': 'MIT', 'n_tokens_mistral': 1354, 'n_tokens_neox': 1236, 'n_words': 412, 'path': 'solutions/2018/kws/12-kws.ipynb', 'repo_name': 'kws/AdventOfCode', 'size': '4397'}