Proper Chess Engine with networked CPUs & LAN play

User projects written in or related to FreeBASIC.
Post Reply
Stonemonkey
Posts: 649
Joined: Jun 09, 2005 0:08

Re: Proper Chess Engine with networked CPUs & LAN play

Post by Stonemonkey »

Here's something I've put together, it's pretty rough and doesn't yet allow castling or en passant though it's set up to recognise that and shouldn't be too much of a problem to add it in. Something that might be a bit more work is check-mate/stale-mate conditions.

It doesn't do any scoring for moves yet, just looks for valid moves and can be set to any depth although it gets slow.

Code: Select all

'piece bits
'
'0-2 -> piece (pawn=1,rook=2,knight=3,bishop=4,queen=5,king=6)
'3   -> colour (0=black,8=white)
'4   -> piece moved (0=moved,16=not moved)
'5   -> pawn moved 2 on first move and not moved again (en passant)
board_data:'board startup position
'black (17=pawn->22=king)
data 18,19,20,21,22,20,19,18
data 17,17,17,00,17,17,17,17
data 00,00,00,00,00,00,00,00
data 00,00,00,01,00,00,00,00
data 00,00,00,00,09,00,00,00
data 00,00,00,00,00,00,00,00
data 25,25,25,25,00,25,25,25
data 26,27,28,29,30,28,27,26
'white(25=pawn->30=king)
'
'When a piece is moved, bits 4 and 5(=16 and 32) are set to 0.
'Exception - if a pawn is moved forward by 2 places on it's
'first move bit 5 is set to 1
'
dim shared as string piece_name(0 to 7)
piece_name(1)="pawn"
piece_name(2)="rook"
piece_name(3)="kngt"
piece_name(4)="bish"
piece_name(5)="quen"
piece_name(6)="king"

type board
    square(0 to 7,0 to 7) as integer
    score(0 to 7,0 to 7,0 to 7,0 to 7)as integer
end type

function create_new_board()as board ptr
    dim as board ptr board=new board
    restore board_data
    for y as integer=7 to 0 step-1
        for x as integer=0 to 7 step 1
            read board->square(x,y)
        next
    next
    function=board
end function

sub copy_board(byval dst as board ptr,_
                byval src as board ptr)
    for y as integer=0 to 7
        for x as integer=0 to 7
            dst->square(x,y)=src->square(x,y)
        next
    next
end sub

function validate_move(byval board as board ptr,_
                    byval x as integer,_
                    byval y as integer,_
                    byval x_dest as integer,_
                    byval y_dest as integer,_
                    byval colour as integer)as integer
    dim as integer valid=0
    if ((board->square(x,y)shr 3)and 1)=colour then
        if (x<>x_dest)or(y<>y_dest) then
            if (((board->square(x_dest,y_dest)shr 3)and 1)<>colour)or _
                (board->square(x_dest,y_dest)=0) then
        
                select case as const (board->square(x,y)and 7)
        
                    case 1'pawn
valid=0
if colour=0 then
    if (x=x_dest) then
        if (y=y_dest+1)and(board->square(x_dest,y_dest)=0)then valid=1
        if y_dest<7 then
            if (y=y_dest+2)and(board->square(x_dest,y_dest)=0)and _
                (board->square(x_dest,y_dest+1)=0)and _
                ((board->square(x,y)and 16)=16) then valid=1
        end if
    else
        if (abs(x-x_dest)=1)and(y=y_dest+1)and _
            (board->square(x_dest,y_dest)>0) then valid=1
    end if
else
    if (x=x_dest) then
        if (y=y_dest-1)and(board->square(x_dest,y_dest)=0)then valid=1
        if y_dest>0 then
            if (y=y_dest-2)and(board->square(x_dest,y_dest)=0)and _
                (board->square(x_dest,y_dest-1)=0)and _
                ((board->square(x,y)and 16)=16) then valid=1
        end if
    else
        if (abs(x-x_dest)=1)and(y=y_dest-1)and _
            (board->square(x_dest,y_dest)>0) then valid=1
    end if
end if
                case 2'rook
valid=1
if (x=x_dest)or(y=y_dest) then
    dim as integer cx=x,cy=y
    if x_dest<cx then cx-=1
    if x_dest>cx then cx+=1
    if y_dest<cy then cy-=1
    if y_dest>cy then cy+=1
    while (cx<>x_dest)or(cy<>y_dest)
        if board->square(cx,cy)<>0 then valid=0
        if x_dest<cx then cx-=1
        if x_dest>cx then cx+=1
        if y_dest<cy then cy-=1
        if y_dest>cy then cy+=1
    wend
