Writing a proper legal move list generator for chess is indeed tricky. You have to deal with exotic rules like en passant, and castling being illegal if the king or corresponding rook has moved. Additionally, rather than chess ending simply when the king is taken, it instead ends 2 moves before when it is determined that the king cannot escape the possibility of being taken. Extensive testing has to be done to determine if cases involving specialized rules are indeed correct.
Before I discovered FreeBASIC I made a simultaneous version of chess using VB.NET.
Note: This game runs using a simple http connection to my php/sql based webserver, so you can play anyone anywhere online. However my basic hosting plan is not meant for a high volume of traffic, so if it becomes popular, I guess I will have to get back to work on it and milk it for server money. :)
The client side code I have to merely test to see if a user specified move is legal or not is quite complicated and over 600 lines. Further this code neither tests for 3 fold repetition nor 50 moves without progress. Also note that the rules of chess are a little different from simul-chess eg. rules relating to 'bouncing' are needed in simul-chess but not in regular chess.
Code: Select all
Imports System.Math
Public Enum ChessPiece
EmptyTile = 0
King = 1
Queen = 2
Knight = 3
Biship = 4
Rook = 5
Pawn = 6
White = 16
End Enum
Public Class ChessBoard
Public Pos(7,7) As ChessPiece
Public GuardW(7,7), GuardB(7,7) As Byte
Public CantCastle(7, 7) As Boolean
Public PrvMvW, PrvMvB As New ChessMove
Public MoveNum As Integer, Bounce, Reversed As Boolean
Public Sub New
Me.SetUp
End Sub
Public Sub SetUp()
Dim X, Y As Long
PrvMvW.ZeroMove
PrvMvB.ZeroMove
MoveNum = 1
Bounce = False
CantCastle(7, 0) = False
CantCastle(0, 0) = False
CantCastle(7, 7) = False
CantCastle(0, 7) = False
Pos(0, 0) = ChessPiece.Rook
Pos(1, 0) = ChessPiece.Knight
Pos(2, 0) = ChessPiece.Biship
Pos(3, 0) = ChessPiece.Queen
Pos(4, 0) = ChessPiece.King
Pos(5, 0) = ChessPiece.Biship
Pos(6, 0) = ChessPiece.Knight
Pos(7, 0) = ChessPiece.Rook
For X = 0 To 7
For Y = 2 To 5
Pos(X, Y) = ChessPiece.EmptyTile
Next Y
Pos(X, 1) = ChessPiece.Pawn
Pos(X, 6) = ChessPiece.Pawn Or ChessPiece.White
Pos(X, 7) = Pos(X, 0) Or ChessPiece.White
Next X
ComputeGuarding
End Sub
Public Function Rotate180() As ChessBoard
Dim X, Y As Integer
Rotate180 = New ChessBoard(Me)
For X = 0 To 7
For Y = 0 To 7
Rotate180.Pos(X, Y) = Pos(7 - X, 7 - Y)
Rotate180.GuardB(X, Y) = GuardB(7 - X, 7 - Y)
Rotate180.GuardW(X, Y) = GuardW(7 - X, 7 - Y)
Rotate180.CantCastle(X, Y) = CantCastle(7 - X, 7 - Y)
Next Y
Next X
Rotate180.PrvMvW.SrcX = 7 - PrvMvW.SrcX
Rotate180.PrvMvW.SrcY = 7 - PrvMvW.SrcY
Rotate180.PrvMvW.DestX = 7 - PrvMvW.DestX
Rotate180.PrvMvW.DestY = 7 - PrvMvW.DestY
Rotate180.PrvMvB.SrcX = 7 - PrvMvB.SrcX
Rotate180.PrvMvB.SrcY = 7 - PrvMvB.SrcY
Rotate180.PrvMvB.DestX = 7 - PrvMvB.DestX
Rotate180.PrvMvB.DestY = 7 - PrvMvB.DestY
Rotate180.Reversed = True
End Function
Public Sub ComputeGuarding
Dim X, Y As Integer
For X = 0 To 7
For Y = 0 To 7
GuardB(X, Y) = 0
GuardW(X, Y) = 0
Next Y
Next X
For X = 0 To 7
For Y = 0 To 7
PieceGuard(X, Y, 1)
Next Y
Next X
End Sub
Private Sub GuardTile(G As Object, X As Integer, Y As Integer, Delta As Integer)
If X < 0 Or X > 7 Or Y < 0 Or Y > 7 Then Return
G(X, Y) += Delta
End Sub
Private Sub GuardLine(G As Object, ByVal X As Integer, ByVal Y As Integer, DX As Integer, DY As Integer, Delta As Integer)
Do
X += DX
Y += DY
If X < 0 Or X > 7 Or Y < 0 Or Y > 7 Then Return
G(X, Y) += Delta
Loop Until Pos(X,Y) <> ChessPiece.EmptyTile
End Sub
Public Sub PieceGuard(X As Byte, Y As Byte, Delta As Integer)
Dim Piece As ChessPiece, G As Object
Piece = Pos(X, Y)
If Piece And ChessPiece.White Then
G = GuardW
Else
G = GuardB
End If
Select Case Piece And (Not ChessPiece.White)
Case ChessPiece.EmptyTile : Return
Case ChessPiece.Pawn
If CBool(Piece And ChessPiece.White) Xor Reversed Then
GuardTile(G, X-1, Y-1, Delta)
GuardTile(G, X+1, Y-1, Delta)
Else
GuardTile(G, X-1, Y+1, Delta)
GuardTile(G, X+1, Y+1, Delta)
End If
Case ChessPiece.King
GuardTile(G, X-1, Y-1, Delta)
GuardTile(G, X+1, Y-1, Delta)
GuardTile(G, X-1, Y+1, Delta)
GuardTile(G, X+1, Y+1, Delta)
GuardTile(G, X-1, Y, Delta)
GuardTile(G, X, Y+1, Delta)
GuardTile(G, X, Y-1, Delta)
GuardTile(G, X+1, Y, Delta)
Case ChessPiece.Queen
GuardLine(G, X, Y, 1, 1, Delta)
GuardLine(G, X, Y, -1, 1, Delta)
GuardLine(G, X, Y, 1, -1, Delta)
GuardLine(G, X, Y, -1, -1, Delta)
GuardLine(G, X, Y, 0, 1, Delta)
GuardLine(G, X, Y, 0, -1, Delta)
GuardLine(G, X, Y, 1, 0, Delta)
GuardLine(G, X, Y, -1, 0, Delta)
Case ChessPiece.Biship
GuardLine(G, X, Y, 1, 1, Delta)
GuardLine(G, X, Y, -1, 1, Delta)
GuardLine(G, X, Y, 1, -1, Delta)
GuardLine(G, X, Y, -1, -1, Delta)
Case ChessPiece.Rook
GuardLine(G, X, Y, 0, 1, Delta)
GuardLine(G, X, Y, 0, -1, Delta)
GuardLine(G, X, Y, 1, 0, Delta)
GuardLine(G, X, Y, -1, 0, Delta)
Case ChessPiece.Knight
GuardTile(G, X+1, Y+2, Delta)
GuardTile(G, X-1, Y+2, Delta)
GuardTile(G, X+1, Y-2, Delta)
GuardTile(G, X-1, Y-2, Delta)
GuardTile(G, X+2, Y+1, Delta)
GuardTile(G, X-2, Y+1, Delta)
GuardTile(G, X+2, Y-1, Delta)
GuardTile(G, X-2, Y-1, Delta)
End Select
End Sub
Default Property Piece(X As Integer, Y As Integer) As ChessPiece
Get
If X < 0 Or X > 7 Or Y < 0 Or Y > 7 Then Return ChessPiece.EmptyTile
Piece = Pos(X, Y)
End Get
Set
If X < 0 Or X > 7 Or Y < 0 Or Y > 7 Then Return
Pos(X, Y) = Value
End Set
End Property
Sub New(ByVal B As ChessBoard)
'Me.MemberwiseClone()
Me.Pos = B.Pos.Clone
Me.GuardW = B.GuardW.Clone
Me.GuardB = B.GuardB.Clone
Me.CantCastle = B.CantCastle.Clone
Me.PrvMvW = B.PrvMvW.Copy
Me.PrvMvB = B.PrvMvB.Copy
Me.MoveNum = B.MoveNum
Me.Reversed = B.Reversed
Me.Bounce = B.Bounce
End Sub
Public Function InCheck(ByVal White As Boolean)
Dim X, Y As Integer
Dim P As ChessPiece = ChessPiece.King Or IIf(White, ChessPiece.White, 0)
For X = 0 To 7
For Y = 0 To 7
If Pos(X, Y) = P Then
If White Then
Return GuardB(X, Y) > 0
Else
Return GuardW(X, Y) > 0
End If
End If
Next Y
Next X
End Function
Public Function NoLegalMoveExists(ByVal White As Boolean) As Boolean
Dim MV As New ChessMove
For MV.SrcX = 0 To 7
For MV.SrcY = 0 To 7
If Pos(MV.SrcX, MV.SrcY) <> ChessPiece.EmptyTile And CBool(Pos(MV.SrcX, MV.SrcY) And ChessPiece.White) = White Then
If White Then
For MV.DestX = 0 To 7
For MV.DestY = 0 To 7
If GuardW(MV.DestX, MV.DestY) > 0 Then
If MV.IsPotentiallyLegal(Me) Then Return False
End If
Next MV.DestY
Next MV.DestX
If Pos(MV.SrcX, MV.SrcY) Mod 16 = ChessPiece.Pawn Then
MV.DestX = MV.SrcX
MV.DestY = 4
If MV.IsPotentiallyLegal(Me) Then Return False
MV.DestY = MV.SrcY - 1
If MV.IsPotentiallyLegal(Me) Then Return False
End If
Else
For MV.DestX = 0 To 7
For MV.DestY = 0 To 7
If GuardB(MV.DestX, MV.DestY) > 0 Then
If MV.IsPotentiallyLegal(Me) Then Return False
End If
Next MV.DestY
Next MV.DestX
If Pos(MV.SrcX, MV.SrcY) Mod 16 = ChessPiece.Pawn Then
MV.DestX = MV.SrcX
MV.DestY = 3
If MV.IsPotentiallyLegal(Me) Then Return False
MV.DestY = MV.SrcY + 1
If MV.IsPotentiallyLegal(Me) Then Return False
End If
End If
End If
Next MV.SrcY
Next MV.SrcX
Return True
End Function
Private Function IsPieceOnTile(ByVal X As Integer, ByVal Y As Integer, ByVal Piece As ChessPiece) As Boolean
If X < 0 Or X > 7 Or Y < 0 Or Y > 7 Then Return False
If Pos(X, Y) <> Piece Then Return False
Return True
End Function
Private Function IsPieceOnLine(ByVal X As Integer, ByVal Y As Integer, ByVal DX As Integer, ByVal DY As Integer, ByVal Piece As ChessPiece) As Boolean
Do
X += DX
Y += DY
If X < 0 Or X > 7 Or Y < 0 Or Y > 7 Then Return False
If Pos(X, Y) = Piece Then Return True
Loop Until Pos(X, Y) <> ChessPiece.EmptyTile
Return False
End Function
Public Function IsAttackingKing(ByVal X As Integer, ByVal Y As Integer) As Boolean
Dim Piece As ChessPiece, King As ChessPiece
Piece = Pos(X, Y)
If CBool(Piece And ChessPiece.White) Then
King = ChessPiece.King
Else
King = ChessPiece.King Or ChessPiece.White
End If
Select Case Piece And (Not ChessPiece.White)
Case ChessPiece.EmptyTile : Return False
Case ChessPiece.Pawn
If CBool(Piece And ChessPiece.White) Xor Reversed Then
If IsPieceOnTile(X - 1, Y - 1, King) Then Return True
If IsPieceOnTile(X + 1, Y - 1, King) Then Return True
Else
If IsPieceOnTile(X - 1, Y + 1, King) Then Return True
If IsPieceOnTile(X + 1, Y + 1, King) Then Return True
End If
Case ChessPiece.King
If IsPieceOnTile(X - 1, Y - 1, King) Then Return True
If IsPieceOnTile(X + 1, Y - 1, King) Then Return True
If IsPieceOnTile(X - 1, Y + 1, King) Then Return True
If IsPieceOnTile(X + 1, Y + 1, King) Then Return True
If IsPieceOnTile(X - 1, Y, King) Then Return True
If IsPieceOnTile(X, Y + 1, King) Then Return True
If IsPieceOnTile(X, Y - 1, King) Then Return True
If IsPieceOnTile(X + 1, Y, King) Then Return True
Case ChessPiece.Queen
If IsPieceOnLine(X, Y, 1, 1, King) Then Return True
If IsPieceOnLine(X, Y, -1, 1, King) Then Return True
If IsPieceOnLine(X, Y, 1, -1, King) Then Return True
If IsPieceOnLine(X, Y, -1, -1, King) Then Return True
If IsPieceOnLine(X, Y, 0, 1, King) Then Return True
If IsPieceOnLine(X, Y, 0, -1, King) Then Return True
If IsPieceOnLine(X, Y, 1, 0, King) Then Return True
If IsPieceOnLine(X, Y, -1, 0, King) Then Return True
Case ChessPiece.Biship
If IsPieceOnLine(X, Y, 1, 1, King) Then Return True
If IsPieceOnLine(X, Y, -1, 1, King) Then Return True
If IsPieceOnLine(X, Y, 1, -1, King) Then Return True
If IsPieceOnLine(X, Y, -1, -1, King) Then Return True
Case ChessPiece.Rook
If IsPieceOnLine(X, Y, 0, 1, King) Then Return True
If IsPieceOnLine(X, Y, 0, -1, King) Then Return True
If IsPieceOnLine(X, Y, 1, 0, King) Then Return True
If IsPieceOnLine(X, Y, -1, 0, King) Then Return True
Case ChessPiece.Knight
If IsPieceOnTile(X + 1, Y + 2, King) Then Return True
If IsPieceOnTile(X - 1, Y + 2, King) Then Return True
If IsPieceOnTile(X + 1, Y - 2, King) Then Return True
If IsPieceOnTile(X - 1, Y - 2, King) Then Return True
If IsPieceOnTile(X + 2, Y + 1, King) Then Return True
If IsPieceOnTile(X - 2, Y + 1, King) Then Return True
If IsPieceOnTile(X + 2, Y - 1, King) Then Return True
If IsPieceOnTile(X - 2, Y - 1, King) Then Return True
End Select
Return False
End Function
Public Function LongAlgebraicPrevMoveString() As String
LongAlgebraicPrevMoveString = (MoveNum - 1) & ". "
If Bounce And PrvMvB.SrcP = ChessPiece.Pawn And PrvMvW.SrcP Mod 16 = ChessPiece.Pawn And PrvMvB.SrcX = PrvMvB.DestX Then
LongAlgebraicPrevMoveString &= Chr(Asc("a") + PrvMvW.SrcX) & (8 - PrvMvW.SrcY) & "-"
LongAlgebraicPrevMoveString &= Chr(Asc("a") + PrvMvW.DestX) & (7 - PrvMvW.DestY)
If InCheck(False) Then LongAlgebraicPrevMoveString &= "+"
LongAlgebraicPrevMoveString &= ")("
LongAlgebraicPrevMoveString &= Chr(Asc("a") + PrvMvB.SrcX) & (8 - PrvMvB.SrcY) & "-"
LongAlgebraicPrevMoveString &= Chr(Asc("a") + PrvMvB.DestX) & (9 - PrvMvB.DestY)
If InCheck(True) Then LongAlgebraicPrevMoveString &= "+"
Exit Function
End If
LongAlgebraicPrevMoveString &= PrvMvW.LongAlgebraicNotation
If InCheck(False) Then LongAlgebraicPrevMoveString &= "+"
If Bounce Then
LongAlgebraicPrevMoveString &= ")("
Else
LongAlgebraicPrevMoveString &= " "
End If
LongAlgebraicPrevMoveString &= PrvMvB.LongAlgebraicNotation
If InCheck(True) Then LongAlgebraicPrevMoveString &= "+"
End Function
End Class
Public Class ChessMove
Public SrcX, SrcY As Byte
Public DestX, DestY As Byte
Public SrcP, DestP, PromoP As ChessPiece
Public Take As Boolean
Public Function Copy() As ChessMove
'Me.MemberwiseClone()
Copy = New ChessMove
Copy.SrcX = SrcX
Copy.SrcY = SrcY
Copy.DestX = DestX
Copy.DestY = DestY
Copy.SrcP = SrcP
Copy.DestP = DestP
Copy.PromoP = PromoP
Copy.Take = Take
End Function
Public Sub ZeroMove()
SrcX = 0
SrcY = 0
DestX = 0
DestY = 0
SrcP = ChessPiece.EmptyTile
DestP = ChessPiece.EmptyTile
PromoP = ChessPiece.EmptyTile
End Sub
Public Function LongAlgebraicNotation() As String
Select Case SrcP Mod 16
Case ChessPiece.Pawn : LongAlgebraicNotation = ""
Case ChessPiece.King
Select Case CInt(Me.DestX) - Me.SrcX
Case 2 : Return "0-0"
Case -2 : Return "0-0-0"
Case Else : LongAlgebraicNotation = "K"
End Select
Case ChessPiece.Queen : LongAlgebraicNotation = "Q"
Case ChessPiece.Knight : LongAlgebraicNotation = "N"
Case ChessPiece.Rook : LongAlgebraicNotation = "R"
Case ChessPiece.Biship : LongAlgebraicNotation = "B"
End Select
LongAlgebraicNotation &= Chr(Asc("a") + SrcX) & (8 - SrcY)
If Take Then
LongAlgebraicNotation &= "x"
Else
LongAlgebraicNotation &= "-"
End If
LongAlgebraicNotation &= Chr(Asc("a") + DestX) & (8 - DestY)
Select Case PromoP Mod 16
Case ChessPiece.EmptyTile
Case ChessPiece.Queen : LongAlgebraicNotation &= "=Q"
Case ChessPiece.Knight : LongAlgebraicNotation &= "=N"
Case ChessPiece.Rook : LongAlgebraicNotation &= "=R"
Case ChessPiece.Biship : LongAlgebraicNotation &= "=B"
End Select
End Function
Public Function MoveStr() As String
Return CStr(SrcX) & CStr(SrcY) & CStr(DestX) & CStr(DestY) & CStr(PromoP Mod 16)
End Function
Public Sub MoveFromStr(ByVal Str As String)
Dim I As Double
I = Val(Str)
PromoP = I Mod 10
I = I \ 10
DestY = I Mod 10
I = I \ 10
DestX = I Mod 10
I = I \ 10
SrcY = I Mod 10
I = I \ 10
SrcX = I Mod 10
End Sub
Public Sub ApplyMove(ByVal MoveStr As String, ByVal Board As ChessBoard)
Dim MV As New ChessMove
'Dim P1, P2 As Byte
Dim DX, DY, MVDX, MVDY As Integer, X, Y As Byte
MV.MoveFromStr(MoveStr)
SrcP = Board(SrcX, SrcY)
DestP = Board(DestX, DestY)
If PromoP <> ChessPiece.EmptyTile Then PromoP = PromoP Or (SrcP And ChessPiece.White)
MV.SrcP = Board(MV.SrcX, MV.SrcY)
MV.DestP = Board(MV.DestX, MV.DestY)
If MV.PromoP <> ChessPiece.EmptyTile Then MV.PromoP = MV.PromoP Or (MV.SrcP And ChessPiece.White)
DX = CInt(DestX) - SrcX
DY = CInt(DestY) - SrcY
MVDX = CInt(MV.DestX) - MV.SrcX
MVDY = CInt(MV.DestY) - MV.SrcY
Board(SrcX, SrcY) = ChessPiece.EmptyTile
Board(MV.SrcX, MV.SrcY) = ChessPiece.EmptyTile
Board.Bounce = False
'Castling
If SrcP Mod 16 = ChessPiece.King And Abs(DX) = 2 Then
Board(SrcX + Sign(DX), SrcY) = ChessPiece.Rook Or (SrcP And ChessPiece.White)
Board(((DX + 2) \ 4) * 7, SrcY) = ChessPiece.EmptyTile
End If
If MV.SrcP Mod 16 = ChessPiece.King And Abs(MVDX) = 2 Then
Board(MV.SrcX + Sign(MVDX), MV.SrcY) = ChessPiece.Rook Or (MV.SrcP And ChessPiece.White)
Board(((MVDX + 2) \ 4) * 7, MV.SrcY) = ChessPiece.EmptyTile
End If
Take = False
MV.Take = False
If Board(DestX, DestY) <> ChessPiece.EmptyTile Then Take = True
If Board(MV.DestX, MV.DestY) <> ChessPiece.EmptyTile Then MV.Take = True
If MV.DestX <> DestX Or MV.DestY <> DestY Then
Board(DestX, DestY) = SrcP
Board(MV.DestX, MV.DestY) = MV.SrcP
Else
If SrcP Mod 16 = ChessPiece.Pawn And MV.SrcP Mod 16 = ChessPiece.Pawn Then
Board(SrcX, SrcY) = SrcP
Board(MV.SrcX, MV.SrcY) = MV.SrcP
DestY = MV.SrcY
MV.DestY = SrcY
Board.Bounce = True
Else
If SrcP Mod 16 = ChessPiece.Pawn Then Board(MV.DestX, MV.DestY) = MV.SrcP : MV.Take = True
If MV.SrcP Mod 16 = ChessPiece.Pawn Then Board(DestX, DestY) = SrcP : Take = True
End If
End If
'En Passant
If DestX <> SrcX And SrcP Mod 16 = ChessPiece.Pawn And DestP = ChessPiece.EmptyTile Then Board(DestX, SrcY) = ChessPiece.EmptyTile : Take = True
If MV.DestX <> MV.SrcX And MV.SrcP Mod 16 = ChessPiece.Pawn And MV.DestP = ChessPiece.EmptyTile Then Board(MV.DestX, MV.SrcY) = ChessPiece.EmptyTile : MV.Take = True
If (Abs(DX) = Abs(DY) Or DX = 0 Or DY = 0) And (Abs(MVDX) = Abs(MVDY) Or MVDX = 0 Or MVDY = 0) Then
DX = Sign(DX)
DY = Sign(DY)
If DX = -Sign(MVDX) And DY = -Sign(MVDY) Then
X = SrcX
Y = SrcY
Do Until X = DestX And Y = DestY
If X = MV.DestX And Y = MV.DestY Then
Board(DestX, DestY) = MV.SrcP
Board(MV.DestX, MV.DestY) = SrcP
Board.Bounce = True
Exit Do
End If
X = X + DX
Y = Y + DY
Loop
End If
End If
If PromoP <> ChessPiece.EmptyTile And SrcP = Board(DestX, DestY) Then Board(DestX, DestY) = PromoP
If MV.PromoP <> ChessPiece.EmptyTile And MV.SrcP = Board(MV.DestX, MV.DestY) Then Board(MV.DestX, MV.DestY) = MV.PromoP
If SrcP = ChessPiece.King Or MV.SrcP = ChessPiece.King Then
Board.CantCastle(7, 0) = True
Board.CantCastle(0, 0) = True
End If
If SrcP = (ChessPiece.King Or ChessPiece.White) Or MV.SrcP = (ChessPiece.King Or ChessPiece.White) Then
Board.CantCastle(7, 7) = True
Board.CantCastle(0, 7) = True
End If
Board.CantCastle(SrcX, SrcY) = True
Board.CantCastle(DestX, DestY) = True
Board.CantCastle(MV.SrcX, MV.SrcY) = True
Board.CantCastle(MV.DestX, MV.DestY) = True
If CBool(SrcP And ChessPiece.White) Then
Board.PrvMvB = MV.Copy
Board.PrvMvW = Me.Copy
Else
Board.PrvMvB = Me.Copy
Board.PrvMvW = MV.Copy
End If
Board.ComputeGuarding()
Board.MoveNum += 1
End Sub
Public Sub ApplySingleMove(ByVal Board As ChessBoard)
Dim DX, DY, MVDX, MVDY As Integer, X, Y As Byte
SrcP = Board(SrcX, SrcY)
DestP = Board(DestX, DestY)
'If PromoP <> ChessPiece.EmptyTile Then PromoP = PromoP Or (SrcP And ChessPiece.White)
DX = CInt(DestX) - SrcX
DY = CInt(DestY) - SrcY
Board(SrcX, SrcY) = ChessPiece.EmptyTile
'Castling
If SrcP Mod 16 = ChessPiece.King And Abs(DX) = 2 Then
Board(SrcX + Sign(DX), SrcY) = ChessPiece.Rook Or (SrcP And ChessPiece.White)
Board(((DX + 2) \ 4) * 7, SrcY) = ChessPiece.EmptyTile
End If
Board(DestX, DestY) = SrcP
'En Passant
If DestX <> SrcX And SrcP Mod 16 = ChessPiece.Pawn And DestP = ChessPiece.EmptyTile Then Board(DestX, SrcY) = ChessPiece.EmptyTile
If PromoP <> ChessPiece.EmptyTile And SrcP = Board(DestX, DestY) Then Board(DestX, DestY) = PromoP Or (SrcP And ChessPiece.White)
If SrcP = ChessPiece.King Then
Board.CantCastle(7, 0) = True
Board.CantCastle(0, 0) = True
End If
If SrcP = (ChessPiece.King Or ChessPiece.White) Then
Board.CantCastle(7, 7) = True
Board.CantCastle(0, 7) = True
End If
Board.CantCastle(SrcX, SrcY) = True
Board.CantCastle(DestX, DestY) = True
Board.ComputeGuarding()
End Sub
Public Function IsLegal(ByVal Board As ChessBoard) As Boolean
SrcP = Board(SrcX, SrcY)
'Pawn Promotion
If PromoP <> ChessPiece.EmptyTile And SrcP Mod 16 <> ChessPiece.Pawn Then Return False
If (SrcP And (Not ChessPiece.White)) = ChessPiece.Pawn Then
If CBool(SrcP And ChessPiece.White) Then
If DestY = 0 And (PromoP <= ChessPiece.King Or PromoP >= ChessPiece.Pawn) Then Return False
Else
If DestY = 7 And (PromoP <= ChessPiece.King Or PromoP >= ChessPiece.Pawn) Then Return False
End If
End If
Return IsPotentiallyLegal(Board)
End Function
Public Function IsPotentiallyLegal(ByVal Board As ChessBoard) As Boolean
' Rules of Simul Chess
Dim DX, DY As Integer, X, Y As Byte
' Must Move a Piece on the board
If SrcX > 7 Or SrcY > 7 Or DestX > 7 Or DestY > 7 Then Return False
If Board(SrcX, SrcY) = ChessPiece.EmptyTile Then Return False
' May not attack a piece of the same color
Dim Piece As ChessPiece = Board(SrcX, SrcY)
If Board(DestX, DestY) <> ChessPiece.EmptyTile And Board(DestX, DestY) \ ChessPiece.White = Piece \ ChessPiece.White Then Return False
'Cant Move a Piece after Taking, or bouncing except the King
If Piece Mod 16 <> ChessPiece.King Then
If CBool(Piece And ChessPiece.White) Then
If Board.PrvMvW.Take And Board.PrvMvW.DestX = SrcX And Board.PrvMvW.DestY = SrcY Then Return False
If Board.Bounce And Board.PrvMvB.DestX = SrcX And Board.PrvMvB.DestY = SrcY Then Return False
Else
If Board.PrvMvB.Take And Board.PrvMvB.DestX = SrcX And Board.PrvMvB.DestY = SrcY Then Return False
If Board.Bounce And Board.PrvMvW.DestX = SrcX And Board.PrvMvW.DestY = SrcY Then Return False
End If
'May Not move Pieces Attacking the King
If Board.IsAttackingKing(SrcX, SrcY) Then Return False
End If
' King may not be taken
If Board(DestX, DestY) Mod 16 = ChessPiece.King Then Return False
'Each of the 6 Pieces allowed to move only as in regular Chess
DX = CInt(DestX) - SrcX
DY = CInt(DestY) - SrcY
Select Case Piece And (Not ChessPiece.White)
Case ChessPiece.EmptyTile : Return False
Case ChessPiece.Pawn
If Abs(DY) = 2 Then
If DX <> 0 Then Return False
If CBool(Piece And ChessPiece.White) Then
If SrcY < 6 Then Return False
Else
If SrcY > 1 Then Return False
End If
If Board(DestX, SrcY + Sign(DY)) <> ChessPiece.EmptyTile Then Return False
Else
If CBool(Piece And ChessPiece.White) Then
If DY <> -1 Or Abs(DX) > 1 Then Return False
Else
If DY <> 1 Or Abs(DX) > 1 Then Return False
End If
End If
If DX = 0 Then
If Board(DestX, DestY) <> ChessPiece.EmptyTile Then Return False
Else
If Board(DestX, DestY) = ChessPiece.EmptyTile Then
'En Passant
If CBool(Piece And ChessPiece.White) Then
If SrcY <> 3 Or DestX <> Board.PrvMvB.DestX Then Return False
'If Board.PrvMvB.SrcP <> ChessPiece.Pawn Then Return False
If Board(Board.PrvMvB.DestX, Board.PrvMvB.DestY) <> ChessPiece.Pawn Then Return False
If Board.PrvMvB.DestY - Board.PrvMvB.SrcY <> 2 Then Return False
Else
If SrcY <> 4 Or DestX <> Board.PrvMvW.DestX Then Return False
If Board(Board.PrvMvW.DestX, Board.PrvMvW.DestY) <> (ChessPiece.Pawn Or ChessPiece.White) Then Return False
If Board.PrvMvW.SrcY - Board.PrvMvW.DestY <> 2 Then Return False
End If
End If
End If
Case ChessPiece.King
If Abs(DX) = 2 And DY = 0 Then
'Castling
If DX > 0 Then
If Board(5, SrcY) <> ChessPiece.EmptyTile Or Board(6, SrcY) <> ChessPiece.EmptyTile Then Return False
Else
If Board(1, SrcY) <> ChessPiece.EmptyTile Or Board(2, SrcY) <> ChessPiece.EmptyTile Or Board(3, SrcY) <> ChessPiece.EmptyTile Then Return False
End If
If CBool(Piece And ChessPiece.White) Then
If Board.GuardB(SrcX + Sign(DX), SrcY) > 0 Or Board.GuardB(SrcX, SrcY) > 0 Then Return False
If DX > 0 And Board.CantCastle(7, 7) Then Return False
If DX < 0 And Board.CantCastle(0, 7) Then Return False
Else
If Board.GuardW(SrcX + Sign(DX), SrcY) > 0 Or Board.GuardW(SrcX, SrcY) > 0 Then Return False
If DX > 0 And Board.CantCastle(7, 0) Then Return False
If DX < 0 And Board.CantCastle(0, 0) Then Return False
End If
Else
If Abs(DX) > 1 Or Abs(DY) > 1 Then Return False
End If
Case ChessPiece.Queen
If Abs(DX) <> Abs(DY) And DX <> 0 And DY <> 0 Then Return False
Case ChessPiece.Biship
If Abs(DX) <> Abs(DY) Then Return False
Case ChessPiece.Rook
If DX <> 0 And DY <> 0 Then Return False
Case ChessPiece.Knight
If (Abs(DX) <> 1 Or Abs(DY) <> 2) And (Abs(DX) <> 2 Or Abs(DY) <> 1) Then Return False
End Select
' Line Movements may not pass through pieces
If Abs(DX) = Abs(DY) Or DX = 0 Or DY = 0 Then
DX = Sign(DX)
DY = Sign(DY)
X = SrcX + DX
Y = SrcY + DY
Do Until X = DestX And Y = DestY
If Board(X, Y) <> ChessPiece.EmptyTile Then Return False
X = X + DX
Y = Y + DY
Loop
End If
'Cant Move into check
Dim B As New ChessBoard(Board)
ApplySingleMove(B)
If B.InCheck(CBool(Piece And ChessPiece.White)) Then Return False
Return True
End Function
End Class