====== LIFE plane ====== August 17, 2017 Life on expanding network. Only nodes needed are included into network. # https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life # 1. Any live cell with fewer than two live neighbours dies, # as if caused by underpopulation. # 2. Any live cell with two or three live neighbours lives on # to the next generation. # 3. Any live cell with more than three live neighbours dies, # as if by overpopulation. # 4. Any dead cell with exactly three live neighbours becomes # a live cell, as if by reproduction. from Nets import Network def expandNode(L,u): if L.getNode(u,'active') is not None: return L.setNode(u,'active',False); r,c = u for dr,dc in [(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)]: v = (r+dr,c+dc) if v not in L._nodes: L.addNode(v) if v not in L.edgeNeighbors(u): id = L.addEdge(u,v) def setActive(L,A): for v in A: if v not in L._nodes: L.addNode(v) if L.getNode(v,'active') is None: expandNode(L,v) L.setNode(v,'active',True); L.setNode(v,'s',[]) def show(Active,m1=None,m2=None,M1=None,M2=None): if m1 is None: m1 = min([r for r,c in Active]) if M1 is None: M1 = max([r for r,c in Active]) if m2 is None: m2 = min([c for r,c in Active]) if M2 is None: M2 = max([c for r,c in Active]) print(m1,m2,M1,M2) P = [['□'] * (M2-m2+1) for i in range(M1-m1+1)] for r,c in Active: P[r-m1][c-m2] = '■' for l in P: print(' '.join(l)) L = Network() glider = [(1,2),(2,3),(3,1),(3,2),(3,3)]; setActive(L,glider) nStep = 5; Cand = glider #; m1=None; m2=None; M1=None; M2=None m1=1; m2=1; M1=5; M2=5 # pentadecathlon = [(5,7),(5,12),(6,5),(6,6),(6,8),(6,9),(6,10),(6,11),(6,13), # (6,14),(7,7),(7,12)] # setActive(L,pentadecathlon); nStep = 16; Cand = pentadecathlon for step in range(nStep+1): # display current state Active = { v for v in Cand if L.getNode(v,'active') } print('\nStep',step,'/',len(L._nodes)) show(Active,m1,m2,M1,M2) for v in Active: L.setNode(v,'s',L.getNode(v,'s')+[step]) if step>=nStep: break # determine Candidates for change Cand = Active for v in Active: Cand = Cand | L.neighbors(v) # prepare change S = [ (u, sum([ L.getNode(v,'active',False) for v in {u}|L.neighbors(u) ])) \ for u in Cand ] # make change # To avoid decisions and branches -if the sum of all nine fields: # - is 3, the inner field state for the next generation will be life # - is 4, the inner field retains its current state # - every other sum sets the inner field to death. for v,s in S: if s==3: expandNode(L,v); L.setNode(v,'active',True); if 's' not in L._nodes[v][3]: L.setNode(v,'s',[]) elif (s!=4) and L.getNode(v,'active'): L.setNode(v,'active',False) print("\nNode activity") for v in L.nodes(): s = L.getNode(v,'s') if s is not None: print(v,"=",s) m1=None; m2=None; M1=None; M2=None ======= RESTART: C:\Users\batagelj\work\Python\graph\Nets\lifePlane.py ======= Step 0 / 22 1 1 3 3 □ ■ □ □ □ ■ ■ ■ ■ Step 1 / 26 2 1 4 3 ■ □ ■ □ ■ ■ □ ■ □ Step 2 / 27 2 1 4 3 □ □ ■ ■ □ ■ □ ■ ■ Step 3 / 30 2 2 4 4 ■ □ □ □ ■ ■ ■ ■ □ Step 4 / 31 2 2 4 4 □ ■ □ □ □ ■ ■ ■ ■ Step 5 / 34 3 2 5 4 ■ □ ■ □ ■ ■ □ ■ □ Node activity (1, 2) = [0] (2, 1) = [1] (2, 2) = [3] (2, 3) = [0, 1, 2, 4] (3, 2) = [0, 1, 5] (3, 3) = [0, 1, 2, 3] (3, 4) = [3, 4, 5] (3, 1) = [0, 2] (4, 2) = [1, 2, 3, 4] (4, 3) = [2, 3, 4, 5] (4, 4) = [4, 5] (5, 3) = [5] >>> m1=1; m2=1; M1=5; M2=5 ======= RESTART: C:\Users\batagelj\work\Python\graph\Nets\lifePlane.py ======= Step 0 / 22 1 1 5 5 □ ■ □ □ □ □ □ ■ □ □ ■ ■ ■ □ □ □ □ □ □ □ □ □ □ □ □ Step 1 / 26 1 1 5 5 □ □ □ □ □ ■ □ ■ □ □ □ ■ ■ □ □ □ ■ □ □ □ □ □ □ □ □ Step 2 / 27 1 1 5 5 □ □ □ □ □ □ □ ■ □ □ ■ □ ■ □ □ □ ■ ■ □ □ □ □ □ □ □ Step 3 / 30 1 1 5 5 □ □ □ □ □ □ ■ □ □ □ □ □ ■ ■ □ □ ■ ■ □ □ □ □ □ □ □ Step 4 / 31 1 1 5 5 □ □ □ □ □ □ □ ■ □ □ □ □ □ ■ □ □ ■ ■ ■ □ □ □ □ □ □ Step 5 / 34 1 1 5 5 □ □ □ □ □ □ □ □ □ □ □ ■ □ ■ □ □ □ ■ ■ □ □ □ ■ □ □ ===== Variant ===== # https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life # 1. Any live cell with fewer than two live neighbours dies, # as if caused by underpopulation. # 2. Any live cell with two or three live neighbours lives on # to the next generation. # 3. Any live cell with more than three live neighbours dies, # as if by overpopulation. # 4. Any dead cell with exactly three live neighbours becomes # a live cell, as if by reproduction. from Nets import Network def expandNode(L,u): if L.getNode(u,'active') is not None: return L.setNode(u,'active',True); L.setNode(u,'s',[]); r,c = u for dr,dc in [(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)]: v = (r+dr,c+dc) if v not in L._nodes: L.addNode(v) if v not in L.edgeNeighbors(u): id = L.addEdge(u,v) def setActive(L,A): for v in A: if v not in L._nodes: L.addNode(v) expandNode(L,v) def show(Active,m1=None,m2=None,M1=None,M2=None): if m1 is None: m1 = min([r for r,c in Active]) if M1 is None: M1 = max([r for r,c in Active]) if m2 is None: m2 = min([c for r,c in Active]) if M2 is None: M2 = max([c for r,c in Active]) print(m1,m2,M1,M2) P = [['□'] * (M2-m2+1) for i in range(M1-m1+1)] for r,c in Active: P[r-m1][c-m2] = '■' for l in P: print(' '.join(l)) L = Network() glider = [(1,2),(2,3),(3,1),(3,2),(3,3)]; setActive(L,glider) nStep = 10; Cand = glider #; m1=None; m2=None; M1=None; M2=None m1=1; m2=1; M1=6; M2=6 # pentadecathlon = [(5,7),(5,12),(6,5),(6,6),(6,8),(6,9),(6,10),(6,11),(6,13), # (6,14),(7,7),(7,12)] # setActive(L,pentadecathlon); nStep = 16; Cand = pentadecathlon for step in range(nStep+1): # display current state Active = { v for v in Cand if L.getNode(v,'active') } print('\nStep',step,'/',len(L._nodes),len(Cand),len(Active)) show(Active,m1,m2,M1,M2) for v in Active: L.setNode(v,'s',L.getNode(v,'s')+[step]) if step>=nStep: break # determine Candidates for change Cand = Active for v in Active: Cand = Cand | L.neighbors(v) # prepare change S = [ (u,sum([L.getNode(v,'active',False) for v in {u}|L.neighbors(u)])) \ for u in Cand ] # make change # To avoid decisions and branches -if the sum of all nine fields: # - is 3, the inner field state for the next generation will be life # - is 4, the inner field retains its current state # - every other sum sets the inner field to death. for v,s in S: if s==3: expandNode(L,v); L.setNode(v,'active',True); elif (s!=4) and L.getNode(v,'active'): L.setNode(v,'active',False) print("\nNode activity") for v in L.nodes(): s = L.getNode(v,'s') if s is not None: print(v,"=",s) ========= RESTART: C:/Users/batagelj/work/Python/graph/Nets/lifeP.py ========= Step 0 / 22 5 5 1 1 6 6 □ ■ □ □ □ □ □ □ ■ □ □ □ ■ ■ ■ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ Step 1 / 26 22 5 1 1 6 6 □ □ □ □ □ □ ■ □ ■ □ □ □ □ ■ ■ □ □ □ □ ■ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ Step 2 / 27 22 5 1 1 6 6 □ □ □ □ □ □ □ □ ■ □ □ □ ■ □ ■ □ □ □ □ ■ ■ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ Step 3 / 30 22 5 1 1 6 6 □ □ □ □ □ □ □ ■ □ □ □ □ □ □ ■ ■ □ □ □ ■ ■ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ Step 4 / 31 22 5 1 1 6 6 □ □ □ □ □ □ □ □ ■ □ □ □ □ □ □ ■ □ □ □ ■ ■ ■ □ □ □ □ □ □ □ □ □ □ □ □ □ □ Step 5 / 34 22 5 1 1 6 6 □ □ □ □ □ □ □ □ □ □ □ □ □ ■ □ ■ □ □ □ □ ■ ■ □ □ □ □ ■ □ □ □ □ □ □ □ □ □ Step 6 / 35 22 5 1 1 6 6 □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ ■ □ □ □ ■ □ ■ □ □ □ □ ■ ■ □ □ □ □ □ □ □ □ Step 7 / 38 22 5 1 1 6 6 □ □ □ □ □ □ □ □ □ □ □ □ □ □ ■ □ □ □ □ □ □ ■ ■ □ □ □ ■ ■ □ □ □ □ □ □ □ □ Step 8 / 39 22 5 1 1 6 6 □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ ■ □ □ □ □ □ □ ■ □ □ □ ■ ■ ■ □ □ □ □ □ □ □ Step 9 / 42 22 5 1 1 6 6 □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ ■ □ ■ □ □ □ □ ■ ■ □ □ □ □ ■ □ □ Step 10 / 43 22 5 1 1 6 6 □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ ■ □ □ □ ■ □ ■ □ □ □ □ ■ ■ □ Node activity (1, 2) = [0] (2, 1) = [1] (2, 2) = [3] (2, 3) = [0, 1, 2, 4] (3, 2) = [0, 1, 5] (3, 3) = [0, 1, 2, 3, 7] (3, 4) = [3, 4, 5, 6, 8] (3, 1) = [0, 2] (4, 2) = [1, 2, 3, 4, 6] (4, 3) = [2, 3, 4, 5, 9] (4, 4) = [4, 5, 6, 7] (5, 3) = [5, 6, 7, 8, 10] (5, 4) = [6, 7, 8, 9] (4, 5) = [7, 8, 9, 10] (5, 5) = [8, 9, 10] (6, 4) = [9, 10] (6, 5) = [10] >>>