#!/usr/bin/perl use strict; use warnings; use Getopt::Long; use Data::Dumper; ######################################################### #I use a hash for my board, store these live/light cell site #for example a key 7-2 mean there is a live cell at 7-2 site on the board #so, first I write a subroutine for light some cells, input a array reference #this array comtain a site list sub light_cell { my $live_cells = shift; my $board = shift; for my $f (@{$live_cells}) { $board->{$f} = 42; } } sub around_cells { my $site = shift; my $size = shift; my ($x,$y) = split '-',$site; my @cells_site; my $first = 42; for my $x_value ($x, $x+1, $x-1) { for my $y_value ($y, $y+1, $y-1) { if ($x_value > 0 and $y_value > 0 and $x_value <= $size and $y_value <= $size) { if ($first) { undef $first; next; } my $value = join '-',$x_value,$y_value; push @cells_site, $value; } } } return \@cells_site; } #then I write some subroutine for computing in every turns #the name of this subroutine is live_cells, it use 'for keys' to traverse all live cell(a key) #and use another subroutine, around_cells, to get cells around this cell #finally create a new hash, use same strategy to store site #add 1 to value of every cells around this live cell,and calculate which cells will be light in next turn(return a array reference) #e.g., if value higher than 4, the cell should die sub live_cells { my $board = shift; my $size = shift; my %life; my @live_cells; for my $f (keys %{$board}) { my $sites = around_cells($f,$size); for my $add (@{$sites}) { if (not exists $life{$add}) { $life{$add} = 1 } else { $life{$add}++ } } } for my $n (keys %life) { if ($life{$n} < 4) { push @white,$n if $life{$n} == 3; push @white,$n if $life{$n} == 2 and exists $world->{$n}; } } return (\@live_cells,\%life); } #this subroutine control next turn coming, input board information into live cells #get live_cells list with array reference #then undef the board hash, use light_cell to set live cell in next turn sub next_turn { my $board = shift; my $size = shift; my ($live_cells,$model) = live_cells($board,$size); undef %{$board}; #darkness light($live_cells,$board); return $model; } #print the board and now number of turns to screen sub show_board { my $size = shift; my $board = shift; for my $x (1...$size) { print $x,"\t"; for my $y (1...$size) { my $allo = join '-',$x,$y; if (exists $board->{$allo}) { print 'O ' } else { print '. ' } } print "\n" } } #only use for debug, you can use this to see how number change in the board, these decide cell live/die sub show_model { my $size = shift; my $world = shift; for my $x (1...$size) { print $x,"\t"; for my $y (1...$size) { my $allo = join '-',$x,$y; if (exists $world->{$allo}) { print $world->{$allo},' ' } else { print '. ' } } print "\n" } print "\n" } #return a random integer sub rand_int { my $region = shift; my $out = int (rand $region); return $out; } #here work for create a random start condition before game start sub random_start_set { my $size = shift; my $number = shift; my %out; for my $f (1...$number) { my ($x,$y) = (rand_int($size),rand_int($size)); my $site = join '-',$x+1,$y+1; if (not exists $out{$site}) { $out{$site} = 42; } else { redo } } my @out = keys %out; print "number: ",scalar @out,"\n"; return \@out; } #compare two hashes, if they are same, return 1 sub hash_key_comp { my ($h1, $h2) = @_; my $equal = (keys %{$h1}) <=> (keys %{$h2}); if ($equal == 0) { for my $f (keys %{$h1}) { if (not exists $h2->{$f}) { return 0; } } } else { return 0 } return 1 } ######################################################### #here I set some options: -s for board size, -t for turns number, -w for live cells before game start #-w set sleep parameter between every turns, let user have time to drink tea:) #-e times of restart game, e.g, -e 5 will let game run 5 times with same set my %world; my $size = 10; my $turns = 5; my $creature = 25; my $speed = 0; my $exp = 1; GetOptions( 'size|s=i' => \$size, 'turn|t=i' => \$turns, 'live|w=i' => \$creature, 'speed|r=i' => \$speed, 'exp|e=i' => \$exp) or die $!; my $log; open $log,'>>','log_file' or die $!; #so now, it is work, and if same pattern exist on board over 3 turns, program will automatically stop. for my $cen (1...$exp) { undef %world; my $live = random_start_set($size,$creature); light_cell($live,\%world); show_board($size,\%world); print "\n\t\t0\n\n"; sleep $speed; my @pre; for (1...$turns) { my $model = twilight(\%world,$size); push @pre, $model; show_world($size,\%world); print "\n\t\t$_\n\n"; if ($#pre > 3) { my $stop = hash_key_comp($pre[0],$pre[-1]); if ($stop == 1) { print $log $cen,"\t",scalar keys %{$pre[0]},"\n"; last; } shift @pre; } sleep $speed; } }