NAME

Game::Life::Faster - Plays John Horton Conway's Game of Life

SYNOPSIS

use Game::Life::Faster;
my $game = Game::Life::Faster->new( 20 );
$game->place_points( 10, 10, [
    [ 1, 1, 1 ],
    [ 1, 0, 0 ],
    [ 0, 1, 0 ],
 ] );
 for ( 1 .. 20 ) {
     print scalar $game->get_text_grid(), "\n\n";
     $game->process();
 }

DESCRIPTION

This Perl package is yet another implementation of John Horton Conway's Game of Life. This "game" takes place on a rectangular grid, each of whose cells is considered "living" or "dead". The grid is seeded with an initial population, and then the rules are iterated. In each iteration cells change state based on their current state and how many of the 8 adjacent cells (orthogonally or diagonally) are "living".

In Conway's original formulation, the rules were that a "dead" cell becomes alive if it has exactly two living neighbors, and a "living" cell becomes "dead" unless it has two or three living neighbors.

This implementation was inspired by Game::Life, and is intended to be reasonably compatible with its API. But the internals have been tweaked in a number of ways in order to get better performance, particularly on large but mostly-empty grids.

METHODS

General note: In all methods that specify $x-$y coordinates, the $x is the row number (zero-based) and the $y is the column number (also zero-based).

This class supports the following public methods:

new

my $life = Game::Life::Faster->new( $size, $breed, $live )

This static method creates a new instance of the game. The arguments are:

$size

This specifies the size of the grid. It must be either a positive integer (which creates a square grid) or a reference to an array containing two positive integers (which creates a rectangular grid of the given width and height). Note that this means we specify number of columns before number of rows, which is inconsistent with the General note above, but is consistent with Game::Life.

The default is 100.

$breed

This specifies the number of neighbors a "dead" cell needs to become "living". It must be a reference to an array of non-negative integers; the cell will become "living" if its number of neighbors appears in the array. Order is not important.

The default is [ 3 ].

$live

This specifies the number of neighbors a "living" cell needs to remain "living". It must be a reference to an array of non-negative integers; the cell will remain "living" if its number of neighbors appears in the array. Order is not important.

clear

This method clears the grid, setting all cells to "dead." It returns its invocant.

This method is an extension to Game::Life.

get_active_grid_coord

my $coord = $life->get_active_text_grid();

This method returns the coordinates of the bounding rectangle for all active points in the grid -- that is, all whose value changed in the most-recent iteration (if any) or whose values were manually changed since the most-recent iteration. An exception is thrown if there are no active points.

The return is a reference to an array containing the minimum and maximum X coordinate followed by the minimum and maximum Y coordinate; that is:

[ $min_x, $max_x, $min_y, $max_y ]

Note that these intervals are closed on both ends. To iterate over the active rows you would specify $min_x .. $max_x.

get_active_text_grid

print $life->get_active_text_grid( $living, $dead )

This convenience method returns the result of

$life->get_text_grid( $living, $dead,
    $life->get_active_grid_coord() )

The arguments are the character to represent an occupied cell and the character to represent an empty cell.

get_breeding_rules

use Data::Dumper;
print Dumper( [ $self->get_breeding_rules() ] );

This method returns the breeding rule, as specified in the $breed argument to new(), but as an array rather than an array reference.

Note that this method always returns the data in ascending order. The corresponding Game::Life method returns them in the originally-specified order.

get_grid

my $grid = $life->get_grid( $coord )

This method returns the grid as a reference to an array of array references. The argument is a reference to the minimum and maximum X and Y coordinates:

[ $min_x, $max_x, $min_y, $max_y ]

If the argument is omitted you get the entire grid.

get_grid_coord

my $coord = $life->get_grid_coord();

This method returns the coordinates of the bounding rectangle for the entire grid.

The return is a reference to an array containing the minimum and maximum X coordinate followed by the minimum and maximum Y coordinate. Assuming the size specified when the object was created was

[ $size_x, $size_y ]

the return will be

[ 0, $size_x - 1, 0, $size_y - 1 ]

get_living_rules

use Data::Dumper;
print Dumper( [ $self->get_living_rules() ] );

This method returns the living rule, as specified in the $breed argument to new(), but as an array rather than an array reference.

Note that this method always returns the data in ascending order. The corresponding Game::Life method returns them in the originally-specified order.

get_rule

use Data::Dumper;
print "'$rule' rule is ", Dump( [ $life->get_rule( $rule ) ] );

This method returns the rule specified by the $rule argument, which must be either 'breed' or 'live'. Note that in contrast to set_rule(), this method returns an array rather than an array reference.

Note that this method always returns the data in ascending order. The corresponding Game::Life method returns them in the originally-specified order.

This method is an extension to Game::Life.

get_text_grid

print "$_\n" for $life->get_text_grid( $living, $dead );

This method returns an array of strings representing the state of the grid. The arguments are:

$living