else
    valid=0
end if
                    case 3'knight
valid=1
dim as integer dx=abs(x-x_dest),dy=abs(y-y_dest)
if ((dx=1)and(dy=2))or((dx=2)and(dy=1)) then
    valid=1
else
    valid=0
end if
                    case 4'bishop
valid=1
if ((x+y)=(x_dest+y_dest))or((x-y)=(x_dest-y_dest)) then
    dim as integer cx=x,cy=y
    if x_dest<cx then cx-=1
    if x_dest>cx then cx+=1
    if y_dest<cy then cy-=1
    if y_dest>cy then cy+=1
    while (cx<>x_dest)and(cy<>y_dest)
        if board->square(cx,cy)<>0 then valid=0
        if x_dest<cx then cx-=1
        if x_dest>cx then cx+=1
        if y_dest<cy then cy-=1
        if y_dest>cy then cy+=1
    wend
else
    valid=0
end if
                    case 5'queen
valid=1
if ((x+y)=(x_dest+y_dest))or((x-y)=(x_dest-y_dest)) then
    dim as integer cx=x,cy=y
    if x_dest<cx then cx-=1
    if x_dest>cx then cx+=1
    if y_dest<cy then cy-=1
    if y_dest>cy then cy+=1
    while (cx<>x_dest)and(cy<>y_dest)
        if board->square(cx,cy)<>0 then valid=0
        if x_dest<cx then cx-=1
        if x_dest>cx then cx+=1
        if y_dest<cy then cy-=1
        if y_dest>cy then cy+=1
    wend
else
    if (x=x_dest)or(y=y_dest) then
        dim as integer cx=x,cy=y
        if x_dest<cx then cx-=1
        if x_dest>cx then cx+=1
        if y_dest<cy then cy-=1
        if y_dest>cy then cy+=1
        while (cx<>x_dest)or(cy<>y_dest)
            if board->square(cx,cy)<>0 then valid=0
            if x_dest<cx then cx-=1
            if x_dest>cx then cx+=1
            if y_dest<cy then cy-=1
            if y_dest>cy then cy+=1
        wend
    else
        valid=0
    end if
end if
                    case 6'king
valid=1
if (abs(x-x_dest)>1)or(abs(y-y_dest)>1) then valid=0
                end select
            end if
        end if
    end if
    function=valid
end function

sub move_piece(byval board as board ptr,_
                    byval x as integer,_
                    byval y as integer,_
                    byval x_dest as integer,_
                    byval y_dest as integer,_
                    byval colour as integer)
    
    board->square(x_dest,y_dest)=board->square(x,y) and &hcf
    if (abs(y-y_dest)=2)and((board->square(x,y)and 7)=1) then board->square(x_dest,y_dest) or=32
    board->square(x,y)=0
end sub

sub score_moves(byval board as board ptr,_
                byval colour as integer,_
                byval depth as integer)
    if depth>0 then
        dim as board temp
        for y as integer=0 to 7
            for x as integer=0 to 7
                if ((board->square(x,y) shr 3)and 1)=colour then
                    for y_dest as integer=0 to 7
                        for x_dest as integer=0 to 7
                            if validate_move(board,x,y,x_dest,y_dest,colour)=1 then
                                
                                if colour=1 then 
                                    print string((6-depth)*2," ");"white ";
                                else
                                    print string((6-depth)*2," ");"black ";
                                end if
                                print piece_name(board->square(x,y)and 7);" ";x;y;" ->";x_dest;y_dest;
                                if board->square(x_dest,y_dest)>0 then 
                                    print " takes ";
                                    if colour=1 then 
                                        print "black ";
                                    else
                                        print "white ";
                                    end if
                                    print piece_name(board->square(x_dest,y_dest)and 7)
                                else
                                    print
                                end if
                                copy_board(@temp,board)
                                move_piece(@temp,x,y,x_dest,y_dest,colour)
                                score_moves(@temp,colour xor 1,depth-1)
                            end if
                        next
                    next
                end if
            next
        next
    end if
end sub


sub main
    dim as board ptr board=create_new_board()
    score_moves(board,1,1)'(board,colour(0=black,1-white),depth)
