Game of Life

Any computer scientist probably knows Conway's Game of Life. Anyone reading this blog is probably a computer scientist. Thus, this page probably needs no further introduction.

For the mildly curious, I've implemented a very simple client in D using ArcLib (a 2D game library powered by Derelict, which is powered by OpenGL/SDL). It should compile on all the platforms supported by Derelict, which I daresay should cover most everyone.

I recommend using this tutorial for instructions on how to build ArcLib programs using DSSS.

The Source

module main;

import arc.input;
import arc.time;
import arc.window;
import arc.font;
import arc.math.point;
import arc.math.size;
import arc.draw.color;
import arc.draw.shape;
import std.random;
import std.stdio;

const int rows = 90, cols = 144;
const int grid_size = 5; // pixels per grid edge
const int sprinkle_percent = 10;

bool[cols][rows] grid;
int steps = 0, fps = 12;

/+ SHAPES +/

struct Shape
{
  int width, height;
  char[][] shape;
}

enum Shapes { BLOCK, GLIDER };
Shape[] cool_shapes = [
  { width:2, height:2, // 0: Boat
    shape:[ "xx",
            "xx"  ] },
  { width:3, height:3, // 1: Glider
    shape:[ " x ",
            "  x",
            "xxx"  ] }
];

/+ HELPER FUNCTIONS +/

int wrap_row(int row) { return (row + rows) % rows; }
int wrap_col(int col) { return (col + cols) % cols; }

int live_neighbors(int row, int col)
{
  int neighbors = 0;
  for(int r = -1; r <= 1; r++)
    for(int c = -1; c <= 1; c++)
      if(r != 0 || c != 0)
        neighbors += grid[wrap_row(row+r)][wrap_col(col+c)] ? 1 : 0;
  return neighbors;
}

void life_step()
{
  bool[cols][rows] next;

  for(int row = 0; row < rows; row++)
    for(int col = 0; col < cols; col++)
    {
      int neighbors = live_neighbors(row, col);

      if(neighbors < 2 || neighbors > 3)
        next[row][col] = false;
      else if(neighbors == 3)
        next[row][col] = true;
      else
        next[row][col] = grid[row][col];
    }

  for(int row = 0; row < rows; row++)
    for(int col = 0; col < cols; col++)
      grid[row][col] = next[row][col];
}

void draw(int row, int col) { grid[wrap_row(row)][wrap_col(col)] = true; }

void draw_shape(int row, int col, Shape shape)
{
  for(int r = 0; r < shape.width; r++)
    for(int c = 0; c < shape.height; c++)
      if(shape.shape[r][c] == 'x')
        draw(row+r, col+c);
}

void draw_random_shape()
{
  int shape = rand() % (Shapes.max + 1);
  draw_shape(rand() % rows, rand() % cols, cool_shapes[shape]);
}

void sprinkle_life()
{
  // (rows * cols) * (sprinkle_percent / 100)
  int quota = rows * cols * sprinkle_percent / 100;
  for(int i = 0; i < quota; i++)
    draw(rand() % rows, rand() % cols);
}

/+ GAME LOOPS +/

int main()
{
  arc.window.open("Life (keys: left, right, space, q, x)", cols*grid_size, rows*grid_size, 0 );
  arc.input.open();
  arc.time.open();

  while(!arc.input.keyDown(ARC_QUIT))
  {
    arc.input.process();
    arc.time.process();

    // draw
    arc.window.clear();
    for(int row = 0; row < rows; row++)
      for(int col = 0; col < cols; col++)
        drawRectangle(Point(col*grid_size, row*grid_size),
            Size(grid_size, grid_size), grid[row][col] ? Color.White :
            Color.Black, true);

    // paint
    arc.window.swap();

    if(arc.input.keyReleased(ARC_SPACE))
      sprinkle_life();
    if(arc.input.keyReleased(ARC_x))
      draw_random_shape();
    if(arc.input.keyReleased(ARC_RIGHT))
      fps += 3;
    if(arc.input.keyReleased(ARC_LEFT))
      fps = (fps - 3 > 1) ? fps - 3 : 1;
    if(arc.input.keyReleased(ARC_q))
      break;

    // progress life
    life_step();
    steps++;

    limitFPS(fps);
  }

  arc.window.close();
  return 0;
}

Comments

Click here to view the comments on this post.