Powershell 康威';s生活|动力地狱
我一直在努力在Powershell中创建一个基本的康威生活游戏模拟器,以便更熟悉该语言。但我当前的代码错误地计算了相邻单元格的数量,因此游戏无法运行。据我所知,我相信我的包装是正确的,但我的邻居伯爵似乎有点不对劲 代码如下:Powershell 康威';s生活|动力地狱,powershell,conways-game-of-life,Powershell,Conways Game Of Life,我一直在努力在Powershell中创建一个基本的康威生活游戏模拟器,以便更熟悉该语言。但我当前的代码错误地计算了相邻单元格的数量,因此游戏无法运行。据我所知,我相信我的包装是正确的,但我的邻居伯爵似乎有点不对劲 代码如下: function next-gen{ param([System.Array]$origM) $tmpM = $origM For($x=0; $x -lt $tmpM.GetUpperBound(0); $x++ ){ For(
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 ""
}
在上面演示的电路板设置中,结果应该是闪烁:
Gen0
第1代
对于那些不熟悉的人,可以在这里找到规则:
我添加了一些调试代码(使用write verbose),并找到了错误所在 首先,您检查的是
$tmpm
阵列(正在更新)中的邻居,而不是当前一代)。
其次,您在下一代函数末尾设置了$global:OrigM
,意思是$global:M
,但实际上它必须是$script:M
,因为变量存在于脚本范围中,而不是全局范围中
此外,getneighbories
还错误地将目标位置本身视为邻居,而不仅仅是周围的8个位置
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 ""
}
另外,你在下一代循环中的边界检查被关闭了1,我让它从一个干净的板开始,而不是重复使用上一代(因为你明确地设置了每个位置,这并不重要)。已经提供了一个工作解决方案,并解释了问题代码的问题
让我用一个重构版本来补充它:
- 通过
(通过引用)变量就地更新线路板变量,以便后续调用按需工作[ref]
- 具有灵活的显示功能,允许指定要显示的代数、是否就地更新显示以及代之间的暂停时间
- 如果按原样运行代码,则会显示5代代码,并在原地更新,暂停1秒。世代之间
- 使用函数名更符合PowerShell的命名约定
- 使代码更加健壮,例如:
- 完全避免使用脚本级(或全局)变量。
- 顺便说一句:全局变量是会话全局变量,在脚本退出后保持在范围内;如果需要“脚本全局”变量,请使用
范围;该脚本作用域(不是全局作用域)是在脚本顶层没有显式作用域的情况下创建的变量的默认值$script:
- 顺便说一句:全局变量是会话全局变量,在脚本退出后保持在范围内;如果需要“脚本全局”变量,请使用
- 对接收电路板的参数使用强类型的
参数[int[,]
- 防止引用未初始化的变量
- 完全避免使用脚本级(或全局)变量。
警告:我们将要看到的是非常黑客和。。。图形的性质。 不,真的,这个代码是草率的,即使是一个新手,所以我道歉 以下是以黑白方式输出到控制台的版本。如果你愿意,它还会随机为你生成一些种子。它不漂亮,我正在工作,所以目前没有时间花在它上面:)希望这个周末github的版本是整洁的
$ErrorActionPreference = 'Stop' # Abort on all unhandled errors.
Set-StrictMode -version 1 # Prevent use of uninitialized variables.
# Given a board as $m_ref, calculates the next generation and assigns it
# back to $m_ref.
function update-generation {
param(
[ref] [int[,]]$m_ref # the by-reference board variable (matrix)
)
# Create a new, all-zero clone of the current board (matrix) to
# receive the new generation.
$m_new = New-Object 'int[,]' ($m_ref.Value.GetLength(0), $m_ref.Value.GetLength(1))
For($x=0; $x -le $m_new.GetUpperBound(0); $x++ ){
For($y=0; $y -le $m_new.GetUpperBound(1); $y++){
# Get the count of live neighbors.
# Note that the *original* matrix must be used to:
# - determine the live neighbors
# - inspect the current state
# because the game rules must be applied *simultaneously*.
$neighborCount = get-LiveNeighborCount $m_ref.Value $x $y
if ($m_ref.Value[$x,$y]) { # currently LIVE cell
# A live cell with 2 or 3 neighbors lives, all others die.
$m_new[$x,$y] = [int] ($neighborCount -eq 2 -or $neighborCount -eq 3)
} else { # curently DEAD cell
# A currently dead cell is resurrected if it has 3 live neighbors.
$m_new[$x,$y] = [int] ($neighborCount -eq 3)
}
$null = $m_new[$x,$y]
}
}
# Assign the new generation to the by-reference board variable.
$m_ref.Value = $m_new
}
# Get the count of live neighbors for board position $x, $y.
function get-LiveNeighborCount{
param(
[int[,]]$m, # the board (matrix)
[Int]$x,
[Int]$y
)
$xLength = $m.GetLength(0)
$yLength = $m.GetLength(1)
$count = 0
for($xOffset = -1; $xOffset -le 1; $xOffset++) {
for($yOffset = -1; $yOffset -le 1; $yOffset++) {
if (-not ($xOffset -eq 0 -and $yOffset -eq 0)) { # skip the position at hand itself
if($m[(get-wrappedIndex $xLength ($x + $xOffset)),(get-wrappedIndex $yLength ($y + $yOffset))]) {
$count++
}
}
}
}
# Output the count.
$count
}
# Given a potentially out-of-bounds index along a dimension of a given length,
# return the wrapped-around-the-edges value.
function get-wrappedIndex{
param(
[Int]$length,
[Int]$index
)
If($index -lt 0){
$index += $length
}
ElseIf($index -ge $length){
$index -= $length
}
# Output the potentially wrapped index.
$index
}
# Print a single generation's board.
function show-board {
param(
[int[,]] $m # the board (matrix)
)
0..$m.GetUpperBound(0) |
ForEach-Object {
$dim1=$_
(0..$m.GetUpperBound(1) | ForEach-Object { $m[$dim1, $_] }) -join ' '
}
}
# Show successive generations.
function show-generations {
param(
[int[,]] $Board,
[uint32] $Count = [uint32]::MaxValue,
[switch] $InPlace,
[int] $MilliSecsToPause
)
# Print the initial board (the 1st generation).
Clear-Host
#show-board $Board
drawIt $Board
# Print the specified number of generations or
# indefinitely, until Ctrl+C is pressed.
[uint32] $i = 1
while (++$i -le $Count -or $Count -eq [uint32]::MaxValue) {
# Calculate the next generation.
update-generation ([ref] $Board)
if ($MilliSecsToPause) {
Start-Sleep -Milliseconds $MilliSecsToPause
}
if ($InPlace) {
Clear-Host
} else {
'' # Output empty line before new board is printed.
}
# Print this generation.
#show-board $Board
drawIt $Board
}
}
function drawIt{
param([int[,]]$m_ref)
For($x=0; $x -le $m_ref.GetUpperBound(0); $x++ ){
For($y=0; $y -le $m_ref.GetUpperBound(1); $y++){
$val = $m_ref[$x,$y]
If($val -eq 1){
$cellColor = 'White'
}
Else{
$cellColor = 'Black'
}
#write-host $cellColor.GetType()
Write-Host " " -NoNewline -BackgroundColor $CellColor
Write-Host " " -NoNewline
}
Write-Host '' #start a new line
}
}
# Board is always a square, $size represents both x and y.
$size = 10 #change this to change size of the board
$board = New-Object 'int[,]' ($size, $size)
$seedCount = 17 #change this to change # of alive cells to start with
# Seed the board.
for($seed = 0; $seed -lt $seedCount ; $seed ++){
$board[$(Get-random -Maximum ($size -1)),$(Get-random -Maximum ($size -1))] = 1
}
# Determine how many generations to show and how to show them.
$htDisplayParams = @{
Count = 25 # how many generations to show (1 means: just the initial state)
# omit this entry to keep going indefinitely
InPlace = $True # whether to print subsequent generations in-place
MilliSecsToPause = 1000 # To slow down updates
}
# Start showing the generations.
show-generations -Board $board @htDisplayParams
非常感谢mklement0和Mike Shepard花时间提供帮助、指导和建议。您对问题的含义非常模糊。邻居的计数错了怎么办?调试告诉了你什么?邻居计数返回的值高于矩阵所表示的值。很抱歉,我已经摆弄了几个小时,所以我不想发布我所有的调试输出,以一个特定的值或以其他无法解释的方式一直更高?我已经扫描了您的逻辑以查找典型错误/不正确的假设,从PowerShell的角度看,我看不到任何明显的错误。@MarkWragg GetNeights函数返回的值如下(000012333531121),这不仅是错误的(gen1中没有包含5个邻居的单元格),而且很短。。。我希望返回25个值。也许迭代是错误的。这里有一个不必要的冒号,但实际上并没有引起任何问题:
$z-=$:size
我在你发布前2分钟发现了第二个错误,但它仍然不起作用,我准备用另一种语言重新制作!!谢谢,我从来没有使用过write verbose,我必须查看它才能看到这些,请将$verbosepreference设置为“继续”。谢谢。当我看到代码的时候,我的主要精力是让它工作起来。谢谢你!这是一个很好的学习工具,可以帮助我更好地理解:)接下来——获取图形输出!呜!:)
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 ""
}
$ErrorActionPreference = 'Stop' # Abort on all unhandled errors.
Set-StrictMode -version 1 # Prevent use of uninitialized variables.
# Given a board as $m_ref, calculates the next generation and assigns it
# back to $m_ref.
function update-generation {
param(
[ref] [int[,]]$m_ref # the by-reference board variable (matrix)
)
# Create a new, all-zero clone of the current board (matrix) to
# receive the new generation.
$m_new = New-Object 'int[,]' ($m_ref.Value.GetLength(0), $m_ref.Value.GetLength(1))
For($x=0; $x -le $m_new.GetUpperBound(0); $x++ ){
For($y=0; $y -le $m_new.GetUpperBound(1); $y++){
# Get the count of live neighbors.
# Note that the *original* matrix must be used to:
# - determine the live neighbors
# - inspect the current state
# because the game rules must be applied *simultaneously*.
$neighborCount = get-LiveNeighborCount $m_ref.Value $x $y
if ($m_ref.Value[$x,$y]) { # currently LIVE cell
# A live cell with 2 or 3 neighbors lives, all others die.
$m_new[$x,$y] = [int] ($neighborCount -eq 2 -or $neighborCount -eq 3)
} else { # curently DEAD cell
# A currently dead cell is resurrected if it has 3 live neighbors.
$m_new[$x,$y] = [int] ($neighborCount -eq 3)
}
$null = $m_new[$x,$y]
}
}
# Assign the new generation to the by-reference board variable.
$m_ref.Value = $m_new
}
# Get the count of live neighbors for board position $x, $y.
function get-LiveNeighborCount{
param(
[int[,]]$m, # the board (matrix)
[Int]$x,
[Int]$y
)
$xLength = $m.GetLength(0)
$yLength = $m.GetLength(1)
$count = 0
for($xOffset = -1; $xOffset -le 1; $xOffset++) {
for($yOffset = -1; $yOffset -le 1; $yOffset++) {
if (-not ($xOffset -eq 0 -and $yOffset -eq 0)) { # skip the position at hand itself
if($m[(get-wrappedIndex $xLength ($x + $xOffset)),(get-wrappedIndex $yLength ($y + $yOffset))]) {
$count++
}
}
}
}
# Output the count.
$count
}
# Given a potentially out-of-bounds index along a dimension of a given length,
# return the wrapped-around-the-edges value.
function get-wrappedIndex{
param(
[Int]$length,
[Int]$index
)
If($index -lt 0){
$index += $length
}
ElseIf($index -ge $length){
$index -= $length
}
# Output the potentially wrapped index.
$index
}
# Print a single generation's board.
function show-board {
param(
[int[,]] $m # the board (matrix)
)
0..$m.GetUpperBound(0) |
ForEach-Object {
$dim1=$_
(0..$m.GetUpperBound(1) | ForEach-Object { $m[$dim1, $_] }) -join ' '
}
}
# Show successive generations.
function show-generations {
param(
[int[,]] $Board,
[uint32] $Count = [uint32]::MaxValue,
[switch] $InPlace,
[int] $MilliSecsToPause
)
# Print the initial board (the 1st generation).
Clear-Host
show-board $Board
# Print the specified number of generations or
# indefinitely, until Ctrl+C is pressed.
[uint32] $i = 1
while (++$i -le $Count -or $Count -eq [uint32]::MaxValue) {
# Calculate the next generation.
update-generation ([ref] $Board)
if ($MilliSecsToPause) {
Start-Sleep -Milliseconds $MilliSecsToPause
}
if ($InPlace) {
Clear-Host
} else {
'' # Output empty line before new board is printed.
}
# Print this generation.
show-board $Board
}
}
# Board is always a square, $size represents both x and y.
$size = 5
$board = New-Object 'int[,]' ($size, $size)
# Seed the board.
$board[2,1] = 1
$board[2,2] = 1
$board[2,3] = 1
# Determine how many generations to show and how to show them.
$htDisplayParams = @{
Count = 5 # How many generations to show (1 means: just the initial state);
# omit this entry to keep going indefinitely.
InPlace = $True # Whether to print subsequent generations in-place.
MilliSecsToPause = 1000 # To slow down updates.
}
# Start showing the generations.
show-generations -Board $board @htDisplayParams
$ErrorActionPreference = 'Stop' # Abort on all unhandled errors.
Set-StrictMode -version 1 # Prevent use of uninitialized variables.
# Given a board as $m_ref, calculates the next generation and assigns it
# back to $m_ref.
function update-generation {
param(
[ref] [int[,]]$m_ref # the by-reference board variable (matrix)
)
# Create a new, all-zero clone of the current board (matrix) to
# receive the new generation.
$m_new = New-Object 'int[,]' ($m_ref.Value.GetLength(0), $m_ref.Value.GetLength(1))
For($x=0; $x -le $m_new.GetUpperBound(0); $x++ ){
For($y=0; $y -le $m_new.GetUpperBound(1); $y++){
# Get the count of live neighbors.
# Note that the *original* matrix must be used to:
# - determine the live neighbors
# - inspect the current state
# because the game rules must be applied *simultaneously*.
$neighborCount = get-LiveNeighborCount $m_ref.Value $x $y
if ($m_ref.Value[$x,$y]) { # currently LIVE cell
# A live cell with 2 or 3 neighbors lives, all others die.
$m_new[$x,$y] = [int] ($neighborCount -eq 2 -or $neighborCount -eq 3)
} else { # curently DEAD cell
# A currently dead cell is resurrected if it has 3 live neighbors.
$m_new[$x,$y] = [int] ($neighborCount -eq 3)
}
$null = $m_new[$x,$y]
}
}
# Assign the new generation to the by-reference board variable.
$m_ref.Value = $m_new
}
# Get the count of live neighbors for board position $x, $y.
function get-LiveNeighborCount{
param(
[int[,]]$m, # the board (matrix)
[Int]$x,
[Int]$y
)
$xLength = $m.GetLength(0)
$yLength = $m.GetLength(1)
$count = 0
for($xOffset = -1; $xOffset -le 1; $xOffset++) {
for($yOffset = -1; $yOffset -le 1; $yOffset++) {
if (-not ($xOffset -eq 0 -and $yOffset -eq 0)) { # skip the position at hand itself
if($m[(get-wrappedIndex $xLength ($x + $xOffset)),(get-wrappedIndex $yLength ($y + $yOffset))]) {
$count++
}
}
}
}
# Output the count.
$count
}
# Given a potentially out-of-bounds index along a dimension of a given length,
# return the wrapped-around-the-edges value.
function get-wrappedIndex{
param(
[Int]$length,
[Int]$index
)
If($index -lt 0){
$index += $length
}
ElseIf($index -ge $length){
$index -= $length
}
# Output the potentially wrapped index.
$index
}
# Print a single generation's board.
function show-board {
param(
[int[,]] $m # the board (matrix)
)
0..$m.GetUpperBound(0) |
ForEach-Object {
$dim1=$_
(0..$m.GetUpperBound(1) | ForEach-Object { $m[$dim1, $_] }) -join ' '
}
}
# Show successive generations.
function show-generations {
param(
[int[,]] $Board,
[uint32] $Count = [uint32]::MaxValue,
[switch] $InPlace,
[int] $MilliSecsToPause
)
# Print the initial board (the 1st generation).
Clear-Host
#show-board $Board
drawIt $Board
# Print the specified number of generations or
# indefinitely, until Ctrl+C is pressed.
[uint32] $i = 1
while (++$i -le $Count -or $Count -eq [uint32]::MaxValue) {
# Calculate the next generation.
update-generation ([ref] $Board)
if ($MilliSecsToPause) {
Start-Sleep -Milliseconds $MilliSecsToPause
}
if ($InPlace) {
Clear-Host
} else {
'' # Output empty line before new board is printed.
}
# Print this generation.
#show-board $Board
drawIt $Board
}
}
function drawIt{
param([int[,]]$m_ref)
For($x=0; $x -le $m_ref.GetUpperBound(0); $x++ ){
For($y=0; $y -le $m_ref.GetUpperBound(1); $y++){
$val = $m_ref[$x,$y]
If($val -eq 1){
$cellColor = 'White'
}
Else{
$cellColor = 'Black'
}
#write-host $cellColor.GetType()
Write-Host " " -NoNewline -BackgroundColor $CellColor
Write-Host " " -NoNewline
}
Write-Host '' #start a new line
}
}
# Board is always a square, $size represents both x and y.
$size = 10 #change this to change size of the board
$board = New-Object 'int[,]' ($size, $size)
$seedCount = 17 #change this to change # of alive cells to start with
# Seed the board.
for($seed = 0; $seed -lt $seedCount ; $seed ++){
$board[$(Get-random -Maximum ($size -1)),$(Get-random -Maximum ($size -1))] = 1
}
# Determine how many generations to show and how to show them.
$htDisplayParams = @{
Count = 25 # how many generations to show (1 means: just the initial state)
# omit this entry to keep going indefinitely
InPlace = $True # whether to print subsequent generations in-place
MilliSecsToPause = 1000 # To slow down updates
}
# Start showing the generations.
show-generations -Board $board @htDisplayParams