#!/usr/bin/python
# Valentin Voigt <ValentinVoigt@gmx.de>

import os
import sys

F_DEAD  = 0 # Dead cell
F_ALIVE = 1 # Living cell

class GameOfLife:

    field      = []
    renderer   = None
    generation = 0

    def __init__(self, width, height, renderer):
        """Initializes the GameOfLife object.
        Width describes the number of cells on the x-axis, height describes
        the number of cells on the y-axis. Renderer must be callable. It takes
        one argument with the two-dimensional game field."""

        self.field = [[F_DEAD for i in range(width)] for i in range(height)]
        self.width = width
        self.height = height
        self.renderer = renderer

    def alive_neighbours(self, x, y):
        """Returns the number of living cells around the given cell."""

        count = 0
        # Some time ago I put that annoying "if"s into some functions called
        # "cell_exists" and "if_alive_return_one". So the following part was
        # very easy with just 8 lines similar to this one:
        #     count += if_alive_return_one(x+1, y+1)
        # But because this was to slowly I had to put these "if"s here. Now
        # the code works a bit faster, although it doesn't look very
        # professional anymore.
        if y-1 < self.height and y-1 >= 0 and x-1 < self.width and x-1 >= 0:
            if self.field[y-1][x-1] == F_ALIVE:
                count += 1
        if y-1 < self.height and y-1 >= 0 and x < self.width and x >= 0:
            if self.field[y-1][x] == F_ALIVE:
                count += 1
        if y-1 < self.height and y-1 >= 0 and x+1 < self.width and x+1 >= 0:
            if self.field[y-1][x+1] == F_ALIVE:
                count += 1
        if y < self.height and y >= 0 and x-1 < self.width and x-1 >= 0:
            if self.field[y][x-1] == F_ALIVE:
                count += 1
        if y < self.height and y >= 0 and x+1 < self.width and x+1 >= 0:
            if self.field[y][x+1] == F_ALIVE:
                count += 1
        if y+1 < self.height and y+1 >= 0 and x-1 < self.width and x-1 >= 0:
            if self.field[y+1][x-1] == F_ALIVE:
                count += 1
        if y+1 < self.height and y+1 >= 0 and x < self.width and x >= 0:
            if self.field[y+1][x] == F_ALIVE:
                count += 1
        if y+1 < self.height and y+1 >= 0 and x+1 < self.width and x+1 >= 0:
            if self.field[y+1][x+1] == F_ALIVE:
                count += 1
        return count

    def activate(self, x, y):
        """Marks the specified cells as alive"""

        self.field[y][x] = F_ALIVE

    def activate_list(self, cells):
        """Marks the specified list of cells as alive.
        Takes a list of lists with two elemts. Example:
        activate_list([ (0,1), (2,3),  (9,2) ])"""

        for cell in cells:
            self.activate(cell[0], cell[1])

    def tick(self):
        """Calculate the next generation."""

        # Initialises a new field with only dead cells. They'll be
        # overwritten in the following lines. Before I used [].append to
        # create the new field. But this was too slowly, too...
        new_field = [[F_DEAD for i in range(self.width)] for j in range(self.height)]
        y = 0
        for row in self.field:
            x = 0
            for cell in row:
                if cell == F_DEAD:
                    if self.alive_neighbours(x, y) == 3:
                        new_field[y][x] = F_ALIVE
                    else:
                        new_field[y][x] = F_DEAD
                elif cell == F_ALIVE:
                    n = self.alive_neighbours(x, y)
                    if n < 2:
                        new_field[y][x] = F_DEAD
                    elif n > 3:
                        new_field[y][x] = F_DEAD
                    else:
                        new_field[y][x] = F_ALIVE
                x += 1
            y += 1
        self.field = new_field
        self.generation += 1
        
    def render(self):
        """Calls the render function."""

        # Reminds me on some of my friends.
        # They often make others do their jobs. :-D 
        self.renderer(self.field)

def ConsoleRenderer(field):
    """Simplest way I found for drawing the game field."""

    # Clear screen
    if os.name == "nt":
        os.system("cls")
    elif os.name == "posix":
        os.system("clear")
    else:
        for i in range(0, 100):
            print ""
    # Print field
    for row in field:
        for cell in row:
            sys.stdout.write("X" if cell == F_ALIVE else " ")
        print
    print

def start_example():
    game = GameOfLife(79, 25, ConsoleRenderer)
    game.activate_list([ (50, 16), (51, 16), (51, 17), (51, 15), (52, 15) ])
    try:
        while True:
            game.tick()
            game.render()
    except KeyboardInterrupt:
        pass
        
if __name__ == "__main__":
    start_example()

