Guest User

Untitled

a guest
Oct 24th, 2015
33
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. from collections import defaultdict
  2. class GameOfLife(object):
  3.    
  4.     ADJACENT = ((-1,-1), (0,-1), (1,-1),
  5.                 (-1, 0),         (1, 0),
  6.                 (-1, 1), (0, 1), (1, 1))
  7.     CELL = {'Alive': 'o',
  8.             'Dead': '.'}
  9.     ALL_NEIGHBOURS = range(9)
  10.    
  11.     def __init__(self, rule='B3/S23', paste=None):
  12.         """Setup an empty Game Of Life object."""
  13.         self._reset()
  14.         self.new_rule(rule)
  15.         if paste is not None:
  16.             self.paste(paste)
  17.  
  18.     def _reset(self):
  19.         self.game_data = {}
  20.         self.generations = 0
  21.  
  22.     def new_rule(self, rule):
  23.         """Store the information for a new rule.
  24.        
  25.        This works by reading the rule one character at a time, and will
  26.        """
  27.         self.rule_born = self.rule_alive = current_rule = None
  28.        
  29.         for char in rule.lower():
  30.             if not char.isdigit():
  31.                 if char == 's':
  32.                     if self.rule_alive is None:
  33.                         self.rule_alive = []
  34.                     current_rule = self.rule_alive.append
  35.                 elif char == 'b':
  36.                     if self.rule_born is None:
  37.                         self.rule_born = []
  38.                     current_rule = self.rule_born.append
  39.                 else:
  40.                     current_rule = None
  41.             elif current_rule is not None:
  42.                 current_rule(int(char))
  43.                
  44.         self._validate_rules()
  45.        
  46.         #Remove any extra values to speedup the checks
  47.         self.rule_born = tuple(i for i in self.rule_born if i in self.ALL_NEIGHBOURS)
  48.         self.rule_alive = tuple(i for i in self.rule_alive if i in self.ALL_NEIGHBOURS)
  49.            
  50.     def _validate_rules(self):
  51.         """Check the rules contain valid values."""
  52.         if self.rule_born is None or self.rule_alive is None:
  53.             raise AttributeError("rule must be in the format 'B#S#'")
  54.        
  55.    
  56.     def paste(self, cells, offset=(0, 0), clear=False):
  57.         """Paste a string to act as cells.
  58.  
  59.        Use 'o' to bring a cell to live, and '.' to kill a cell.
  60.        An empty space will not modify the cell under it.
  61.        """
  62.         if clear:
  63.             self._reset()
  64.        
  65.         lines = cells.strip('\n').splitlines()
  66.         for y in range(len(lines)):
  67.             for x in range(len(lines[y])):
  68.                 if lines[y][x] == self.CELL['Alive']:
  69.                     self.add((x + offset[0], y + offset[1]))
  70.                 elif lines[y][x] == self.CELL['Dead']:
  71.                     self.remove((x + offset[0], y + offset[1]))
  72.                    
  73.            
  74.     def add(self, coordinate):
  75.         """Add a cell."""
  76.  
  77.         #Add to dictionary
  78.         if coordinate[1] not in self.game_data:
  79.             self.game_data[coordinate[1]] = set([coordinate[0]])
  80.            
  81.         elif coordinate[0] not in self.game_data[coordinate[1]]:
  82.             self.game_data[coordinate[1]].add(coordinate[0])
  83.    
  84.  
  85.     def remove(self, coordinate):
  86.         """Delete a cell."""
  87.  
  88.         #Remove point from dictionary
  89.         if (coordinate[1] in self.game_data
  90.             and coordinate[0] in self.game_data[coordinate[1]]):
  91.             self.game_data[coordinate[1]].remove(coordinate[0])
  92.  
  93.             #Delete column if no more values
  94.             if not self.game_data[coordinate[1]]:
  95.                 del self.game_data[coordinate[1]]
  96.        
  97.  
  98.     def find_all_adjacent(self):
  99.         """Find the number of adjacent cells to each cell.
  100.  
  101.        It will build a list of all the cells currently alive or
  102.        touching something alive, then iterate through each one to
  103.        find how many they are touching.
  104.        """
  105.         all_coordinates = set()
  106.         adjacent_amount = {}
  107.  
  108.         #Iterate through dictionary to build list of all cells
  109.         for y in self.game_data:
  110.             for x in self.game_data[y]:
  111.                 num_adjacent = 0
  112.                
  113.                 for i in self.ADJACENT:
  114.                     c = (x + i[0], y + i[1])
  115.                     if (c[1] in self.game_data
  116.                         and c[0] in self.game_data[c[1]]):
  117.                         num_adjacent += 1
  118.                        
  119.                     all_coordinates.add(c)
  120.                 adjacent_amount[(x, y)] = num_adjacent
  121.  
  122.         #Find neighbours for each cell
  123.         for coordinate in all_coordinates:
  124.             if coordinate not in adjacent_amount:
  125.                 num_adjacent = 0
  126.                
  127.                 for i in self.ADJACENT:
  128.                     c = (coordinate[0] + i[0],
  129.                          coordinate[1] + i[1])
  130.                     if (c[1] in self.game_data
  131.                         and c[0] in self.game_data[c[1]]):
  132.                         num_adjacent += 1
  133.                        
  134.                 adjacent_amount[coordinate] = num_adjacent
  135.                
  136.         return adjacent_amount
  137.    
  138.     def step(self, n=1):
  139.         """Move forward n steps in the generation."""
  140.         self._validate_rules()
  141.        
  142.         for i in range(n):
  143.            
  144.             self.generations += 1
  145.             adjacent_blocks = self.find_all_adjacent()
  146.            
  147.             for cell in adjacent_blocks:
  148.                 neighbours = adjacent_blocks[cell]
  149.                 alive = (cell[1] in self.game_data
  150.                          and cell[0] in self.game_data[cell[1]])
  151.                
  152.                 if (not alive and neighbours in self.rule_born
  153.                     or alive and neighbours in self.rule_alive):
  154.                     self.add(cell)
  155.                 else:
  156.                     self.remove(cell)
  157.    
  158.    
  159.     def __str__(self):
  160.         """Print the current state of the cells."""
  161.  
  162.         output = []
  163.         min_x = '' #String so it will always be larger than a number
  164.        
  165.         #Fix for if game_data is empty
  166.         if not self.game_data:
  167.             y_range = ()
  168.         else:
  169.             y_range = range(min(self.game_data), max(self.game_data) + 1)
  170.            
  171.             #Find lowest X value to offset the printing by
  172.             for y in y_range:
  173.                 if y in self.game_data:
  174.                     min_x_value = min(self.game_data[y])
  175.                     if min_x_value < min_x:
  176.                         min_x = min_x_value
  177.            
  178.         #Generate each cell a line at a time
  179.         for y in y_range:
  180.             last_x = min_x
  181.            
  182.             if y in self.game_data:
  183.                 x_list = sorted(self.game_data[y])
  184.             else:
  185.                 x_list = []
  186.            
  187.             output_text = ''
  188.             for x in x_list:
  189.                 output_text += ' ' * max(0, 2 * (x - last_x) + 1) + 'o'
  190.                 last_x = x + 1
  191.             output.append(output_text)
  192.            
  193.         return '\r\n'.join(output)
RAW Paste Data