This is the character used to represent a "living" cell.

The default is 'X'.

$dead

This is the character used to represent a "dead" cell.

The default is '.'.

As an incompatible change to the same-named method of Game::Life, if called in scalar context this method returns a single string representing the entire grid.

get_used_grid_coord

my $coord = $life->get_used_grid_coord()

This method returns the coordinates of the bounding rectangle for all occupied points in the grid.

The return is a reference to an array containing the minimum and maximum X coordinate followed by the minimum and maximum Y coordinate; that is:

[ $min_x, $max_x, $min_y, $max_y ]

Note that these intervals are closed on both ends. To iterate over the active rows you would specify $min_x .. $max_x.

get_used_text_grid

my ( $x, $y, $grid ) = $life->get_used_text_grid()
print "${grid}at row $x column $y\n"

This convenience method returns the living portion of the grid as text. Specifically, the returns are the number of the first row that contains a living cell, the number of the column that contains the first living cell, and the text grid with each line "\n"-terminated.

If there are no living cells, nothing is returned.

If called in scalar context you get the living portion of the grid.

get_used_grid

use Data::Dumper;
print Dumper( $life->get_used_grid() );

This method is similar to get_grid(), but only returns cells that have actually been assigned a value, or acquired a value in the course of processing. Cells that have never had a value are represented by undef. Trailing undefs in a row are suppressed. Rows consisting only of unused cells are represented by undef, not [], and trailing undef rows are also suppressed.

place_points

$life->place_points( $x, $y, $array );

This method populates a portion of the grid whose top left corner is specified by $x and $y with the state information found in $array. This is a reference to an array of array references. Each value of the inner array represents the state of the corresponding cell, with a true value representing "living," and a false value representing "dead."

As an incompatible change to the same-named method of Game::Life, points whose value is undef are ignored.

place_text_points

$life->place_text_points( $x, $y, $living, @array );

This method populates a portion of the grid whose top left corner is specified by $x and $y with the state information found in the text of @array, one row per element. Characters in @array that match the character in $living cause the corresponding cells to be made "living." All other characters cause the cell to be made "dead." This method interprets the strings in @array as new state

As an incompatible change to the same-named method of Game::Life, if @array contains exactly one element and that element contains new line characters, it is split on new lines, allowing something like

$life->place_text_points( 0, 0, 'X', <<'EOD' );
.X.
..X
XXX
EOD

The heavy lifting is done by set_point_state().

process

$life->process( $iterations );

This method runs the game for the specified number of iterations, which defaults to 1.

As an incompatible change to the same-named method of Game::Life, the number of points that actually changed state is returned. If $iterations is greater than 1, the return represents the last iteration. The corresponding Game::Life method does not have an explicit return.

set_point

$life->set_point( $x, $y );

This method sets the state of the point at position $x, $y of the grid to "living." It returns a true value.

This method is a wrapper for set_point_state(). Because of this, it is fatal to attempt to set a point outside the grid.

set_point_state

$life->set_point_state( $x, $y, $state );

This method sets the state of the point at position $x, $y of the grid to $state. An undef value is ignored; a true value of $state sets the cell "living;" a false value sets it "dead." It returns the state.

An exception will be raised if you attempt to set a point "live" which is outside the grid.

This method is an extension to Game::Life.

set_rule

$life->set_rule( $kind, $rule );

This method sets the breed or live rules, which govern the transition from "dead" to "living" and "living" to "dead" respectively. The arguments are:

$kind

This argument specifies the kind of rule being set, and must be either 'breed' or 'live'.

$rule

This argument specifies the actual rule. It must be either an array of non-negative integers specifying the number of neighbors that must exist to apply this rule, or a false value to specify the default.

The defaults depend on the value of $kind as follows:

breed => [ 3 ]
live => [ 2, 3 ]

This method is an extension to Game::Life.

set_rules

$life->set_rules( $breed, $live );

This method sets the breed and live rules from arguments $breed and $live respectively. It is implemented in terms of set_rule().

toggle_point

$life->toggle_point( $x, $y );

This method toggles the state of the point at position $x, $y of the grid. That is, if it was "dead" it becomes "living," and vice versa. It returns the a true value if the cell became "living," and a false one otherwise.

This method is a wrapper for set_point_state(). Because of this, it is fatal to attempt to toggle a point outside the grid.

unset_point

$life->unset_point( $x, $y );

This method sets the state of the point at position $x, $y of the grid to "dead." It returns a false value.

This method is a wrapper for set_point_state().

SEE ALSO

Game::Life.

SUPPORT

Support is by the author. Please file bug reports at https://github.com/trwyant/perl-Game-Life-Faster/issues, or in electronic mail to the author.

AUTHOR

Thomas R. Wyant, III wyant at cpan dot org

COPYRIGHT AND LICENSE

Copyright (C) 2019-2021 by Thomas R. Wyant, III

This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES.

This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose.