Conway's Game of Life: Difference between revisions
Content added Content deleted
(add Game of Life for Dart developed during Hackergarten Vienna) |
|||
Line 1,360: | Line 1,360: | ||
} |
} |
||
}</lang> |
}</lang> |
||
=={{header|Dart}}== |
|||
<lang dart>/** |
|||
* States of a cell. A cell is either [ALIVE] or [DEAD]. |
|||
* The state contains its [symbol] for printing. |
|||
*/ |
|||
class State { |
|||
const State(this.symbol); |
|||
static final ALIVE = const State('#'); |
|||
static final DEAD = const State(' '); |
|||
final String symbol; |
|||
} |
|||
/** |
|||
* The "business rule" of the game. Depending on the count of neighbours, |
|||
* the [cellState] changes. |
|||
*/ |
|||
class Rule { |
|||
Rule(this.cellState); |
|||
reactToNeighbours(int neighbours) { |
|||
if (neighbours == 3) { |
|||
cellState = State.ALIVE; |
|||
} else if (neighbours != 2) { |
|||
cellState = State.DEAD; |
|||
} |
|||
} |
|||
var cellState; |
|||
} |
|||
/** |
|||
* A coordinate on the [Grid]. |
|||
*/ |
|||
class Point { |
|||
const Point(this.x, this.y); |
|||
operator +(other) => new Point(x + other.x, y + other.y); |
|||
final int x; |
|||
final int y; |
|||
} |
|||
/** |
|||
* List of the relative indices of the 8 cells around a cell. |
|||
*/ |
|||
class Neighbourhood { |
|||
List<Point> points() { |
|||
return [ |
|||
new Point(LEFT, UP), new Point(MIDDLE, UP), new Point(RIGHT, UP), |
|||
new Point(LEFT, SAME), new Point(RIGHT, SAME), |
|||
new Point(LEFT, DOWN), new Point(MIDDLE, DOWN), new Point(RIGHT, DOWN) |
|||
]; |
|||
} |
|||
static final LEFT = -1; |
|||
static final MIDDLE = 0; |
|||
static final RIGHT = 1; |
|||
static final UP = -1; |
|||
static final SAME = 0; |
|||
static final DOWN = 1; |
|||
} |
|||
/** |
|||
* The grid is an endless, two-dimensional [field] of cell [State]s. |
|||
*/ |
|||
class Grid { |
|||
Grid(this.xCount, this.yCount) { |
|||
_field = new Map(); |
|||
_neighbours = new Neighbourhood().points(); |
|||
} |
|||
set(point, state) { |
|||
_field[_pos(point)] = state; |
|||
} |
|||
State get(point) { |
|||
var state = _field[_pos(point)]; |
|||
return state != null ? state : State.DEAD; |
|||
} |
|||
int countLiveNeighbours(point) => |
|||
_neighbours.filter((offset) => get(point + offset) == State.ALIVE).length; |
|||
_pos(point) => '${(point.x + xCount) % xCount}:${(point.y + yCount) % yCount}'; |
|||
print() { |
|||
var sb = new StringBuffer(); |
|||
iterate((point) { sb.add(get(point).symbol); }, (x) { sb.add("\n"); }); |
|||
return sb.toString(); |
|||
} |
|||
iterate(eachCell, [finishedRow]) { |
|||
for (var x = 0; x < xCount; x++) { |
|||
for (var y = 0; y < yCount; y++) { |
|||
eachCell(new Point(x, y)); |
|||
} |
|||
if(finishedRow != null) { |
|||
finishedRow(x); |
|||
} |
|||
} |
|||
} |
|||
final xCount, yCount; |
|||
List<Point> _neighbours; |
|||
Map<String, State> _field; |
|||
} |
|||
/** |
|||
* The game updates the [grid] in each step using the [Rule]. |
|||
*/ |
|||
class Game { |
|||
Game(this.grid); |
|||
tick() { |
|||
var newGrid = createNewGrid(); |
|||
grid.iterate((point) { |
|||
var rule = new Rule(grid.get(point)); |
|||
rule.reactToNeighbours(grid.countLiveNeighbours(point)); |
|||
newGrid.set(point, rule.cellState); |
|||
}); |
|||
grid = newGrid; |
|||
} |
|||
createNewGrid() => new Grid(grid.xCount, grid.yCount); |
|||
printGrid() => print(grid.print()); |
|||
Grid grid; |
|||
} |
|||
main() { |
|||
// Run the GoL with a blinker. |
|||
runBlinker(); |
|||
} |
|||
runBlinker() { |
|||
var game = new Game(createBlinkerGrid()); |
|||
for(int i = 0; i < 3; i++) { |
|||
game.printGrid(); |
|||
game.tick(); |
|||
} |
|||
game.printGrid(); |
|||
} |
|||
createBlinkerGrid() { |
|||
var grid = new Grid(4, 4); |
|||
loadBlinker(grid); |
|||
return grid; |
|||
} |
|||
loadBlinker(grid) => blinkerPoints().forEach((point) => grid.set(point, State.ALIVE)); |
|||
blinkerPoints() => [new Point(0, 1), new Point(1, 1), new Point(2, 1)];</lang> |
|||
Test cases driving the design of this code: |
|||
<lang dart>#import('<path to sdk>/lib/unittest/unittest.dart'); |
|||
main() { |
|||
group('rules', () { |
|||
test('should let living but lonely cell die', () { |
|||
var rule = new Rule(State.ALIVE); |
|||
rule.reactToNeighbours(1); |
|||
expect(rule.cellState, State.DEAD); |
|||
}); |
|||
test('should let proper cell live on', () { |
|||
var rule = new Rule(State.ALIVE); |
|||
rule.reactToNeighbours(2); |
|||
expect(rule.cellState, State.ALIVE); |
|||
}); |
|||
test('should let dead cell with three neighbours be reborn', () { |
|||
var rule = new Rule(State.DEAD); |
|||
rule.reactToNeighbours(3); |
|||
expect(rule.cellState, State.ALIVE); |
|||
}); |
|||
test('should let living cell with too many neighbours die', () { |
|||
var rule = new Rule(State.ALIVE); |
|||
rule.reactToNeighbours(4); |
|||
expect(rule.cellState, State.DEAD); |
|||
}); |
|||
}); |
|||
group('grid', () { |
|||
var origin = new Point(0, 0); |
|||
test('should have state', () { |
|||
var grid = new Grid(1, 1); |
|||
expect(grid.get(origin), State.DEAD); |
|||
grid.set(origin, State.ALIVE); |
|||
expect(grid.get(origin), State.ALIVE); |
|||
}); |
|||
test('should have dimension', () { |
|||
var grid = new Grid(2, 3); |
|||
expect(grid.get(origin), State.DEAD); |
|||
grid.set(origin, State.ALIVE); |
|||
expect(grid.get(origin), State.ALIVE); |
|||
expect(grid.get(new Point(1, 2)), State.DEAD); |
|||
grid.set(new Point(1, 2), State.ALIVE); |
|||
expect(grid.get(new Point(1, 2)), State.ALIVE); |
|||
}); |
|||
test('should be endless', () { |
|||
var grid = new Grid(2, 4); |
|||
grid.set(new Point(2, 4), State.ALIVE); |
|||
expect(grid.get(origin), State.ALIVE); |
|||
grid.set(new Point(-1, -1), State.ALIVE); |
|||
expect(grid.get(new Point(1, 3)), State.ALIVE); |
|||
}); |
|||
test('should print itself', () { |
|||
var grid = new Grid(1, 2); |
|||
grid.set(new Point(0, 1), State.ALIVE); |
|||
expect(grid.print(), " #\n"); |
|||
}); |
|||
}); |
|||
group('game', () { |
|||
test('should exists', () { |
|||
var game = new Game(null); |
|||
expect(game, isNotNull); |
|||
}); |
|||
test('should create a new grid when ticked', () { |
|||
var grid = new Grid(1, 1); |
|||
var game = new Game(grid); |
|||
game.tick(); |
|||
expect(game.grid !== grid); |
|||
}); |
|||
test('should have a grid with the same dimension after tick', (){ |
|||
var game = new Game(new Grid(2, 3)); |
|||
game.tick(); |
|||
expect(game.grid.xCount, 2); |
|||
expect(game.grid.yCount, 3); |
|||
}); |
|||
test('should apply rules to middle cell', (){ |
|||
var grid = new Grid(3, 3); |
|||
grid.set(new Point(1, 1), State.ALIVE); |
|||
var game = new Game(grid); |
|||
game.tick(); |
|||
expect(game.grid.get(new Point(1, 1)), State.DEAD); |
|||
grid.set(new Point(0, 0), State.ALIVE); |
|||
grid.set(new Point(1, 0), State.ALIVE); |
|||
game = new Game(grid); |
|||
game.tick(); |
|||
expect(game.grid.get(new Point(1, 1)), State.ALIVE); |
|||
}); |
|||
test('should apply rules to all cells', (){ |
|||
var grid = new Grid(3, 3); |
|||
grid.set(new Point(0, 1), State.ALIVE); |
|||
grid.set(new Point(1, 0), State.ALIVE); |
|||
grid.set(new Point(1, 1), State.ALIVE); |
|||
var game = new Game(grid); |
|||
game.tick(); |
|||
expect(game.grid.get(new Point(0, 0)), State.ALIVE); |
|||
}); |
|||
}); |
|||
}</lang> |
|||
Output: |
|||
<pre> |
|||
# |
|||
# |
|||
# |
|||
### |
|||
# |
|||
# |
|||
# |
|||
### |
|||
</pre> |
|||
=={{header|E}}== |
=={{header|E}}== |