I have been working on creating a basic Conway's Game of Life simulator in Powershell to get more familiar with the language. But my current code is incorrectly counting # of neighboring cells, so the game is not working. I believe my wrapping is correct, from what I can tell, but something seems to be 'up' with my neighbor count.
Here is the code:
function next-gen{
param([System.Array]$origM)
$tmpM = $origM
For($x=0; $x -lt $tmpM.GetUpperBound(0); $x++ ){
For($y=0; $y -lt $tmpM.GetUpperBound(1); $y++){
$neighborCount = getNeighbors $tmpM $x $y
if($neighborCount -lt 2 -OR $neighborCount -gt 3){
$tmpM[$x,$y] = 0
}
elseif($neighborCount -eq 3){
$tmpM[$x, $y] = 1
}
}
}
$Global:origM = $tmpM
}
function getNeighbors{
param(
[System.Array]$g,
[Int]$x,
[Int]$y
)
$newX=0
$newY=0
$count=0
for($newX = -1; $newX -le 1; $newX++){
for($newY = -1; $newY -le 1; $newY++){
if($g[$(wrap $x $newX),$(wrap $y $newY)]){
$count++
}
}
}
return $count
}
function wrap{
param(
[Int]$z,
[Int]$zEdge
)
$z+=$zEdge
If($z -lt 0){
$z += $size
}
ElseIf($z -ge $size){
$z -= $:size
}
return $z
}
function printBoard{
0..$m.GetUpperBound(0) |
% { $dim1=$_; (0..$m.GetUpperBound(1) | % { $m[$dim1, $_] }) -join ' ' }
write-host ""
}
#board is always a square, size represents both x and y
$size = 5
$m = New-Object 'int[,]' ($size, $size)
$m[2,1] = 1
$m[2,2] = 1
$m[2,3] = 1
clear
printBoard
For($x=0; $x -lt 1; $x++){
next-gen $m
printBoard
write-host ""
}
With the board setup in the above demo, the result of should be a blinker:
Gen0
0 0 0 0 0
0 0 0 0 0
0 1 1 1 0
0 0 0 0 0
0 0 0 0 0
Gen1
0 0 0 0 0
0 0 1 0 0
0 0 1 0 0
0 0 1 0 0
0 0 0 0 0
For those unfamiliar, the rules can be found here: Wikipedia - Conway's Game of Life
I added some debugging code (with write-verbose), and found where the error(s) were.
First, you were checking neighbors in the $tmpm
array (which was being updated) rather than the current generation).
Second, you were setting $global:OrigM
when you meant $Global:M
at the end of then next-gen function, but it actually has to be $script:M
, because the variable exists in the script scope, not the global one.
Also, getNeighbors
mistakenly also considered the target position itself as a neighbor instead of just the 8 surrounding positions.
function next-gen{
param([System.Array]$origM)
$size=1+$origM.GetUpperBound(0)
$tmpM = New-Object 'int[,]' ($size, $size)
For($x=0; $x -lt $size; $x++ ){
For($y=0; $y -lt $size; $y++){
$neighborCount = getNeighbors $origm $x $y
if($neighborCount -lt 2 -OR $neighborCount -gt 3){
$tmpM[$x,$y] = 0
write-verbose "Clearing $x,$y"
}
elseif($neighborCount -eq 3 -or $OrigM[$x,$y] -eq 1){
$tmpM[$x, $y] = 1
write-verbose "Setting $x,$y"
}
}
}
$script:M = $tmpM
}
function getNeighbors{
param(
[System.Array]$g,
[Int]$x,
[Int]$y
)
$newX=0
$newY=0
$count=0
for($newX = -1; $newX -le 1; $newX++){
for($newY = -1; $newY -le 1; $newY++){
if($newX -ne 0 -or $newY -ne 0){
$neighborx=wrap $x $newx
$neighborY=wrap $y $newY
write-verbose "x=$x y=$y Nx=$neighborx Ny=$neighborY"
if($g[$neighborx,$neighborY] -eq 1){
write-verbose "Neighbor at $neighborx, $neighborY is Set!"
$count++
}
}
}
}
write-verbose "x=$x y=$y Neighbor count = $count"
return $count
}
function wrap{
param(
[Int]$z,
[Int]$zEdge
)
$z+=$zEdge
If($z -lt 0){
$z += $size
}
ElseIf($z -ge $size){
$z -= $size
}
return $z
}
function printBoard{
0..$m.GetUpperBound(0) |
% { $dim1=$_; (0..$m.GetUpperBound(1) | % { $m[$dim1, $_] }) -join ' ' }
write-host ""
}
#board is always a square, size represents both x and y
$size = 5
$m = New-Object 'int[,]' ($size, $size)
$m[2,1] = 1
$m[2,2] = 1
$m[2,3] = 1
clear
printBoard
For($x=0; $x -lt 1; $x++){
next-gen $m
printBoard
write-host ""
}
P.S. Your bounds checking in the loops in next-gen were off by 1, and I made it start with a clean board rather than re-using the last gen (since you explicitly set each position, it doesn't really matter).