I've just released MObject. It's a tiny library to construct objects and object trees for mocking and stubbing in Python.
Go check it out!
Go check it out!
# gameoflife.py
"""
Simple Game of Life implementation for a fixed size board that
wraps at the edges. All cells, both alive and dead are part of
the representation.
"""
ALIVE = 1
DEAD = 0
class GameAnalyzer(object):
directions = ((-1,-1), (0,-1), (1,-1),
(-1, 0), (1, 0),
(-1, 1), (0, 1), (1, 1))
def __init__(self, board):
self.board = board
def cell_at(self, x, y):
y_wrapped = y % len(self.board)
x_wrapped = x % len(self.board[y_wrapped])
return self.board[y_wrapped][x_wrapped]
def neighbour_count(self, x, y):
return sum([self.cell_at(x + dx, y + dy) for dx, dy in self.directions])
def judge_cell(self, x, y):
rules = (DEAD, DEAD, self.cell_at(x, y), ALIVE, DEAD, DEAD, DEAD, DEAD, DEAD)
neighbour_count = self.neighbour_count(x, y)
return rules[neighbour_count]
def evolve(board):
a = GameAnalyzer(board)
return [[a.judge_cell(x, y) for x, _ in enumerate(row)] for y, row in enumerate(board)]
# gameoflife_test.py
from gameoflife import evolve, GameAnalyzer, ALIVE as x, DEAD as o
from nose.tools import assert_equal
def test_empty_board_remains_empty():
assert_equal(evolve([[o, o, o],
[o, o, o],
[o, o, o]]), [[o, o, o],
[o, o, o],
[o, o, o]])
def test_single_cell_dies():
assert_equal(evolve([[o, o, o],
[o, x, o],
[o, o, o]]), [[o, o, o],
[o, o, o],
[o, o, o]])
def test_stable_formation_remains():
assert_equal(evolve([[o, o, o],
[o, x, x],
[o, x, x]]), [[o, o, o],
[o, x, x],
[o, x, x]])
def test_blinker_blinks():
assert_equal(evolve([[o, o, o, o, o],
[o, o, o, o, o],
[o, x, x, x, o],
[o, o, o, o, o],
[o, o, o, o, o]]), [[o, o, o, o, o],
[o, o, x, o, o],
[o, o, x, o, o],
[o, o, x, o, o],
[o, o, o, o, o]])
assert_equal(evolve([[o, o, o, o, o],
[o, o, x, o, o],
[o, o, x, o, o],
[o, o, x, o, o],
[o, o, o, o, o]]), [[o, o, o, o, o],
[o, o, o, o, o],
[o, x, x, x, o],
[o, o, o, o, o],
[o, o, o, o, o]])
def test_board_wraps_around():
# South -> North
assert_equal(evolve([[o, o, o, o, o],
[o, o, o, o, o],
[o, o, o, o, o],
[o, o, o, o, o],
[o, x, x, x, o]]), [[o, o, x, o, o],
[o, o, o, o, o],
[o, o, o, o, o],
[o, o, x, o, o],
[o, o, x, o, o]])
# North -> South
assert_equal(evolve([[o, x, x, x, o],
[o, o, o, o, o],
[o, o, o, o, o],
[o, o, o, o, o],
[o, o, o, o, o]]), [[o, o, x, o, o],
[o, o, x, o, o],
[o, o, o, o, o],
[o, o, o, o, o],
[o, o, x, o, o]])
# East -> West
assert_equal(evolve([[o, o, o, o, o],
[o, o, o, o, x],
[o, o, o, o, x],
[o, o, o, o, x],
[o, o, o, o, o]]), [[o, o, o, o, o],
[o, o, o, o, o],
[x, o, o, x, x],
[o, o, o, o, o],
[o, o, o, o, o]])
# East -> West
assert_equal(evolve([[o, o, o, o, o],
[x, o, o, o, o],
[x, o, o, o, o],
[x, o, o, o, o],
[o, o, o, o, o]]), [[o, o, o, o, o],
[o, o, o, o, o],
[x, x, o, o, x],
[o, o, o, o, o],
[o, o, o, o, o]])
def check_neighbour_count(board, at, expected):
(x, y) = at
a = GameAnalyzer(board)
assert_equal(a.neighbour_count(x, y), expected)
def test_correct_neighbour_count_to_the_right():
check_neighbour_count([[o, o, o],
[o, o, x],
[o, o, o]], (1, 1), expected=1)
def test_correct_neighbour_count_to_the_right_and_right_down():
check_neighbour_count([[o, o, o],
[o, o, x],
[o, o, x]], (1, 1), expected=2)
def test_zero_neighbour_count_with_no_cells():
check_neighbour_count([[o, o, o],
[o, o, o],
[o, o, o]], (1, 1), expected=0)
def test_correct_neighbour_count_with_alive_cell_on_the_board_but_no_neighbours():
check_neighbour_count([[o, o, o],
[o, x, o],
[o, o, o]], (1, 1), expected=0)
def test_correct_neighbour_count_with_alive_cells_in_all_neighbour_positions():
check_neighbour_count([[x, x, x],
[x, x, x],
[x, x, x]], (1, 1), expected=8)
def test_correct_neighbour_count_for_corner_cell():
check_neighbour_count([[o, o, o],
[o, x, x],
[o, x, o]], (2, 2), expected=3)
Representing the whole board makes the tests easy to read (no additional comments needed, both the existing and expected board are all fleshed out), but input data may be on the large size.# gameoflife2.py
"""
Game of Life implementation for an expanding game. Live cells are
represented as x and y tuples stored in a set.
"""
class Game(object):
directions = ((-1,-1), (0,-1), (1,-1),
(-1, 0), (1, 0),
(-1, 1), (0, 1), (1, 1))
def __init__(self, live_cells):
self.live_cells = live_cells
def next_generation(self):
return set.union(self.survivors(), self.births())
def births(self):
return set([(x, y) for x, y in self.birth_candidates()
if len(self.live_neighbours(x, y)) == 3])
def birth_candidates(self):
return set.difference(self.all_neighbours(), self.live_cells)
def all_neighbours(self):
neighbours = [self.neighbours(x, y) for x, y in self.live_cells] or [set()]
return set.union(*neighbours)
def survivors(self):
return set([(x, y) for x, y in self.live_cells
if len(self.live_neighbours(x, y)) in (2, 3)])
def live_neighbours(self, x, y):
return set.intersection(self.live_cells, self.neighbours(x, y))
def neighbours(self, x, y):
return set([(x + dx, y + dy) for dx, dy in self.directions])
def game_generator(initial_state):
state = initial_state
while True:
state = Game(state).next_generation()
yield state
# gameoflife2_test.py
from gameoflife2 import game_generator, Game
def test_game_with_no_live_cells_remains_dead():
assert game_generator(set()).next() == set()
def test_a_single_live_cell_dies():
game = game_generator(set([(1, 1)]))
assert game.next() == set()
def test_stable_formation_remains():
"""
xxo xxo
xxo -> xxo
ooo ooo
"""
stable = set([(0, 0), (0, 1), (1, 0), (1, 1)])
game = game_generator(stable)
assert game.next() == stable
def test_blinker_blinks():
"""
ooo oxo ooo
xxx -> oxo -> xxx
ooo oxo ooo
"""
game = game_generator(set([(0, 1), (1, 1), (2, 1)]))
assert game.next() == set([(1, 0), (1, 1), (1, 2)])
assert game.next() == set([(0, 1), (1, 1), (2, 1)])
def test_game_plan_expands():
"""
oxo
xxx oxo
ooo -> oxo
ooo ooo
"""
game = game_generator(set([(0, 0), (1, 0), (2, 0)]))
assert game.next() == set([(1,-1), (1, 0), (1, 1)])
def test_Game_survivors_live_cell_with_two_and_three_neighbours_survive():
"""
xxo xxo
xxo -survivors-> oox
oxo oxo
"""
state = set([(0, 0), (0, 1), (1, 0), (1, 1), (1, 2)])
assert Game(state).survivors() == set([(0, 0), (1, 0), (1, 2)])
def test_Game_births_dead_cell_with_three_neighbours_come_alive():
"""
oxo ooo
oxo -births-> xox
oxo ooo
"""
state = set([(1, 0), (1, 1), (1, 2)])
assert Game(state).births() == set([(0, 1), (2, 1)])
def test_Game_birth_candidates_all_dead_cells_with_with_live_neighbour():
"""
ooo ooo
ooo -birth_candidates-> xxx
oxo xox
"""
state = set([(1, 2)])
assert Game(state).birth_candidates() == set([(0, 1), (1, 1), (2, 1), (0, 2),
(2, 2), (0, 3), (1, 3), (2, 3)])
def test_Game_live_neighbours_counts_correctly():
state = set([(0, 0), (0, 1), (1, 0)])
game = Game(state)
assert game.live_neighbours(0, 0) == set([(0, 1), (1, 0)])
assert game.live_neighbours(1, 1) == set([(0, 0), (0, 1), (1, 0)])
CREATE DATABASE sonar CHARACTER SET utf8 COLLATE utf8_general_ci;CREATE USER 'sonar' IDENTIFIED BY 'sonar';GRANT ALL ON sonar.* TO 'sonar'@'%' IDENTIFIED BY 'sonar';GRANT ALL ON sonar.* TO 'sonar'@'localhost' IDENTIFIED BY 'sonar';FLUSH PRIVILEGES;
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>datalogger</groupId>
<artifactId>DataLogger</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>DataLogger</name>
<url>http://maven.apache.org</url>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.7.2</version>
<configuration>
<test>**/*.java</test>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.5.10.201208310627</version>
<executions>
<execution>
<id>jacoco-initialize</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<!-- use this goal to weave all your main classes -->
<goal>test-compile</goal>
<!-- use this goal to weave all your test classes -->
</goals>
</execution>
</executions>
<configuration>
<complianceLevel>1.6</complianceLevel>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>2.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<excludes>
<exclude>junit:junit</exclude>
<exclude>org.aspectj:aspectjrt</exclude>
<exclude>org.slf4j:slf4j-simple</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<coverage.reports.dir>${basedir}/target/coverage-reports</coverage.reports.dir>
<sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
</properties>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.4</version>
</dependency>
</dependencies>
</project>