<jupyter_start><jupyter_text>Day 11 part A<jupyter_code>import numpy as np
infile="11a_input.txt"
with open(infile) as f:
    lines=f.readlines()
def to_int(letter):
    if letter==".": return -1
    elif letter=="L": return 0
    elif letter=="#": return 1
    else: return None
# str to int: -1: floor, 0: empty, 1: occupied
linesint=[]
for line in lines:
    line=line.replace("\n","")
    line=[to_int(c) for c in line]
    linesint.append(line)
    
#print(linesint[0])
# to array
seats=np.array(linesint)
seats
def find_neighbors(rctr,cctr, seats):
    """rctr,cctr: seat position
    seats: 2D numpy array with -1(floor),0(empty),1(occupied)
    returns: row and col positions of neighbors (empty or occupied seats)
    """
    rows_cur_neigh=[]
    cols_cur_neigh=[]
    for r in range(rctr-1,rctr+2):
        for c in range(cctr-1,cctr+2):
            if r==rctr and c==cctr: 
                continue
            if (0<=r<seats.shape[0]) and (0<=c<seats.shape[1]) and seats[r,c]>=0:
                rows_cur_neigh.append(r)
                cols_cur_neigh.append(c)
    return rows_cur_neigh, cols_cur_neigh
# lookup table of neighbors for each seat
rows_chair, cols_chair=np.nonzero(seats==0) # seat positions

rows_neigh=[]
cols_neigh=[]

for rctr,cctr in zip(rows_chair,cols_chair):
    rn,cn=find_neighbors(rctr,cctr,seats)
    rows_neigh.append(rn)
    cols_neigh.append(cn)

    #example
display(rows_neigh[1000])
display(cols_neigh[1000])
def one_round(seats,rows_chair,cols_chair,rows_neigh,cols_neigh,occ_limit=4):
    updated=seats.copy()
    for idx,(r,c) in enumerate(zip(rows_chair,cols_chair)):
        occ=np.sum(seats[rows_neigh[idx],cols_neigh[idx]])
        if occ==0 and seats[r,c]==0:
            updated[r,c]=1
        elif occ>=occ_limit and seats[r,c]==1:
            updated[r,c]=0
    return updated

#%load_ext line_profiler
# %lprun -f one_round one_round(seats,rows_chair,cols_chair,rows_neigh,cols_neigh)
seats_backup=seats.copy()
# play game until stable condition is reached
idx=0
seats=seats_backup.copy()
while True:
    if idx%10==0:
        print("iteration",idx)
    seats_new=one_round(seats,rows_chair,cols_chair,rows_neigh,cols_neigh)
    if np.sum(np.abs(seats_new)-np.abs(seats))==0:
        print("stable condition reached")
        break
    if idx>2000:
        print("2000 interruption: giving up")
        break
    seats=seats_new
    idx+=1
# nr of occupied seats
np.count_nonzero(seats==1)<jupyter_output><empty_output><jupyter_text>part B<jupyter_code>def find_neighbors_B(rctr,cctr, seats):
    """Similar to find_neighbors but with neighbor definition as in part B
    rctr,cctr: seat position
    seats: 2D numpy array with -1(floor),0(empty),1(occupied)
    returns: row and col positions of neighbors (empty or occupied seats)
    """
    rows_cur_neigh=[]
    cols_cur_neigh=[]
    # loop over all possible directions
    for dr in [-1,0,1]:
        for dc in [-1,0,1]:
            if dr==0 and dc==0: 
                continue
            # all possible distances along a specific direction
            for steps in range(1,max(seats.shape[0],seats.shape[1])):
                r=rctr+dr*steps
                c=cctr+dc*steps
                # reached border
                if (r<0) or (r>=seats.shape[0]) or (c<0) or (c>=seats.shape[1]):
                    break
                # check if position is a chair
                if seats[r,c]>=0:
                    rows_cur_neigh.append(r)
                    cols_cur_neigh.append(c)
                    break
    return rows_cur_neigh, cols_cur_neigh
# lookup table with neighbors defined as in B
rows_neigh_B=[]
cols_neigh_B=[]

for rctr,cctr in zip(rows_chair,cols_chair):
    rn,cn=find_neighbors_B(rctr,cctr,seats)
    rows_neigh_B.append(rn)
    cols_neigh_B.append(cn)

#example
display(rows_neigh_B[1000])
display(cols_neigh_B[1000])
# play game until stable condition is reached
idx=0
seats=seats_backup.copy()
while True:
    if idx%10==0:
        print("iteration",idx)
    seats_new=one_round(seats,rows_chair,cols_chair,rows_neigh_B,cols_neigh_B,occ_limit=5) # new params
    if np.sum(np.abs(seats_new)-np.abs(seats))==0:
        print("stable condition reached")
        break
    if idx>2000:
        print("2000 interruption: giving up")
        break
    seats=seats_new
    idx+=1
# nr of occupied seats
np.count_nonzero(seats==1)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1221287241300887, 'fraction_numerical': 0.022287923584261998, 'mean_word_length': 3.1845861084681255, 'pattern_counts': {'":': 3, '<': 15, '<?xml version=': 0, '>': 16, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, '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': '43970', 'license': 'MIT', 'n_tokens_mistral': 1656, 'n_tokens_neox': 1612, 'n_words': 339, 'path': 'advent_of_code_11.ipynb', 'repo_name': 'walkernoreen/adventofcode2020', 'size': '4397'}