I recently started to try to copy C++ code of Conway's Game of Life that I wrote into Perl, and I copied it almost word-for-word. However, the output of the C++ code is vastly different from the Perl one. The C++ code runs perfectly, but the Perl code gives strange behavior. Anyone who's ever seen the Game of Life should see that the following state of the game is strange:
[][] [] [] [] [][][] [] [] [] [] [] [] [][] []
[] [] [] [] [] [] [] [][] [] [] [][] [] [] []
[] [][] [] [] [] [] [] [][][][] [] [] [][][][][] [] []
[] [][] [] [] 开发者_JAVA百科 [] [][][][] [] [] [] [] [][] []
[] [][] [] [] [][][] [] [] [] [] [][] [] []
[] [] [][][][][] [] [] [][] [][] [] [][] [] [][][] []
[] [] [] [][][] [][] [][][] [][][] [] []
[] [] [][][][][][][][][][] [][][][][] [] []
[][] [] [][][][][][][] [][][] [][] [][] []
[] [] [][][][] [] [] [] [] [][][] []
[][] [] [] [] [] [] [][] [] [][]
[] [] [][] [] [][][][] [][] [] []
[][] [] [][][][][] [][][] [][][][][][][][] [][]
[] [] [] [] [] [][][][] [][]
[][] [] [] [] [] [] [] [][][] [][][][][][][][]
[][] [] [] [] [] [] [] [][][][] [] [][] [][]
[] [][] [] [][] [] [] [][][][] [][][]
[] [][][][] [] [] [] [] [] [] [] [] [] [] [] []
[][][] [][][] [] [][] [][] [] [] [] []
[] [][][] [] [][] [][] [] [] [] [][] []
[][] [] [] [][][][][][] [][] [] [] []
[] [] [][][][][][][][][] [] [] [] [] [][][]
[][] [][][] [] [] [][] [] [] [] [][] [] [] []
[] [] [] [][] [][][][][][][] [] [][] [] [] [] []
Each []
represents a living cell, while any two white spaces represent a dead cell. The weird part is the horizontal and vertical lines that are present throughout many attempts of running the code. I've yet to see expected behavior (gliders, oscillators, et cetera).
My code is as follows; I'd appreciate any help/clarification. Thanks in advance!
#!/usr/bin/perl
use warnings;
use strict;
use Curses;
use Time::HiRes 'usleep';
my $iterations = 100;
$iterations = $ARGV[0] if @ARGV;
initscr;
getmaxyx(my $rows, my $columns);
$columns = int($columns / 2);
my ($i, $j);
my @initial_state;
foreach $i (0 .. $rows) {
foreach $j (0 .. $columns) {
$initial_state[$i][$j] = int rand(2);
}
}
my @current_state = @initial_state;
my @next_state;
my $iteration;
my ($up, $down, $right, $left);
my $adjacent_cells;
foreach $iteration (0 .. $iterations) {
foreach $i (0 .. $rows) {
foreach $j (0 .. $columns) {
$up = ($i + 1) % $rows;
$down = ($i - 1) % $rows;
$right = ($j + 1) % $columns;
$left = ($j - 1) % $columns;
$adjacent_cells = $current_state[$i][$right]
+ $current_state[$i][$left]
+ $current_state[$up][$right]
+ $current_state[$up][$left]
+ $current_state[$down][$right]
+ $current_state[$down][$left]
+ $current_state[$up][$j]
+ $current_state[$down][$j];
if ( $current_state[$i][$j] ) {
$next_state[$i][$j] = $adjacent_cells == 2 || $adjacent_cells == 3 ? 1 : 0;
addstr($i, 2*$j, '[]');
}
else {
$next_state[$i][$j] = $adjacent_cells == 3 ? 1 : 0;
addstr($i, 2*$j, ' ');
}
}
}
@current_state = @next_state unless $iteration == $iterations;
usleep 10000;
refresh();
}
getch();
endwin();
Aha, found it.
In Perl, a two-dimensional array is really an array of references to arrays. When you do @current_state = @next_state
, it only makes a shallow copy of the outer array, so that both arrays end up containing references to the same row arrays.
In your case, the fix is simple: just set @next_state = ()
at the beginning of the loop. That way, perl will autovivify a new set of row arrays for you.
(Also, you probably want to fix your loops so that you're iterating from 0 to $rows-1
and $columns-1
instead of to $rows
and $columns
.)
After the first iteration, changing $next_state[$i][$j]
also changes $current_state[$i][$j]
because $next_state[$i]
contains the same reference as $current_state[$i]
.
Don't forget that 2d arrays are really Arrays of Arrays, or to be specific, Array of References to Arrays.
You need to make a deep copy instead of a shallow copy.
@current_state = map { [ @$_ ] } @next_state;
The whole program, cleaned up some:
#!/usr/bin/perl
use strict;
use warnings;
use Curses;
use Time::HiRes qw( usleep );
use List::Util qw( sum );
my $iterations = $ARGV[0] || 100;
initscr;
getmaxyx(my $rows, my $cols);
$cols = int($cols / 2);
my @initial_state;
for my $i (0 .. $rows-1) {
for my $j (0 .. $cols-1) {
$initial_state[$i][$j] = int rand(2);
}
}
my @current_state = map { [ @$_ ] } @initial_state;
my @next_state;
my $iteration = 0;
for (;;) {
for my $i (0 .. $rows-1) {
for my $j (0 .. $cols-1) {
addstr($i, 2*$j, $current_state[$i][$j] ? '[]' : ' ');
}
}
last if ++$iteration == $iterations;
for my $i (0 .. $rows-1) {
for my $j (0 .. $cols-1) {
my $up = ($i + 1) % $rows;
my $down = ($i - 1) % $rows;
my $right = ($j + 1) % $cols;
my $left = ($j - 1) % $cols;
my $adjacent_cells = sum
$current_state[$i][$right],
$current_state[$i][$left],
$current_state[$up][$right],
$current_state[$up][$left],
$current_state[$down][$right],
$current_state[$down][$left],
$current_state[$up][$j],
$current_state[$down][$j];
if ( $current_state[$i][$j] ) {
$next_state[$i][$j] = ($adjacent_cells == 2 || $adjacent_cells == 3) ? 1 : 0;
}
else {
$next_state[$i][$j] = $adjacent_cells == 3 ? 1 : 0;
}
}
}
@current_state = map { [ @$_ ] } @next_state;
usleep 100000;
refresh();
}
getch();
endwin();
PS - I don't think you should be peeking around the edges.
精彩评论