Skip to content

Instantly share code, notes, and snippets.

@dgellow
Last active August 29, 2015 14:24
Show Gist options
  • Save dgellow/c14d36b04e1f51663353 to your computer and use it in GitHub Desktop.
Save dgellow/c14d36b04e1f51663353 to your computer and use it in GitHub Desktop.
Playing with cellular automation
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