import numpy as np from collections import Counter def history(cp,left0,T,depth=80): p=len(cp); L=len(left0)+T+2; left=np.zeros(L,dtype=np.uint8); left[:len(left0)]=left0; H=[] for t in range(T): H.append(left[:depth].copy()); c=cp[t%p] right=np.concatenate([[c],left[:-1]]); leftnb=np.concatenate([left[1:],[0]]) left=leftnb^(left|right) return np.array(H) rng=np.random.default_rng(21) for cp in ((0,1),(0,0,0,1),(0,0,1)): p=len(cp); T=200; w=14 stats=Counter(); gaps=Counter(); bounces_per_pair=[]; pairs=0 for _ in range(4000): s=rng.integers(0,2,size=w).astype(np.uint8); s[-1]=1 H=history(cp,s,T+p) A=H[:T]; B=H[p:T+p] # evolutions of s and s(p) under the same drive D=A^B lit=[cp[t%p]==1 for t in range(T)] first=next((t for t in range(T) if lit[t] and D[t,0]),T) if first<25: continue pairs+=1; nb=0 for t in range(first-1): nz=np.nonzero(D[t])[0]; nz1=np.nonzero(D[t+1])[0] if len(nz)==0 or len(nz1)==0: continue d=nz[0]+1; d1=nz1[0]+1 if d==1 and not lit[t] and d1>=2: # a bounce nb+=1 d2=(nz[1]+1) if len(nz)>1 else None if d2 is None: stats["no second layer"]+=1 elif d1==d2-1: stats["consumed: old 2nd layer marched in"]+=1 elif d1=25 min); bounces per pair: mean {np.mean(bounces_per_pair) if bounces_per_pair else 0:.1f}, max {max(bounces_per_pair) if bounces_per_pair else 0}") print(f" at each bounce: {dict(stats)}") print(f" spacing between difference layers, mod {p}: {dict(sorted(gaps.items()))}")