Game of Life

[email protected] (Ala Qumsieh)
Newsgroups perl.ai
Message-ID <[email protected]>
Hey all,

I hacked a very quick Perl/Tk version of Conway's Game of Life and thought
some of you might enjoy it.

--Ala

#!/usr/bin/perl -w

use strict;
use Tk;

use vars qw/$repeat/;

my $size = 20;

#############################
# START OF USER CUSTOMIZABLE VARIABLES
#############################

# Size of the grid.
my $rows = 30;
my $cols = 30;

# The rules. An anonymous array of 2 arrays.
# The rules are like this:
# [
#   [ creation list  ]
#   [ survival list  ]
# ]
#
# The creation list is a list of numbers from 0 to 8 inclusive.
# If the sum of living neighbours of any cell is in this list
# then this cell springs up to life in the next generation.
#
# The survival list is a list of numbers from 0 to 8 inclusive.
# If the sum of living neighbours of any cell is in this list
# and the cell is alive, then it will survive to the next generation.
#
# All other cells die in the next generation.

my $rules = [
	     [3],
	     [2, 3],
	    ];

# Anonymous list or (row, column) coordinates of the starting
# pattern.

# Some boring test shape.
#  my $start = [
#  	     [4,3],
#  	     [4,4],
#  	     [4,5],
#  	     [4,6],
#  	    ];

# The infamous Glider!!
my $start = [
	     [1,2],
	     [2,3],
	     [3,1],
	     [3,2],
	     [3,3],
	    ];
#############################
# START OF USER CUSTOMIZABLE VARIABLES
#############################

drawGUI($rows, $cols, $size, $rules, $start);

MainLoop;

sub drawGUI {
    my ($rows,
	$cols,
	$size,
	$rules,
	$start,
       ) = @_;

    my $mw = new MainWindow;
    my $c  = $mw->Canvas(-bg     => 'black',
			 -width  => $cols * $size,
			 -height => $rows * $size,
			)->pack(qw/-side top/);

    $mw->bind('<Any-Enter>', sub { $c->Tk::focus });

    my $array = [];
    for my $i (0 .. $rows - 1) {
	$array->[$i] = [(0) x $cols];
    }

    # Draw the grid.
    for my $i (0 .. $rows) {
	$c->createLine(0, $i * $size,
		       $cols * $size, $i * $size,
		       -fill => 'white',
		      );
    }

    for my $i (0 .. $cols) {
	$c->createLine($i * $size, 0,
		       $i * $size, $rows * $size,
		       -fill => 'white',
		      );
    }

    my $frame = $mw->Frame->pack(qw/-side top -expand 1
				 -fill both -padx 10 -pady 10/);

    $frame->Button(
		   -text    => 'Run',
		   -height  => 3,
		   -command => [\&run, $array, $rules, $rows,
				$cols, $size, $c],
		  )->pack(qw/side left -fill both -expand 1/);

    $frame->Button(
		   -text    => 'Exit',
		   -command => sub { exit },
		  )->pack(qw/side left -fill both -expand 1/);

    # Fill in the starting pattern.
    for my $x (@$start) {
	fill($c, $size, @$x, $array);
    }
}

sub run {
    my ($array, $rules, $rows, $cols, $size, $c) = @_;

    $repeat = $c->repeat(200, [\&step, $array, $rules,
			       $rows, $cols, $size, $c]);
}

sub step {
    my ($array, $rules, $rows, $cols, $size, $c) = @_;

    my ($fill, $unfill) = calc(
			       $array,
			       $rules,
			       $rows,
			       $cols,
			      );

    if (!@$unfill and !@$fill) {
	print "Done!\n";
	$c->afterCancel($repeat);
    }

    fill  ($c, $size, @$_, $array) for @$fill;
    unfill($c, @$_, $array) for @$unfill;
    $c->update;
}

sub fill {
    my ($c,
	$size,
	$row,
	$col,
	$array,
       ) = @_;

    $array->[$row][$col] = 1;
    $c->createOval(
		   $col * $size, $row * $size,
		   ($col + 1) * $size, ($row + 1) * $size,
		   -fill => 'red',
		   -tags => "$row-$col",
		  );
}

sub unfill {
    my ($c, $row, $col, $array) = @_;

    $c->delete("$row-$col");
    $array->[$row][$col] = 0;
}

sub calc {
    my ($array, $rules, $rows, $cols) = @_;

    my (@fill, @unfill);

    for my $r (0 .. $rows - 1) {
	for my $c (0 .. $cols - 1) {
	    # Look at the neighbours.
	    my $sum = 0;

	    for my $n (
		       [$r - 1, $c - 1],
		       [$r - 1, $c    ],
		       [$r - 1, $c + 1],
		       [$r    , $c - 1],
		       [$r    , $c + 1],
		       [$r + 1, $c - 1],
		       [$r + 1, $c    ],
		       [$r + 1, $c + 1],
		      ) {

		$sum += $array->[$n->[0]][$n->[1]] if
		    $n    ->[0] >= 0 && $n->[0] < $rows &&
			$n->[1] >= 0 && $n->[1] < $cols;
	    }

	    if ($array->[$r][$c]) {
		# will it survive?
		unless (grep {$_ == $sum} @{$rules->[1]}) {
		    push @unfill => [$r, $c];
		}
	    } else {
		# will it get born?
		if (grep {$_ == $sum} @{$rules->[0]}) {
		    push @fill => [$r, $c];
		}
	    }
	}
    }

    return (\@fill, \@unfill);
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.