Skip to content

Instantly share code, notes, and snippets.

@langthom
Created July 27, 2016 13:08
Show Gist options
  • Save langthom/cab1285aa25a9656536d8b41c70e41c4 to your computer and use it in GitHub Desktop.
Save langthom/cab1285aa25a9656536d8b41c70e41c4 to your computer and use it in GitHub Desktop.
Conway's "Game Of Life" in Perl
#!/usr/bin/perl
# A damn nice "Conway's game of life".
# Taken from 'Coding for fun with Python' and translated into Perl.
#
# Thomas Lang, (c) 2016.
use strict;
use warnings;
use OpenGL qw/ :all/;
# Configuration
my $TITLE = "Game of Life";
my $LIVING_SPACE_WIDTH = 100;
my $LIVING_SPACE_HEIGHT = 60;
my $CREATURE_SIZE = 10; # Size of a cell.
my $WINDOW_WIDTH = $LIVING_SPACE_WIDTH * $CREATURE_SIZE;
my $WINDOW_HEIGHT = $LIVING_SPACE_HEIGHT * $CREATURE_SIZE;
sub setColor { glColor3f(1.0, 0.0, 0.0); } # Color of cells.
#-----------------------------------------------------------
# Implementation
#-----------------------------------------------------------
# Logic:
my @livingSpace; # Global space where all cells lie.
# Initializes the living space randomly with either '0' or '1000'.
sub initLivingSpace {
map {my $row = $_; map { $livingSpace[$row][$_] = int(rand(2)) == 1 ? 1000 : 0 } 0 .. ($LIVING_SPACE_WIDTH - 1)} 0 .. ($LIVING_SPACE_HEIGHT - 1);
}
# Check if cell is alive or not.
sub isAlive {
my ($column, $row) = @_;
$livingSpace[$row][$column] == 1000;
}
# Counts all alive neighbours, on a Torus.
sub getNeighbourCount {
my ($x, $y) = @_;
my $count = 0;
my $x_pos = ($x + 1) % $LIVING_SPACE_WIDTH;
my $y_pos = ($y + 1) % $LIVING_SPACE_HEIGHT;
$count += isAlive($x , $y_pos);
$count += isAlive($x_pos, $y_pos);
$count += isAlive($x_pos, $y );
$count += isAlive($x_pos, $y - 1);
$count += isAlive($x , $y - 1);
$count += isAlive($x - 1, $y - 1);
$count += isAlive($x - 1, $y );
$count += isAlive($x - 1, $y_pos);
$count
}
# Gets the next generation.
sub calcNextGeneration {
my @neighbourCount;
# Gets the amount of alive neighbours for each point.
map {my $row = $_; map { $neighbourCount[$row][$_] = getNeighbourCount($_, $row) } 0 .. ($LIVING_SPACE_WIDTH - 1)} 0 .. ($LIVING_SPACE_HEIGHT - 1);
foreach my $column (0 .. ($LIVING_SPACE_WIDTH-1)) {
foreach my $row (0 .. ($LIVING_SPACE_HEIGHT-1)) {
my $_cnt = $neighbourCount[$row][$column];
if ($_cnt >= 2 && $_cnt <= 3) {
if ($_cnt == 3) {
# This point comes alive, so it has a value of 1000.
$livingSpace[$row][$column] = 1000;
}
} else {
# This cell dies slowly, so just decrease the value by 10 percent.
$livingSpace[$row][$column] /= 1.1;
}
# If the cell is too weak, it finally dies.
if ($livingSpace[$row][$column] < 200) { $livingSpace[$row][$column] = 0; }
}
}
}
#----------
# Graphics:
# Draws the entire world as small squares.
sub drawLivingSpace {
my $OFF = $CREATURE_SIZE - 1.0;
glBegin(GL_QUADS);
for my $column (0 .. ($LIVING_SPACE_WIDTH-1)) {
for my $row (0 .. ($LIVING_SPACE_HEIGHT-1)) {
my $health = $livingSpace[$row][$column] / 1000.0; # How much alive is the cell?
glColor3f($health, 0.0, 0.0); # Suitable color (the darker, the weaker)
my $x = $column * $CREATURE_SIZE; # Stretch point to square
my $y = $row * $CREATURE_SIZE;
glVertex3f($x , $y , 0.0);
glVertex3f($x + $OFF, $y , 0.0);
glVertex3f($x + $OFF, $y + $OFF, 0.0);
glVertex3f($x , $y + $OFF, 0.0);
}
}
glEnd;
}
sub display {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0, 0.0, 3.0);
setColor; # Set configurable colour.
drawLivingSpace; # Draw world.
calcNextGeneration; # Next generation.
glutSwapBuffers; # Make drawings visible (GLUT is double buffered).
glutPostRedisplay; # Notify for redrawing.
}
sub resize {
my ($width, $height) = @_;
if ($height == 0) { $height = 1; }
glViewport(0, 0, $width, $height);
glMatrixMode(GL_PROJECTION); # parallel projection
glLoadIdentity(); # revert all previous changes
# Change coordinate system:
#
# Assume:
# * CREATURE_SIZE = 10
# * WINDOW_WIDTH = 1000
# * WINDOW_HEIGHT = 600
#
# This gives the following window:
#
# (-10, -10)-----------------------(1010, -10)
# | |
# | |
# | |
# | |
# (-10, 610)-----------------------(1010, 610)
#
# So, we have a uniform space of '10' on each side.
glOrtho(-$CREATURE_SIZE,
$WINDOW_WIDTH + $CREATURE_SIZE,
$WINDOW_HEIGHT + $CREATURE_SIZE,
-$CREATURE_SIZE,
-6.0,
0.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
sub init {
glClearColor(0.0, 0.0, 0.0, 0.0);
}
#-----------------------------------------------------------
# Main entry point
glutInit;
glutInitWindowSize $WINDOW_WIDTH, $WINDOW_HEIGHT;
glutInitWindowPosition 180, 80; # nice positioning on my 13" notebook
glutCreateWindow $TITLE;
resize $WINDOW_WIDTH, $WINDOW_HEIGHT;
init;
initLivingSpace;
glutDisplayFunc(\&display);
glutMainLoop;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment