Created
July 27, 2016 13:08
-
-
Save langthom/cab1285aa25a9656536d8b41c70e41c4 to your computer and use it in GitHub Desktop.
Conway's "Game Of Life" in Perl
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
#!/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