PowerShell/scripts/play-chess.ps1

1233 lines
47 KiB
PowerShell
Raw Normal View History

2024-10-04 22:23:14 +02:00
<#
.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."
}
}