end sub

main
print"done"
sleep
end

EDIT: fixed some bugs
Stonemonkey
Posts: 649
Joined: Jun 09, 2005 0:08

Re: Proper Chess Engine with networked CPUs & LAN play

Post by Stonemonkey »

Something I hadn't thought about was promotion, the move of the pawn to the square on the last row should be seen as 4 different moves as the pawn can be promoted to any of the 4 pieces. Gonna have to have a think about how to do that.
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

Re: Proper Chess Engine with networked CPUs & LAN play

Post by BasicCoder2 »

@stonemonkey
Are you aware of previous examples?
The minimax translation is the fastest and best chess engine I think.

http://www.freebasic.net/forum/viewtopi ... ax#p169820

http://www.freebasic.net/forum/viewtopi ... ilit=chess

http://www.freebasic.net/forum/viewtopi ... ilit=chess

GUI Interface for minimax translation to FreeBasic
http://www.freebasic.net/forum/viewtopi ... ilit=chess
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

Re: Proper Chess Engine with networked CPUs & LAN play

Post by BasicCoder2 »

.
Last edited by BasicCoder2 on Dec 08, 2012 22:25, edited 1 time in total.
Stonemonkey
Posts: 649
Joined: Jun 09, 2005 0:08

Re: Proper Chess Engine with networked CPUs & LAN play

Post by Stonemonkey »

Thanks, I'm having a look through that now.
TESLACOIL
Posts: 1769
Joined: Jun 20, 2010 16:04
Location: UK
Contact:

Re: Proper Chess Engine with networked CPUs & LAN play

Post by TESLACOIL »

Numpty is written in freebasic
https://sites.google.com/site/numptyche ... s-versions

The source code as approx 6000 lines long so i cant paste it here. It has some winboard stuff in it to

move format is e2e4 no spaces


The problem with all FB chess programs ive seen is failure to generate legal moves or failure to be aware of all the rules. Im not sure if Numpty plays 100% legal chess, after reading through the code and notes it looks like it might have a few obscure 'illegal move' bugs still.

If you play chess such illegal bugs are simply unacceptable and a throw back to the 1980s a time when most commercial programs had epic fail bugs.

A test set of positions/games is required for debugging purposes.

Common bugs

Castling through lines of check ( queen-side)
Failing to detect check on promotion
Failure to detect 3 fold repetition
50 move rule

En passant bugs ( numerous )
Castling bugs ( numerous )
Promotion bugs ( numerous )
Ignoring checks altogether



Basic Standard

If you had a 100% legal chess engine that did nothing more than count the value of the pieces at the end of a 6 ply brute force search then this would be of sufficient strength to beat 99% of all humans.
Last edited by TESLACOIL on Dec 02, 2012 20:38, edited 1 time in total.
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

Re: Proper Chess Engine with networked CPUs & LAN play

Post by BasicCoder2 »

TESLACOIL wrote:Numpty is written in freebasic
https://sites.google.com/site/numptyche ... s-versions
Interesting I haven't seen that before or the tutorial link given,
http://eli.thegreenplace.net/programs-and-code/jamca/
I will keep it in case I decide to revisit writing a chess program.

The problem with all FB chess programs ive seen is failure to generate legal moves or failure to be aware of all the rules.
These are minor and uninteresting with regards to understanding the guts of the program which is a search for good moves using opening books, alphabeta pruning of a game tree and so on... It is an issue for a commercial program but I don't see any problem in fixing the bugs as they are found.
TESLACOIL
Posts: 1769
Joined: Jun 20, 2010 16:04
Location: UK
Contact:

Re: Proper Chess Engine with networked CPUs & LAN play

Post by TESLACOIL »

100% legal chess or no is NOT a minor issue

if your prog doesn't play legal chess then it isn't playing chess, its playing something else END OF !



While I appreciate that many chess efforts are just explorations they are of very doubtful value to anyone if they do not play legal 100% chess. A search algorithm which occasionally emits garbage is just a garbage emitter

which is why the this project is entitled Proper Chess engine thank you very much

so with the greatest of respect don't tell me that a 100% legal move generator is easy in one breath and then in another tell me its unimportant which is why you haven't bothered to do it.
Last edited by TESLACOIL on Dec 02, 2012 22:04, edited 3 times in total.
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

Re: Proper Chess Engine with networked CPUs & LAN play

Post by BasicCoder2 »

