Last active
August 29, 2015 14:24
-
-
Save dgellow/c14d36b04e1f51663353 to your computer and use it in GitHub Desktop.
Playing with cellular automation
This file contains hidden or 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 os | |
from strutils import `%` | |
import libncurses | |
const | |
sleepTime = 100 | |
charAlive = "0" | |
charDead = "_" | |
nbRow = 30 | |
nbCol = 120 | |
type | |
Cell = tuple | |
x: int | |
y: int | |
alive: bool | |
type | |
Row = array[nbCol, int] | |
type | |
Board = array[nbRow, Row] | |
type | |
Neighbours = array[0..7, Cell] | |
proc getCell(cells: Board, x: int, y: int): Cell | |
iterator items(b: Board): Cell = | |
for y in 0..(nbRow - 1): | |
for x in 0..(nbCol - 1): | |
yield b.getCell(x, y) | |
proc initialize(): void = | |
discard initscr() | |
discard cbreak() | |
discard noecho() | |
proc terminate(): void = | |
discard endwin() | |
proc putCharAt(x: int, y:int, ch: string): void = | |
let | |
x_c = cast[cint](x) | |
y_c = cast[cint](y) | |
discard mvaddstr(y_c, x_c, ch) | |
proc neighbourTop(cells: Board, c: Cell): Cell = | |
cells.getCell(c.x, if c.y == 0: nbRow - 1 | |
else: c.y - 1) | |
proc neighbourBottom(cells: Board, c: Cell): Cell = | |
cells.getCell(c.x, if c.y == nbRow - 1: 0 | |
else: c.y + 1) | |
proc neighbourLeft(cells: Board, c: Cell): Cell = | |
cells.getCell(if c.x == 0: nbCol - 1 | |
else: c.x - 1, | |
c.y) | |
proc neighbourRight(cells: Board, c: Cell): Cell = | |
cells.getCell(if c.x == nbCol - 1: 0 | |
else: c.x + 1, | |
c.y) | |
proc neighbourTopLeft(cells: Board, c: Cell): Cell = | |
cells.getCell(if c.x == 0: nbCol - 1 | |
else: c.x - 1, | |
if c.y == 0: nbRow - 1 | |
else: c.y - 1) | |
proc neighbourTopRight(cells: Board, c: Cell): Cell = | |
cells.getCell(if c.x == nbCol - 1: 0 | |
else: c.x + 1, | |
if c.y == 0: nbRow - 1 | |
else: c.y - 1) | |
proc neighbourBottomLeft(cells: Board, c: Cell): Cell = | |
cells.getCell(if c.x == 0: nbCol - 1 | |
else: c.x - 1, | |
if c.y == nbRow - 1: 0 | |
else: c.y + 1) | |
proc neighbourBottomRight(cells: Board, c: Cell): Cell = | |
cells.getCell(if c.x == nbCol - 1: 0 | |
else: c.x + 1, | |
if c.y == nbRow - 1: 0 | |
else: c.y + 1) | |
proc neighbours(c: Cell, cells: Board): Neighbours = | |
[neighbourTop(cells, c), | |
neighbourBottom(cells, c), | |
neighbourLeft(cells, c), | |
neighbourRight(cells, c), | |
neighbourTopLeft(cells, c), | |
neighbourTopRight(cells, c), | |
neighbourBottomLeft(cells, c), | |
neighbourBottomRight(cells, c)] | |
proc aliveNeighbours(c: Cell, cells: Board): int = | |
var | |
countAlive: int | |
for n in c.neighbours(cells): | |
if n.alive: countAlive += 1 | |
countAlive | |
proc isUnderpopulated(c: Cell, aliveNeighbours: int): bool = | |
aliveNeighbours < 2 | |
proc willSurvive(c: Cell, aliveNeighbours: int): bool = | |
(aliveNeighbours == 2) or (aliveNeighbours == 3) | |
proc isOvercrowded(c: Cell, aliveNeighbours: int): bool = | |
aliveNeighbours > 3 | |
proc willBecomeAlive(c: Cell, aliveNeighbours: int): bool = | |
aliveNeighbours == 3 | |
proc getCell(cells: Board, x: int, y: int): Cell = | |
(x: x, y: y, alive: cells[y][x] == 1) | |
proc draw(c: Cell): void = | |
putCharAt(c.x, c.y, if c.alive: charAlive | |
else: charDead) | |
proc draw(cells: Board): void = | |
for cell in cells: | |
cell.draw() | |
discard refresh() | |
proc applyRules(cells: Board): Board = | |
# Rule 1. Any live cell with fewer than two live neighbours dies, | |
# as if caused by under-population. | |
# | |
# Rule 2. Any live cell with two or three live neighbours lives on | |
# to the next generation. | |
# | |
# Rule 3. Any live cell with more than three live neighbours dies, | |
# as if by overcrowding. | |
# | |
# Rule 4. Any dead cell with exactly three live neighbours becomes | |
# a live cell, as if by reproduction. | |
var | |
n_cells: Board | |
for cell in cells: | |
let | |
alive_n = cell.aliveNeighbours(cells) | |
var | |
cell_state: range[0..1] | |
if cell.isUnderpopulated(alive_n): | |
cell_state = 0 | |
elif cell.willSurvive(alive_n): | |
cell_state = 1 | |
elif cell.isOvercrowded(alive_n): | |
cell_state = 0 | |
elif cell.willBecomeAlive(alive_n): | |
cell_state = 1 | |
n_cells[cell.y][cell.x] = cell_state | |
n_cells | |
proc gameloop(cells: Board): void = | |
var | |
n_cells = cells | |
while true: | |
n_cells = applyRules(n_cells) | |
draw(n_cells) | |
sleep(sleepTime) | |
# | |
# Main | |
# | |
var | |
startingBoard: Board | |
for y in 0..nbRow - 1: | |
for x in 0..nbCol - 1: | |
startingBoard[y][x] = 0 | |
startingBoard[(nbRow / 2).toInt][(nbCol / 2).toInt] = 1 | |
startingBoard[((nbRow / 2) + 1).toInt][(nbCol / 2).toInt] = 1 | |
startingBoard[((nbRow / 2) - 1).toInt][(nbCol / 2).toInt] = 1 | |
initialize() | |
draw(startingBoard) | |
gameloop(startingBoard) | |
terminate() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment