Tom Wyant
and 1 contributors

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_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

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

This method returns the state of the grid, as a reference to an array of array references. The contents of the inner arrays represent the states of the cells, with a true value representing a "living" cell and a false value representing a "dead" cell.

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

 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().

SERIALIZATION AND DESERIALIZATION

This package supports the Types::Serialiser object serialisation protocol. That is, it has FREEZE() and THAW() methods compatible with this specification.

Note that, to the best of my knowledge, only CBOR::XS|CBOR::XS supports this out of the box. Sereal::Encoder requires at least version 2, with the freeze_callbacks option set. Whether JSON supports it or not depends on which JSON module you are using (and which version of that module you have), and generally you have to turn on allow_tags() (or something similar) to make it work, and you get non-standard JSON as a result.

SEE ALSO

Game::Life.

SUPPORT

Support is by the author. Please file bug reports at http://rt.cpan.org, or in electronic mail to the author.

AUTHOR

Thomas R. Wyant, III wyant at cpan dot org

COPYRIGHT AND LICENSE

Copyright (C) 2019 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.