<# .SYNOPSIS Local multiplayer chess game in PowerShell. .DESCRIPTION Started off of code from https://github.com/bhassen99/POSH-Chess, which was very much incomplete. I kept the board shape, but have changed everything else. The unicode chess pieces unfortunately do not render in the base PowerShell console, they only appear when run in PowerShell ISE. .NOTES Name: Chess.ps1 Version: 0.3.1 Author: Michael Shen Date: 10-19-2017 #> #Update-Board must be run before Publish-Board #Draws the board to the screen and displays all the icons Function Publish-Board { Clear Write-Host "`n`n" Write-Host ' A B C D E F G H' Write-Host ' --------------------------------- ' Write-Host ' 8 |'$board[0,7].Icon'|'$board[1,7].Icon'|'$board[2,7].Icon'|'$board[3,7].Icon'|'$board[4,7].Icon'|'$board[5,7].Icon'|'$board[6,7].Icon'|'$board[7,7].Icon'| 8' Write-Host ' --------------------------------- ' Write-Host ' 7 |'$board[0,6].Icon'|'$board[1,6].Icon'|'$board[2,6].Icon'|'$board[3,6].Icon'|'$board[4,6].Icon'|'$board[5,6].Icon'|'$board[6,6].Icon'|'$board[7,6].Icon'| 7' Write-Host ' --------------------------------- ' Write-Host ' 6 |'$board[0,5].Icon'|'$board[1,5].Icon'|'$board[2,5].Icon'|'$board[3,5].Icon'|'$board[4,5].Icon'|'$board[5,5].Icon'|'$board[6,5].Icon'|'$board[7,5].Icon'| 6' Write-Host ' --------------------------------- ' Write-Host ' 5 |'$board[0,4].Icon'|'$board[1,4].Icon'|'$board[2,4].Icon'|'$board[3,4].Icon'|'$board[4,4].Icon'|'$board[5,4].Icon'|'$board[6,4].Icon'|'$board[7,4].Icon'| 5' Write-Host ' --------------------------------- ' Write-Host ' 4 |'$board[0,3].Icon'|'$board[1,3].Icon'|'$board[2,3].Icon'|'$board[3,3].Icon'|'$board[4,3].Icon'|'$board[5,3].Icon'|'$board[6,3].Icon'|'$board[7,3].Icon'| 4' Write-Host ' --------------------------------- ' Write-Host ' 3 |'$board[0,2].Icon'|'$board[1,2].Icon'|'$board[2,2].Icon'|'$board[3,2].Icon'|'$board[4,2].Icon'|'$board[5,2].Icon'|'$board[6,2].Icon'|'$board[7,2].Icon'| 3' Write-Host ' --------------------------------- ' Write-Host ' 2 |'$board[0,1].Icon'|'$board[1,1].Icon'|'$board[2,1].Icon'|'$board[3,1].Icon'|'$board[4,1].Icon'|'$board[5,1].Icon'|'$board[6,1].Icon'|'$board[7,1].Icon'| 2' Write-Host ' --------------------------------- ' Write-Host ' 1 |'$board[0,0].Icon'|'$board[1,0].Icon'|'$board[2,0].Icon'|'$board[3,0].Icon'|'$board[4,0].Icon'|'$board[5,0].Icon'|'$board[6,0].Icon'|'$board[7,0].Icon'| 1' Write-Host ' --------------------------------- ' Write-Host ' A B C D E F G H' } #Read and clean text input before calling New-Move Function Read-Input { Write-Host "" if($Script:whiteTurn) { try { [ValidateScript({$_.Length -eq 2 -or $_ -like '*resign*'})]$src = Read-Host 'White piece source' if ($src -eq 'resign') { $Script:gameStatus = [gamestatus]::blackWin Update-Log -resign $true } else { [Int]$cc = Get-Column $src[0] [Int]$cr = Get-Row $src[1] [ValidateScript({$_.Color -eq 'White'})]$pc = $board[$cc, $cr] [ValidateScript({$_.Length -eq 2})]$dst = Read-Host 'White piece destination' New-Move $src $dst } } catch { Write-Error "Illegal input: Not a white piece or valid location" Write-Error $src Read-Input } } else { try { [ValidateScript({$_.Length -eq 2 -or $_.Value -eq 'resign'})]$src = Read-Host 'Black piece source' if ($src -like '*resign*') { $Script:gameStatus = [gamestatus]::whiteWin Update-Log -resign $true } [Int]$cc = Get-Column $src[0] [Int]$cr = Get-Row $src[1] [ValidateScript({$_.Color -eq 'Black'})]$pc = $board[$cc, $cr] [ValidateScript({$_.Length -eq 2})]$dst = Read-Host 'Black piece destination' New-Move $src $dst } catch { Write-Error "Illegal input: Not a black piece or valid location" Read-Input } } } #Update the status of all the pieces and place them Function Update-Board { #Get arrays of all piece that are still alive [Array]$CurrentWhite = $Script:WhitePieces | Where-Object {$_.Alive -eq $true} [Array]$CurrentBlack = $Script:BlackPieces | Where-Object {$_.Alive -eq $true} #Place all the white pieces foreach ($pc in $CurrentWhite) { $board[($pc.CurrentColumn),($pc.CurrentRow)] = $pc } #Place all the black pieces foreach ($pc in $CurrentBlack) { $board[($pc.CurrentColumn),($pc.CurrentRow)] = $pc } #Check for spaces without a piece in them, then fill it with the empty placeholder. for ($i = 0; $i -le 7; $i++) { for ($j = 0; $j -le 7; $j++) { if ($board[$i, $j] -eq $null) { $board[$i, $j] = $Empty } } } } #Used to move pieces on the board Function New-Move { param ([string]$src, [string]$dst) enum castleOptions { none = 0 kingside = 1 queenside = 2 } [bool]$attack = $false [bool]$moveSuccess = $false [int]$castle = [castleOptions]::none [bool]$promote = $false [bool]$ep = $false [bool]$check = $false try { [Int]$CurrentColumn = Get-Column $src[0] [Int]$CurrentRow = Get-Row $src[1] [Int]$DesiredColumn = Get-Column $dst[0] [Int]$DesiredRow = Get-Row $dst[1] $pc = $board[$CurrentColumn, $CurrentRow] } catch { Write-Error "Out of bounds" Read-Input } #Moving nothing, nowhere, or trying to capture your own piece if ($board[$CurrentColumn, $CurrentRow] -eq $Empty) { Write-Error "There is nothing there." Read-Input } elseif (($CurrentRow -eq $DesiredRow) -and ($CurrentColumn -eq $DesiredColumn)) { Write-Error "That wouldn't move anywhere." Read-Input } elseif ($board[$DesiredColumn, $DesiredRow] -ne $Empty -and ` $pc.Color -eq $board[$DesiredColumn, $DesiredRow].Color) { Write-Error "Collision with own team" Read-Input } else { [int]$MoveX = $DesiredColumn - $CurrentColumn [int]$MoveY = $DesiredRow - $CurrentRow #Move verification logic for each piece switch ($pc.GetType().Name) { 'Pawn' { $MoveX = [math]::abs($MoveX) #Pawns can max move one to the side when capturing, two forward when moving if (($MoveX -gt 1) -or ([math]::abs($MoveY) -gt 2)) { Write-Error "Illegal Pawn Move: Too many spaces" } else { #Force pawns to only move "forward" if ($pc.Color -eq 'Black') { $MoveY *= -1 } if ($MoveX -eq 0) { if ($board[$DesiredColumn, $DesiredRow] -eq $Empty) { if ($MoveY -eq 1) { $moveSuccess = $true $pc.firstmove = $false } elseif ($MoveY -eq 2 -and $pc.firstmove -eq $true) { if ($board[$DesiredColumn, ($DesiredRow + 1)] -eq $Empty) { $moveSuccess = $true $pc.firstmove = $false $pc.inpassing = $Script:turnCounter } else { Write-Error "Illegal Pawn Move: Blocked Path" } } else { Write-Error "Illegal Pawn Move: Cannot Move 2 Spaces" } } else { Write-Error "Illegal Pawn Move: Blocked Path" } } elseif (($MoveX -eq 1) -and ($MoveY -eq 1)) { if ($board[$DesiredColumn, $DesiredRow] -eq $Empty) { $enpassant = $board[$DesiredColumn, $CurrentRow] if (($enpassant.GetType().Name -eq 'Pawn') -and ` ($pc.Color -ne $enpassant.Color) -and ` ($enpassant.inpassing -eq ($Script:turnCounter - 1))) { $moveSuccess = $true $attack = $true $ep = $true $enpassant.Alive = $false $enpassant.CurrentPosition = $null $enpassant.CurrentRow = $null $enpassant.CurrentColumn = $null $board[$DesiredColumn, $CurrentRow] = $Empty } else { Write-Error 'Illegal Pawn Move: Cannot Capture en passant' } } else { $attack = $true $moveSuccess = $true $pc.firstmove = $false } } else { #Catch-all, should never get here Write-Error "Illegal Pawn Move" } } } 'Knight' { $MoveX = [math]::abs($MoveX) $MoveY = [math]::abs($MoveY) if ((($MoveX -eq 1) -and ($MoveY -eq 2)) -or (($MoveX -eq 2) -and ($MoveY -eq 1))) { $moveSuccess = $true if ($board[$DesiredColumn, $DesiredRow] -ne $Empty) { $attack = $true } } else { Write-Error "Illegal Knight Move" } } 'Bishop' { if ([math]::abs($MoveX) -ne [math]::abs($MoveY)) { Write-Error "Illegal Bishop Move: Not a Diagonal" } else { if ($MoveX -gt 0) { if ($MoveY -gt 0) { for ($i = 1; $i -lt $MoveX; $i++) { if ($board[($CurrentColumn + $i) , ($CurrentRow + $i)] -ne $Empty) { Write-Error "Illegal Bishop Move: Blocked Path" Read-Input break } } } else { for ($i = 1; $i -lt $MoveX; $i++) { if ($board[($CurrentColumn + $i) , ($CurrentRow - $i)] -ne $Empty) { Write-Error "Illegal Bishop Move: Blocked Path" Read-Input break } } } } else { if ($MoveY -gt 0) { for ($i = 1; $i -lt $MoveY; $i++) { if ($board[($CurrentColumn - $i) , ($CurrentRow + $i)] -ne $Empty) { Write-Error "Illegal Bishop Move: Blocked Path" Publish-Board break } } } else { for ($i = 1; $i -lt [math]::abs($MoveX); $i++) { if ($board[($CurrentColumn - $i) , ($CurrentRow - $i)] -ne $Empty) { Write-Error "Illegal Bishop Move: Blocked Path" Publish-Board break } } } } $moveSuccess = $true if ($board[$DesiredColumn, $DesiredRow] -ne $Empty) { $attack = $true } } } 'Rook' { if (([math]::abs($MoveX) -gt 0) -and ([math]::abs($MoveY) -gt 0)) { Write-Error "Illegal Rook Move" } else { if ($MoveX -gt 0) { for ($i = 1; $i -lt $MoveX; $i++) { if ($board[($CurrentColumn + $i), $CurrentRow] -ne $Empty) { Write-Error "Illegal Rook Move: Blocked Path" Publish-Board break } } } elseif ($MoveX -lt 0) { for ($i = 1; $i -lt [math]::abs($MoveX); $i++) { if ($board[($CurrentColumn - $i), $CurrentRow] -ne $Empty) { Write-Error "Illegal Rook Move: Blocked Path" Publish-Board break } } } elseif ($MoveY -gt 0) { for ($i = 1; $i -lt $MoveY; $i++) { if ($board[$CurrentColumn, ($CurrentRow + $i)] -ne $Empty) { Write-Error "Illegal Rook Move: Blocked Path" Publish-Board break } } } else { for ($i = 1; $i -lt [math]::abs($MoveY); $i++) { if ($board[$CurrentColumn, ($CurrentRow - $i)] -ne $Empty) { Write-Error "Illegal Rook Move: Blocked Path" Publish-Board break } } } $moveSuccess = $true $pc.firstmove = $false if ($board[$DesiredColumn, $DesiredRow] -ne $Empty) { $attack = $true } } } 'King' { $MoveX = [math]::abs($MoveX) $MoveY = [math]::abs($MoveY) if (($MoveX -eq 1) -or ($MoveY -eq 1)) { $moveSuccess = $true if ($board[$DesiredColumn, $DesiredRow] -ne $Empty) { $attack = $true } } elseif (($pc.firstmove -eq $true) -and ` ($pc.color -eq 'White')) { if (($dst -eq 'G1') -and ` ($wHR.firstmove -eq $true)) { $Crk = $board[7, 0] $board[7, 0] = $Empty $Crk.CurrentPosition = 'F1' $Crk.CurrentRow = 0 $Crk.CurrentColumn = 5 $Crk.firstmove = $false $moveSuccess = $true $castle = [castleOptions]::kingside $pc.firstmove = $false } elseif (($dst -eq 'C1') -and ` ($wAR.firstmove -eq $true)) { $Crk = $board[0, 0] $board[0, 0] = $Empty $Crk.CurrentPosition = 'D1' $Crk.CurrentRow = 0 $Crk.CurrentColumn = 3 $Crk.firstmove = $false $moveSuccess = $true $castle = [castleOptions]::queenside $pc.firstmove = $false } } elseif (($pc.firstmove -eq $true) -and ` ($pc.color -eq 'Black')) { if (($dst -eq 'G8') -and ` ($bHR.firstmove -eq $true)) { $Crk = $board[7, 7] $board[7, 7] = $Empty $Crk.CurrentPosition = 'F8' $Crk.CurrentRow = 7 $Crk.CurrentColumn = 5 $Crk.firstmove = $false $moveSuccess = $true $castle = [castleOptions]::kingside $pc.firstmove = $false } elseif (($dst -eq 'C8') -and ` ($bAR.firstmove -eq $true)) { $Crk = $board[0, 7] $board[0, 7] = $Empty $Crk.CurrentPosition = 'D8' $Crk.CurrentRow = 7 $Crk.CurrentColumn = 3 $Crk.firstmove = $false $moveSuccess = $true $castle = [castleOptions]::queenside $pc.firstmove = $false } } else { Write-Error "Illegal King Move" } } 'Queen' { if ([math]::abs($MoveX) -eq [math]::abs($MoveY)) { if ($MoveX -gt 0) { if ($MoveY -gt 0) { for ($i = 1; $i -lt $MoveX; $i++) { if ($board[($CurrentColumn + $i) , ($CurrentRow + $i)] -ne $Empty) { Write-Error "Illegal Queen Move" Publish-Board break } } } else { for ($i = 1; $i -lt $MoveX; $i++) { if ($board[($CurrentColumn + $i) , ($CurrentRow - $i)] -ne $Empty) { Write-Error "Illegal Queen Move" Publish-Board break } } } } else { if ($MoveY -gt 0) { for ($i = 1; $i -lt $MoveY; $i++) { if ($board[($CurrentColumn - $i), ($CurrentRow + $i)] -ne $Empty) { Write-Error "Illegal Queen Move" Publish-Board break } } } else { for ($i = 1; $i -lt [math]::abs($MoveX); $i++) { if ($board[($CurrentColumn - $i) , ($CurrentRow - $i)] -ne $Empty) { Write-Error "Illegal Queen Move" Publish-Board break } } } } $moveSuccess = $true if ($board[$DesiredColumn, $DesiredRow] -ne $Empty) { $attack = $true } } elseif (($MoveX -ne 0 -and $MoveY -eq 0) -or ` ($MoveX -eq 0 -and $MoveY -ne 0)) { if ($MoveX -gt 0) { for ($i = 1; $i -lt $MoveX; $i++) { if ($board[($CurrentColumn + $i), $CurrentRow] -ne $Empty) { Write-Error "Illegal Queen Move" Publish-Board break } } } elseif ($MoveX -lt 0) { for ($i = 1; $i -lt [math]::abs($MoveX); $i++) { if ($board[($CurrentColumn - $i), $CurrentRow] -ne $Empty) { Write-Error "Illegal Queen Move" Publish-Board break } } } elseif ($MoveY -gt 0) { for ($i = 1; $i -lt $MoveY; $i++) { if ($board[$CurrentColumn, ($CurrentRow + $i)] -ne $Empty) { Write-Error "Illegal Queen Move" Publish-Board break } } } else { for ($i = 1; $i -lt [math]::abs($MoveY); $i++) { if ($board[$CurrentColumn, ($CurrentRow - $i)] -ne $Empty) { Write-Error "Illegal Queen Move" Publish-Board break } } } $moveSuccess = $true if ($board[$DesiredColumn, $DesiredRow] -ne $Empty) { $attack = $true } } else { Write-Error "Illegal Queen Move" } } } if ($moveSuccess -eq $true) { if ($attack -eq $true -and $ep -eq $false) { $board[$DesiredColumn, $DesiredRow].Alive = $false $board[$DesiredColumn, $DesiredRow].CurrentPosition = $null $board[$DesiredColumn, $DesiredRow].CurrentRow = $null $board[$DesiredColumn, $DesiredRow].CurrentColumn = $null } #Pawn Promotion logic if (($pc.GetType().Name -eq 'Pawn') -and ($DesiredRow -eq 0)) { [ValidateSet('Knight', 'Bishop', 'Rook', 'Queen')]$ptype = Read-Host 'Promote black pawn to' $promote = $true $pc.Type = $ptype switch ($ptype) { 'Knight' { $pc.Icon = '♞' $pc.Symbol = 'N' } 'Bishop' { $pc.Icon = '♝' $pc.Symbol = 'B' } 'Rook' { $pc.Icon = '♜' $pc.Symbol = 'R' } 'Queen' { $pc.Icon = '♛' $pc.Symbol = 'Q' } } } elseif (($pc.GetType().Name -eq 'Pawn') -and ($DesiredRow -eq 7)) { [ValidateSet('Knight', 'Bishop', 'Rook', 'Queen')]$ptype = Read-Host 'Promote white pawn to' $promote = $true $pc.Type = $ptype switch ($ptype) { 'Knight' { $pc.Icon = '♘' $pc.Symbol = 'N' } 'Bishop' { $pc.Icon = '♗' $pc.Symbol = 'B' } 'Rook' { $pc.Icon = '♖' $pc.Symbol = 'R' } 'Queen' { $pc.Icon = '♕' $pc.Symbol = 'Q' } } } $board[$CurrentColumn, $CurrentRow] = $Empty $pc.CurrentPosition = $dst.ToUpper() $pc.CurrentRow = $DesiredRow $pc.CurrentColumn = $DesiredColumn Update-Board #Check logic #TODO: Shouldn't check when king is captured Issue 25 Test-Gamestatus if ($Script:gameStatus -eq [gamestatus]::ongoing) { [Array]$curWhite = $Script:WhitePieces | Where-Object {$_.Alive -eq $true} [Array]$curBlack = $Script:BlackPieces | Where-Object {$_.Alive -eq $true} if ($Script:whiteTurn -eq $true) { foreach ($whitePiece in $curWhite) { if ($(Test-Move $whitePiece.CurrentPosition $Script:bK.CurrentPosition)[0] -eq $true) { $check = $true } } } else { foreach ($blackPiece in $curBlack) { if ($(Test-Move $blackPiece.CurrentPosition $Script:wK.CurrentPosition)[0] -eq $true) { $check = $true } } } } #Update the log, advance turn Update-Log $src $dst $pc.Symbol $attack $castle $promote $ep $check $Script:turnCounter += 1 $Script:whiteTurn = !($Script:whiteTurn) } else { Read-Input } } } #Log logic will go here Function Update-Log { param([string]$src, [string]$dst, [string]$piece, [bool]$attack, [int]$castle, [bool]$promote, [bool]$ep, [bool]$check, [bool]$resign) [string]$logentry = '' enum castleOptions { none = 0 kingside = 1 queenside = 2 } if ($castle -eq [castleOptions]::kingside) { $logentry = '0-0' } elseif ($castle -eq [castleOptions]::queenside) { $logentry = '0-0-0' } elseif ($promote -eq $true) { if ($attack) { $logentry += 'x' } $logentry += $dst + '=' + $piece } else { $logentry = $piece if ($attack) { $logentry += 'x' } $logentry += $dst } if ($ep -eq $true) { $logentry += ' ep' } if ($check -eq $true) { $logentry += '+' } if ($Script:gameStatus -ne 0) { $logentry += '#' } if ($resign -eq $true) { $logentry = 'resigned' } $Script:log += $logentry #Equivalent of touch command to ensure a log exists Write-Output $null >> $Script:logpath #Clear and rewrite the log each time Clear-Content $Script:logpath $line = "White`t`tBlack`r`n--------------------" #Header Add-Content -Encoding Unicode $Script:logpath $line if ($log.Length -eq 1) { Add-Content -Encoding Unicode $Script:logpath $log[0] } else { for ($i = 0; $i -lt $log.Length - 1; $i += 2) { $line = $Script:log[$i] $line += " `t`t" $line += $Script:log[$i + 1] Add-Content -Encoding Unicode $Script:logpath $line } #If game ended in white move, print out the "half-line" if ($log.Length % 2 -eq 1) { Add-Content -Encoding Unicode $Script:logpath $Script:log[$Script:log.Length - 1] } } } #Try a move, used for check and castling logic Function Test-Move { param ([string]$src, [string]$dst) [bool]$attack = $false [bool]$moveSuccess = $false [bool[]]$status = @($moveSuccess, $attack) try { [Int]$CurrentColumn = Get-Column $src[0] [Int]$CurrentRow = Get-Row $src[1] [Int]$DesiredColumn = Get-Column $dst[0] [Int]$DesiredRow = Get-Row $dst[1] $pc = $board[$CurrentColumn, $CurrentRow] } catch { Write-Error "Out of bounds" return $status } #Moving nothing, nowhere, or trying to capture your own piece if ($board[$CurrentColumn, $CurrentRow] -eq $Empty) { Write-Error "There is nothing there." return $status } elseif (($CurrentRow -eq $DesiredRow) -and ($CurrentColumn -eq $DesiredColumn)) { Write-Error "That wouldn't move anywhere." return $status } elseif ($board[$DesiredColumn, $DesiredRow] -ne $Empty -and ` $pc.Color -eq $board[$DesiredColumn, $DesiredRow].Color) { Write-Error "Collision with own team" return $status } else { [int]$MoveX = $DesiredColumn - $CurrentColumn [int]$MoveY = $DesiredRow - $CurrentRow #Pieces playable switch ($pc.Type) { 'Pawn' { $MoveX = [math]::abs($MoveX) if (($MoveX -gt 1) -or ([math]::abs($MoveY) -gt 2)) { return $status } else { #Force pawns to only move "forward" if ($pc.Color -eq 'Black') { $MoveY *= -1 } if (($MoveX -eq 0) -and ($MoveY -eq 1)) { if ($board[$DesiredColumn,$DesiredRow] -ne $Empty) { return $status } else { $status[0] = $true $pc.firstmove = $false } } elseif (($MoveX -eq 0) -and ($MoveY -eq 2)) { if (($pc.firstmove = $true) -and ` (($board[$DesiredColumn, $DesiredRow] -eq $Empty) -and ` ($board[($DesiredColumn + 1), $DesiredRow] -eq $Empty))) { $status[0] = $true $pc.firstmove = $false $pc.inpassing = $Script:turnCounter } else { return $status } } elseif (($MoveX -eq 1) -and ($MoveY -eq 1)) { if ($board[$DesiredColumn,$DesiredRow] -eq $Empty) { $enpassant = $board[$DesiredColumn, $CurrentRow] if (($enpassant.GetType().Name -eq 'Pawn') -and ` ($pc.Color -ne $enpassant.Color) -and ` ($enpassant.inpassing -eq ($Script:turnCounter - 1))) { $status[0] = $true $enpassant.Alive = $false $enpassant.CurrentPosition = $null $enpassant.CurrentRow = $null $enpassant.CurrentColumn = $null $board[$DesiredColumn, $CurrentRow] = $Empty } else { return $status } } else { $status[1] = $true $status[0] = $true $pc.firstmove = $false } } else { return $status } } } 'Knight' { $MoveX = [math]::abs($MoveX) $MoveY = [math]::abs($MoveY) if ((($MoveX -eq 1) -and ($MoveY -eq 2)) -or (($MoveX -eq 2) -and ($MoveY -eq 1))) { $status[0] = $true if ($board[$DesiredColumn, $DesiredRow] -ne $Empty) { $status[1] = $true } } else { return $status } } 'Bishop' { if ([math]::abs($MoveX) -ne [math]::abs($MoveY)) { return $status } else { if ($MoveX -gt 0) { if ($MoveY -gt 0) { for ($i = 1; $i -lt $MoveX; $i++) { if ($board[($CurrentColumn + $i) , ($CurrentRow + $i)] -ne $Empty) { return $status } } } else { for ($i = 1; $i -lt $MoveX; $i++) { if ($board[($CurrentColumn + $i) , ($CurrentRow - $i)] -ne $Empty) { return $status } } } } else { if ($MoveY -gt 0) { for ($i = 1; $i -lt $MoveY; $i++) { if ($board[($CurrentColumn - $i) , ($CurrentRow + $i)] -ne $Empty) { return $status } } } else { for ($i = 1; $i -lt [math]::abs($MoveX); $i++) { if ($board[($CurrentColumn - $i) , ($CurrentRow - $i)] -ne $Empty) { return $status } } } } $status[0] = $true if ($board[$DesiredColumn, $DesiredRow] -ne $Empty) { $status[1] = $true } } } 'Rook' { if (([math]::abs($MoveX) -gt 0) -and ([math]::abs($MoveY) -gt 0)) { return $status } else { if ($MoveX -gt 0) { for ($i = 1; $i -lt $MoveX; $i++) { if ($board[($CurrentColumn + $i), $CurrentRow] -ne $Empty) { return $status } } } elseif ($MoveX -lt 0) { for ($i = 1; $i -lt [math]::abs($MoveX); $i++) { if ($board[($CurrentColumn - $i), $CurrentRow] -ne $Empty) { return $status } } } elseif ($MoveY -gt 0) { for ($i = 1; $i -lt $MoveY; $i++) { if ($board[$CurrentColumn, ($CurrentRow + $i)] -ne $Empty) { return $status } } } else { for ($i = 1; $i -lt [math]::abs($MoveY); $i++) { if ($board[$CurrentColumn, ($CurrentRow - $i)] -ne $Empty) { return $status } } } $status[0] = $true $pc.firstmove = $false if ($board[$DesiredColumn, $DesiredRow] -ne $Empty) { $status[1] = $true } } } 'Queen' { if ([math]::abs($MoveX) -eq [math]::abs($MoveY)) { if ($MoveX -gt 0) { if ($MoveY -gt 0) { for ($i = 1; $i -lt $MoveX; $i++) { if ($board[($CurrentColumn + $i) , ($CurrentRow + $i)] -ne $Empty) { return $status } } } else { for ($i = 1; $i -lt $MoveX; $i++) { if ($board[($CurrentColumn + $i) , ($CurrentRow - $i)] -ne $Empty) { return $status } } } } else { if ($MoveY -gt 0) { for ($i = 1; $i -lt $MoveY; $i++) { if ($board[($CurrentColumn - $i), ($CurrentRow + $i)] -ne $Empty) { return $status } } } else { for ($i = 1; $i -lt [math]::abs($MoveX); $i++) { if ($board[($CurrentColumn - $i) , ($CurrentRow - $i)] -ne $Empty) { return $status } } } } $status[0] = $true if ($board[$DesiredColumn, $DesiredRow] -ne $Empty) { $status[1] = $true } } elseif (($MoveX -ne 0 -and $MoveY -eq 0) -or ` ($MoveX -eq 0 -and $MoveY -ne 0)) { if ($MoveX -gt 0) { for ($i = 1; $i -lt $MoveX; $i++) { if ($board[($CurrentColumn + $i), $CurrentRow] -ne $Empty) { return $status } } } elseif ($MoveX -lt 0) { for ($i = 1; $i -lt [math]::abs($MoveX); $i++) { if ($board[($CurrentColumn - $i), $CurrentRow] -ne $Empty) { return $status } } } elseif ($MoveY -gt 0) { for ($i = 1; $i -lt $MoveY; $i++) { if ($board[$CurrentColumn, ($CurrentRow + $i)] -ne $Empty) { return $status } } } else { for ($i = 1; $i -lt [math]::abs($MoveY); $i++) { if ($board[$CurrentColumn, ($CurrentRow - $i)] -ne $Empty) { return $status } } } $status[0] = $true if ($board[$DesiredColumn, $DesiredRow] -ne $Empty) { $status[1] = $true } } else { return $status } } 'King' { $MoveX = [math]::abs($MoveX) $MoveY = [math]::abs($MoveY) if (($MoveX -le 1) -and ($MoveY -le 1)) { $status[0] = $true if ($board[$DesiredColumn, $DesiredRow] -ne $Empty) { $status[1] = $true } } elseif (($pc.firstmove -eq $true) -and ` ($pc.color -eq 'White')) { if (($dst -eq 'G1') -and ` ($wHR.firstmove -eq $true)) { $Crk = $board[7, 0] $board[7, 0] = $Empty $Crk.CurrentPosition = 'F1' $Crk.CurrentRow = 0 $Crk.CurrentColumn = 5 $Crk.firstmove = $false $status[0] = $true $pc.firstmove = $false } elseif (($dst -eq 'C1') -and ` ($wAR.firstmove -eq $true)) { $Crk = $board[0, 0] $board[0, 0] = $Empty $Crk.CurrentPosition = 'D1' $Crk.CurrentRow = 0 $Crk.CurrentColumn = 3 $Crk.firstmove = $false $status[0] = $true $pc.firstmove = $false } } elseif (($pc.firstmove -eq $true) -and ` ($pc.color -eq 'Black')) { if (($dst -eq 'G8') -and ` ($bHR.firstmove -eq $true)) { $Crk = $board[7, 7] $board[7, 7] = $Empty $Crk.CurrentPosition = 'F8' $Crk.CurrentRow = 7 $Crk.CurrentColumn = 5 $Crk.firstmove = $false $status[0] = $true $pc.firstmove = $false } elseif (($dst -eq 'C8') -and ` ($bAR.firstmove -eq $true)) { $Crk = $board[0, 7] $board[0, 7] = $Empty $Crk.CurrentPosition = 'D8' $Crk.CurrentRow = 7 $Crk.CurrentColumn = 3 $Crk.firstmove = $false $status[0] = $true $pc.firstmove = $false } } else { return $status } } } return $status } } #Figure out if the game is over or still ongoing Function Test-Gamestatus { if ($wK.Alive -eq $false) { $Script:gameStatus = [gamestatus]::blackWin } elseif ($bK.Alive -eq $false) { $Script:gameStatus = [gamestatus]::whiteWin } } Function Get-Column { param ([ValidatePattern('[A-H]')][string]$Col) switch ($Col) { "A" {Return "0"} "B" {Return "1"} "C" {Return "2"} "D" {Return "3"} "E" {Return "4"} "F" {Return "5"} "G" {Return "6"} "H" {Return "7"} } } Function Get-Row { param ([ValidateRange(1,8)][string]$row) return ($row - 1) } ########################### #endregion: Functions #################################################### #################################################### #region: Classes ########################### #Gives all classes that inherit(:) this class the base properties Class ChessPiece { [bool]$Alive=$true [string]$Type [string]$Icon [string]$Symbol [ValidateSet('White', 'Black')][String]$Color [String]$CurrentPosition [ValidateRange(0,7)][Int]$CurrentRow [ValidateRange(0,7)][Int]$CurrentColumn } Class Pawn : ChessPiece { [bool]$firstmove = $true [int]$inpassing = 0 [string]$Type = $this.GetType().Name [string]$Symbol = " " Pawn([string]$Position, [string]$color) { $this.Color = $color $this.CurrentPosition = $Position $this.CurrentRow = Get-Row $Position[1] $this.CurrentColumn = Get-Column $Position[0] if ($color -eq 'White') { $this.Icon = '♙' } elseif ($color -eq 'Black') { $this.Icon = '♟' } } } Class Rook : ChessPiece { [bool]$firstmove = $true [string]$Type = $this.GetType().Name [string]$Symbol = "R" Rook([string]$Position, [string]$color) { $this.Color = $color $this.CurrentPosition = $Position $this.CurrentRow = Get-Row $Position[1] $this.CurrentColumn = Get-Column $Position[0] if ($color -eq 'White') { $this.Icon = '♖' } elseif ($color -eq 'Black') { $this.Icon = '♜' } } } Class Knight : ChessPiece { [string]$Type = $this.GetType().Name [string]$Symbol = "N" Knight([string]$Position, [string]$color) { $this.Color = $color $this.CurrentPosition = $Position $this.CurrentRow = Get-Row $Position[1] $this.CurrentColumn = Get-Column $Position[0] if ($color -eq 'White') { $this.Icon = '♘' } elseif ($color -eq 'Black') { $this.Icon = '♞' } } } Class Bishop : ChessPiece { [string]$Type = $this.GetType().Name [string]$Symbol = "B" Bishop([String]$Position, [string]$color) { $this.Color = $color $this.CurrentPosition = $Position $this.CurrentRow = Get-Row $Position[1] $this.CurrentColumn = Get-Column $Position[0] if ($color -eq 'White') { $this.Icon = '♗' } elseif ($color -eq 'Black') { $this.Icon = '♝' } } } Class Queen : ChessPiece { [string]$Type = $this.GetType().Name [string]$Symbol = "Q" Queen([String]$Position, [string]$color) { $this.Color = $color $this.CurrentPosition = $Position $this.CurrentRow = Get-Row $Position[1] $this.CurrentColumn = Get-Column $Position[0] if ($color -eq 'White') { $this.Icon = '♕' } elseif ($color -eq 'Black') { $this.Icon = '♛' } } } Class King : ChessPiece { [bool]$firstmove = $true [string]$Type = $this.GetType().Name [string]$Symbol = "K" King([String]$Position, [string]$color) { $this.Color = $color $this.CurrentPosition = $Position $this.CurrentRow = Get-Row $Position[1] $this.CurrentColumn = Get-Column $Position[0] if ($color -eq 'White') { $this.Icon = '♔' } elseif ($color -eq 'Black') { $this.Icon = '♚' } } } Class Blank { [String]$Icon=' ' } ########################### #endregion: Classes #################################################### #Creates the game board [Object]$Script:board = New-Object 'object[,]' 8,8 #Creates a turn status [bool]$Script:whiteTurn = $true #SAN log.txt path, currently on desktop $DesktopPath = [Environment]::GetFolderPath("Desktop") [string]$Script:logpath = $DesktopPath + '\log.txt' [string[]]$Script:log = @() $Script:wAP = [Pawn]::New('A2', 'White') $Script:wBP = [Pawn]::New('B2', 'White') $Script:wCP = [Pawn]::New('C2', 'White') $Script:wDP = [Pawn]::New('D2', 'White') $Script:wEP = [Pawn]::New('E2', 'White') $Script:wFP = [Pawn]::New('F2', 'White') $Script:wGP = [Pawn]::New('G2', 'White') $Script:wHP = [Pawn]::New('H2', 'White') $Script:wAR = [Rook]::New('A1', 'White') $Script:wBN = [Knight]::New('B1', 'White') $Script:wCB = [Bishop]::New('C1', 'White') $Script:wQ = [Queen]::New('D1', 'White') $Script:wK = [King]::New('E1', 'White') $Script:wFB = [Bishop]::New('F1', 'White') $Script:wGN = [Knight]::New('G1', 'White') $Script:wHR = [Rook]::New('H1', 'White') $Script:bAP = [Pawn]::New('A7', 'Black') $Script:bBP = [Pawn]::New('B7', 'Black') $Script:bCP = [Pawn]::New('C7', 'Black') $Script:bDP = [Pawn]::New('D7', 'Black') $Script:bEP = [Pawn]::New('E7', 'Black') $Script:bFP = [Pawn]::New('F7', 'Black') $Script:bGP = [Pawn]::New('G7', 'Black') $Script:bHP = [Pawn]::New('H7', 'Black') $Script:bAR = [Rook]::New('A8', 'Black') $Script:bBN = [Knight]::New('B8', 'Black') $Script:bCB = [Bishop]::New('C8', 'Black') $Script:bQ = [Queen]::New('D8', 'Black') $Script:bK = [King]::New('E8', 'Black') $Script:bFB = [Bishop]::New('F8', 'Black') $Script:bGN = [Knight]::New('G8', 'Black') $Script:bHR = [Rook]::New('H8', 'Black') $Script:Empty = [Blank]::New() [Array] $Script:WhitePieces = @( $Script:wAP,$Script:wBP,$Script:wCP,$Script:wDP, $Script:wEP,$Script:wFP,$Script:wGP,$Script:wHP, $Script:wAR,$Script:wHR,$Script:wBN,$Script:wGN, $Script:wCB,$Script:wFB,$Script:wQ,$Script:wK ) [Array] $Script:BlackPieces = @( $Script:bAP,$Script:bBP,$Script:bCP,$Script:bDP, $Script:bEP,$Script:bFP,$Script:bGP,$Script:bHP, $Script:bAR,$Script:bHR,$Script:bBN,$Script:bGN, $Script:bCB,$Script:bFB,$Script:bQ,$Script:bK ) enum gamestatus { ongoing = 0 whiteWin = 1 blackWin = 2 quit = 3 } #Set global variables to keep track of turn and game status [int]$Script:turnCounter = 0 $Script:gameStatus = [gamestatus]::ongoing while ($Script:gameStatus -eq [gamestatus]::ongoing) { Update-Board Publish-Board Read-Input if ($Script:gameStatus -eq [gamestatus]::blackWin) { Write-Output "Black Wins!" } elseif ($Script:gameStatus -eq [gamestatus]::whiteWin) { Write-Output "White Wins!" } elseif ($Script:gameStatus -eq [gamestatus]::quit) { Write-Output "Game ended by request." } }