Last active
September 25, 2020 12:49
-
-
Save Levi-Lesches/54bb017fb50cfa0dd265a38dfe653dbd to your computer and use it in GitHub Desktop.
Conway's Game of Life, demo in the comments
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "dart:io"; | |
class Position { | |
static int rows = 13; | |
static int columns = 13; | |
static Set<Position> offsets = { | |
Position(-1, -1), Position(-1, 0), Position(-1, 1), | |
Position(0, -1), Position(0, 1), | |
Position(1, -1), Position(1, 0), Position(1, 1), | |
}; | |
final int row, col; | |
const Position(this.row, this.col); | |
@override | |
String toString() => "($row, $col)"; | |
operator + (Position other) => | |
Position(row + other.row, col + other.col); | |
} | |
class Cell { | |
final Board board; | |
final Position position; | |
bool isAlive; | |
bool isAliveNextTick; | |
Cell(this.position, this.board) : isAlive = false; | |
@override | |
String toString() => isAlive ? "■" : "□"; | |
int get aliveNeighborsCount { | |
int result = 0; | |
for (final Position offset in Position.offsets) { | |
final Position newPosition = offset + position; | |
if (!board.contains(newPosition)) continue; | |
final bool isOtherCellAlive = board [newPosition].isAlive; | |
if (isOtherCellAlive) result++; | |
} | |
return result; | |
} | |
void prepareNextTick() { | |
final neighborCount = aliveNeighborsCount; | |
isAliveNextTick = isAlive | |
? neighborCount == 2 || neighborCount == 3 | |
: neighborCount == 3; | |
} | |
void update() { | |
isAlive = isAliveNextTick; | |
isAliveNextTick = null; | |
} | |
} | |
abstract class FileParser { | |
final List<String> lines; | |
FileParser(String filename) : lines = File(filename).readAsLinesSync() { | |
initDimensions(); | |
} | |
void initDimensions(); | |
Iterable<Iterable<bool>> get layout; | |
} | |
class RLEParser extends FileParser { | |
static final RegExp regex = RegExp(r"(?<count>\d*)(?<status>[b|o])"); | |
RLEParser(String filename) : super(filename); | |
void initDimensions() { | |
for (final String line in lines) { | |
if (!line.startsWith("x")) continue; | |
List<String> parts = line.split(","); | |
final String x = parts [0].trim(); | |
final String y = parts [1].trim(); | |
final String x_value = x.split("=") [1].trim(); | |
final String y_value = y.split("=") [1].trim(); | |
Position.columns = int.parse(x_value); | |
Position.rows = int.parse(y_value); | |
} | |
} | |
Iterable<bool> parseLine(String line) sync* { | |
final Iterable<RegExpMatch> matches = regex.allMatches(line); | |
for (final RegExpMatch match in matches) { | |
final String countString = match.namedGroup("count"); | |
final int count = int.parse(countString.isEmpty ? "1" : countString); | |
final String status = match.namedGroup("status"); | |
for (int _ = 0; _ < count; _++) { | |
yield status == "o"; | |
} | |
} | |
} | |
Iterable<Iterable<bool>> get layout sync* { | |
int index = 0; | |
for (;index < lines.length; index++) { | |
final String line = lines[index]; | |
if (line.startsWith("x") || line.startsWith("#")) continue; | |
break; | |
} | |
final String contents = lines.sublist(index).join(""); | |
for (final String line in contents.split("\$")) { | |
yield parseLine(line); | |
} | |
} | |
} | |
class Board { | |
List<List<Cell>> board; | |
Board(this.board); | |
Board.empty() { | |
board = emptyBoard; | |
} | |
Board.fromPositions(List<Position> positions) { | |
board = emptyBoard; | |
for (final Position position in positions) { | |
this [position].isAlive = true; | |
} | |
} | |
Board.fromList(List<List<int>> data) { | |
board = emptyBoard; | |
for (int rowIndex = 0; rowIndex < data.length; rowIndex++) { | |
for (final int index in data [rowIndex]) { | |
final int columnIndex = index % Position.columns; | |
final Position position = Position(rowIndex, columnIndex); | |
this [position].isAlive = true; | |
} | |
} | |
} | |
Board.fromRLE(String path) { | |
final RLEParser parser = RLEParser(path); | |
board = emptyBoard; | |
int rowIndex = 0; | |
for (final Iterable<bool> row in parser.layout) { | |
int columnIndex = 0; | |
for (final bool status in row) { | |
final Position position = Position(rowIndex, columnIndex); | |
this [position].isAlive = status; | |
columnIndex++; | |
} | |
rowIndex++; | |
} | |
} | |
List<List<Cell>> get emptyBoard => List.generate( | |
Position.rows, | |
(int row) => List.generate( | |
Position.columns, | |
(int column) => Cell( | |
Position(row, column), | |
this, | |
) | |
) | |
); | |
void forEach(void Function(Cell) func) { | |
for (int row = 0; row < Position.rows; row++) { | |
for (int column = 0; column < Position.columns; column++) { | |
final Position position = Position(row, column); | |
func(this [position]); | |
} | |
} | |
} | |
void update() { | |
forEach((Cell cell) => cell.prepareNextTick()); | |
forEach((Cell cell) => cell.update()); | |
} | |
bool contains(Position position) => | |
position.row < Position.rows | |
&& position.row >= 0 | |
&& position.col < Position.columns | |
&& position.col >= 0; | |
Cell operator [](Position position) => | |
board [position.row] [position.col]; | |
@override | |
String toString() { | |
String result = ""; | |
for (final List<Cell> row in board) { | |
String rowString = ""; | |
for (final Cell cell in row) { | |
rowString += cell.toString() + " "; | |
} | |
result += rowString + "\n"; | |
} | |
return result; | |
} | |
void simulate(int period) async { | |
for (int t = 0; t < period; t++) { | |
clearScreen(); | |
print(this); | |
print("t = $t"); | |
await Future.delayed(Duration(milliseconds: 10)); | |
update(); | |
} | |
} | |
} | |
void clearScreen() => print("\x1B[2J\x1B[0;0H"); | |
void main(List<String> args) async { | |
final Board gliderGun = Board.fromRLE("glider_gun.rle"); | |
int period = int.parse(args [0]); | |
//print(gliderGun); | |
gliderGun.simulate(period); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#N Gosper glider gun | |
#C This was the first gun discovered. | |
#C As its name suggests, it was discovered by Bill Gosper. | |
#C I modified the x and y values to allow the gliders to travel a bit. | |
x = 40, y = 30, rule = B3/S23 | |
24bo$22bobo$12b2o6b2o12b2o$11bo3bo4b2o12b2o$2o8bo5bo3b2o$2o8bo3bob2o4b | |
obo$10bo5bo7bo$11bo3bo$12b2o! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Make sure you include the glider_gun.rle file (or change the filename in the code)
Video demo: