Monday, January 2, 2017

Conway's Game of Life in PowerShell using WPF.  Slow but seemingly functional (let me know if there are any bugs).  Game pauses when mouse is hovering over board.  Beware of line breaks caused by "blogger."

#SCRIPT
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName PresentationCore

$Window = [System.Windows.Window]@{Width=450;Height=450}

$Global:GX_Size = 25
$Global:GY_Size = 25


$Life_Canvas = [System.Windows.Controls.Canvas]@{Background="#CCEEFF"};$Window.AddChild($Life_Canvas)


Function Compare-Boards ($Board1, $Board2){
    $Keys = $Board1.Keys

    foreach($Key in $Keys){
        $B1_Val = $Board1.$Key
        $B2_Val = $Board2.$Key
        if($B1_Val -ne $B2_Val){
            Write-Host -ForegroundColor Green -Object "$B1_Val != $B2_Val"
        }
        else{
            Write-Host -ForegroundColor Red -Object "$B1_Val = $B2_Val"
        }
    }
}

Function Draw-Cell($Parent,$CellWidth=20,$CellHeight=20,$State=1,$X, $Y, $Board){
    <#States
    1: Any live cell with fewer than two live neighbours dies, as if caused by under-population.
    2: Any live cell with more than three live neighbours dies, as if by overcrowding.
    3: Any live cell with two or three live neighbours lives on to the next generation.
    4: Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
    #>
    $CellPXWidth = $Parent.ActualWidth / ($CellWidth+1)
    $CellPXHeight = $Parent.ActualHeight / ($CellHeight+1)

    if([bool]$State){[System.Windows.Media.Brush]$BG = "Green"}else{[System.Windows.Media.Brush]$BG="Black"}

    $NewCell = [System.Windows.Shapes.Rectangle]@{Width=$CellPXWidth;Height=$CellPXHeight;Name="X$X`_Y$Y";Margin="$($X*$CellPXWidth),$($Y*$CellPXHeight)";HorizontalAlignment="Left";VerticalAlignment="Top";Fill=$BG;Stroke="#FF333333"}
   
    $Click_Change = {
       
        if([System.Windows.Input.Mouse]::LeftButton -ne "Pressed"){}
        else{
            $Board = $Global:Current_State
            $Name = $This.Name
   
            if($Board.$Name -eq 1){$Board.$Name = 0; $This.Fill = "Black"}
            else{$Board.$Name = 1;$This.Fill = "Green"}
        }
    }

    $NewCell.add_MouseEnter($Click_Change)  
    $NewCell.add_MouseDown($Click_Change)  

    $Parent.AddChild($NewCell)

}

Function Get-Tile($x, $y, $Board){
    if($x -lt 0){$x = $Board.Width}
    if($y -lt 0){$y = $Board.Height}
    if($x -gt $Board.Width){$x = 0}
    if($y -gt $Board.Height){$y = 0}
    $CoordString = "X$x`_Y$y"
   
    return @{Coord=$CoordString;Value=$Board.$CoordString}
}

Function Get-Neighbors ($xPos = 10, $yPos = 10, $Board){
    $LiveCount = 0
   
    $CheckedList = @()
   
    for($x = -1; $x -le 1; $x++){
        for($y = -1; $y -le 1; $y++){
            #Skip if self
            if($x -eq 0 -and $y -eq 0){continue}
            $TileData = Get-Tile -x ($xPos + $x) -y ($yPos + $y) -Board $Board
           
            #Skip if already checked; clipping purposes
            if($CheckedList -contains $TileData.Coord){continue}
            $CheckedList += $TileData.Coord
            $LiveCount += $TileData.Value
        }
    }


    return $LiveCount
}

Function Draw-Board ($X_Size=20, $Y_Size=20, $Current_State){
     
    $Next_State = @{Width=$X_Size;Height=$Y_Size;GUID=[guid]::NewGuid().Guid}

    for($x = 0; $x -le $X_Size; $x++){
        for($y = 0; $y -le $Y_Size; $y++){  
            $Coord = "X$x`_Y$y"
            $Count = Get-Neighbors -xPos $x -yPos $y -Board $Current_State
            #Write-Host $Count -ForegroundColor Cyan
            if($Current_State.$Coord -eq 1){
                #Write-Host "Die" -ForegroundColor Red
                if($Count -lt 2){$Next_State.$Coord = 0}
                if($Count -eq 2 -or $Count -eq 3){$Next_State.$Coord = 1}
                if($Count -gt 3){$Next_State.$Coord = 0}
            }
            elseif($Current_State.$Coord -eq 0){
           
                if($Count -eq 3){
                #Write-Host "Born" -ForegroundColor Cyan
                    $Next_State.$Coord = 1
                }
                else{$Next_State.$Coord = 0}
            }
        }
    }
    return $Next_State
}

#Initialize board
$Current_State = @{Width=$Global:GX_Size;Height=$Global:GY_Size;GUID=[guid]::NewGuid().Guid}
for($x = 0; $x -le $Global:GX_Size; $x++){
    for($y = 0; $y -le $Global:GY_Size; $y++){  
        #$Current_State."X$x`_Y$y" = (Get-Random @(1,0))
        $Current_State."X$x`_Y$y" = 0
    }
}
#Write-Host $Current_State.GUID -ForegroundColor Cyan

#Write-Host -ForegroundColor Cyan "Initializing board"
for($ix = 0; $ix -le $Global:GX_Size;$ix++){
    for($iy = 0; $iy -le $Global:GY_Size; $iy++){
        $Coord = "X$ix`_Y$iy"  
        Draw-Cell -Parent $Life_Canvas -CellWidth $Global:GX_Size -CellHeight $Global:GY_Size -State $Current_State.$Coord -X $ix -Y $iy -Board $Current_State
    }
}

$Global:Generation = 0
$GameLoop = {
    Write-Host ($Global:Generation++) -ForegroundColor Cyan
#    Write-Host -ForegroundColor Cyan "Running Game Loop"
    $Next_State = Draw-Board -X_Size $Global:GX_Size -Y_Size $Global:GY_Size -Current_State $global:Current_State
    $Life_Canvas.Children.Clear()
   
    #Write-Host -ForegroundColor Cyan "$($Next_State.X2_Y4)"
    for($ix = 0; $ix -le $Global:GX_Size;$ix++){
        for($iy = 0; $iy -le $Global:GY_Size; $iy++){
            $Coord = "X$ix`_Y$iy"  
            Draw-Cell -Parent $Life_Canvas -CellWidth $Global:GX_Size -CellHeight $Global:GY_Size -State $Next_State.$Coord -X $ix -Y $iy -Board $Next_State
        }
    }
    $global:Current_State = $Next_State
   
    #Write-Host $Current_State.GUID -ForegroundColor Cyan
}

$Refresher ={
    $global:timer = new-object System.Windows.Threading.DispatcherTimer
    $global:timer.Interval = [TimeSpan]"0:0:1"
    $global:timer.Add_Tick($GameLoop)
    $global:timer.Start()
}

$Window.add_Loaded($Refresher)
$Window.add_MouseEnter({$global:Timer.Stop()})
$Window.add_MouseLeave({$global:Timer.Start()})

$Window.ShowDialog()