.
Last edited by BasicCoder2 on Dec 08, 2012 22:26, edited 1 time in total.
TESLACOIL
Posts: 1769
Joined: Jun 20, 2010 16:04
Location: UK
Contact:

Re: Proper Chess Engine with networked CPUs & LAN play

Post by TESLACOIL »

The topic of this project is
Proper Chess Engine
aka 100% bug free move generation.

Your input is still welcome Basic coder, but please understand my projects primary mission = 100% bug free move generation.
Now if i can do this then everyone else can indulge in the fun part of search and analysis AND their work will be of value.
If you read the very first post I clearly outline what i am doing, why i am doing it, and how i am doing it.


Im being here pedantic with very good cause,

That cause is to create a 'proper chess engine' in FreeBASIC that I and others can use with 100% confidence.
The future of chess = a suite of simple plug n play utilities than can run on a network, that's what im building here. First steps.
High utility network capable software that end users , hobby coders and serious chess players can and will use.
These tools do not yet exist, cloned engines are banned from competition, this is why im forced to build from scratch.




Networked Computers with Aquarium.
http://chessok.com/?p=23402

Multiple engines used for analysis but not unified or optimized for play. This is the next logical step and the future of chess.
Stonemonkey
Posts: 649
Joined: Jun 09, 2005 0:08

Re: Proper Chess Engine with networked CPUs & LAN play

Post by Stonemonkey »

I would forget about getting it running across a network until I had something to run, get something that is able to play first then figure out how the work can be divided up.
TESLACOIL
Posts: 1769
Joined: Jun 20, 2010 16:04
Location: UK
Contact:

Re: Proper Chess Engine with networked CPUs & LAN play

Post by TESLACOIL »

Ive already got the network stuff solved. My AI runs on a network. Ill be using a similar regime to that. The networking bit for chess is as simple as pie. My AI system far more demanding so i already know its 'good to go' for chess.

Analyzing board positions is a pretty straight forward counting game. The pruning bit the dark art.



Doing 100% legal move generation , and doing it reasonably efficiently is the key challenge for me

...im still in head scratching mode here, get this right and know everything else will flow


1) the 'full status' of the board has to be emitted, not just the current position of the pieces
2) live enpassant states, castling options, 50move counter, 3fold repetition counter etc

This is a good webby/guide
http://chessprogramming.wikispaces.com/Getting+Started
Richard
Posts: 3096
Joined: Jan 15, 2007 20:44
Location: Australia

Re: Proper Chess Engine with networked CPUs & LAN play

Post by Richard »

Making valid Chess moves is both a mathematical and a programming challenge. If the moves are not clearly defined, or the programming is sloppy, then the code will not generate legally valid Chess moves.

@ TESLACOIL.
So why invest effort arguing that “validity” is important. Is it not better to write a move generator that generates all, and only valid moves. Then any misbegotten generator can be shown to be faulty by comparison. This project will remain a dream until you can generate functional code that makes valid moves. Only then can a LAN come into play.

It appears that you have opened this onanistic project thread prematurely. If you continue to criticise respondents then you will only stall and never progress. Prove me wrong by;

1. Defining a usable UDT “board” object.
2. Coding a module that generates every valid next board for either a Black or for a White move.
TESLACOIL
Posts: 1769
Joined: Jun 20, 2010 16:04
Location: UK
Contact:

Re: Proper Chess Engine with networked CPUs & LAN play

Post by TESLACOIL »

ref " Is it not better to write a move generator that generates all, and only valid moves."

I agree 101%....the last thing you want to do is to have to rewrite your move generator, thats basically game over in my book


Im now studying in fine detail of the computational requirements for 100% legal move generation. Architecture and method important as it has knock on effects. There are many methods as you know, FreeBASIC is a little slow compared to some languages so it cant be too sloppy speedwise.

Ive already written several move generators in the past but they are incomplete or too slow or have a clunky format. This is why im slowly grinding through this key issue.
gothon
Posts: 225
Joined: Apr 11, 2011 22:22

Re: Proper Chess Engine with networked CPUs & LAN play

Post by gothon »

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.
http://vast3d.com/schess/SimulChess.exe
Optional Board/Piece Tilesets: http://vast3d.com/schess/Board.png http://vast3d.com/schess/TileSet.png

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.

modChess.vb

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
Last edited by gothon on Dec 02, 2012 22:58, edited 1 time in total.
Post Reply