' Declaration of Subroutines
DECLARE SUB PrintLogo ()
DECLARE SUB Initialize ()
DECLARE SUB CommandLoop ()
DECLARE FUNCTION AlphaBeta% (Alpha%, Beta%, Distance%)
DECLARE SUB GenerateMoves (AllMoves%)
DECLARE SUB PerformMove (CurrMove%)
DECLARE SUB TakeBackMove (CurrMove%)
DECLARE SUB CopyMainVariant (CurrMove%)
DECLARE FUNCTION AttackingField% (Feld%, Side%)
DECLARE FUNCTION AssessPosition% (Alpha%, Beta%, Side%)
DECLARE SUB InitAssessment ()
DECLARE SUB DisplayBoard (BoardOnly%)
DECLARE SUB ComputerMove ()
DECLARE SUB InitGameTree ()
DECLARE SUB DisplayMove (CurrMove%)
DECLARE SUB DisplayMVar ()
DECLARE SUB PrintMove (CurrMove%)
DECLARE SUB PrintPosition ()
DECLARE SUB PrintBack ()
DECLARE FUNCTION Fieldnotation$ (Fieldnum%)
DECLARE FUNCTION Fieldnumber% (Fieldnote$)
DECLARE SUB SavePromotion (from%, too%)
DECLARE SUB SaveMove (from%, too%)
DECLARE SUB SaveCaptureMove (from%, too%)
DECLARE SUB SaveEpMove (from%, too%, ep%)
DECLARE SUB FlipBoard ()
DECLARE SUB GameOver ()
DECLARE SUB InputPosition ()
DECLARE SUB MoveList ()
DECLARE SUB MoveBack ()
DECLARE SUB ComputingDepth ()
DECLARE SUB ReadPiece (Side%)
DECLARE FUNCTION NextBestMove% ()
DECLARE FUNCTION BPAssessment% (Feld%, Column%, row%, developed%)
DECLARE FUNCTION WPAssessment% (Feld%, Column%, row%, developed%)
DECLARE FUNCTION InputMove% (Move$)
'--------------------------------------------------------------------
' Definition of symbolic Constants.
'--------------------------------------------------------------------
CONST BoardDim% = 119 ' Dimension of Expanded Chess Board
CONST MaxDepth% = 19 ' Maximum search depth
CONST Movedirections% = 15 ' Number of Move directions for all Piece.
CONST PieceTypes% = 6 ' Number of Piecetypes - considering
' the Move directions (wQueen = bQueen)
CONST MoveStackDim% = 1000 ' Dimension of move stacks
' Piece
CONST BK% = -6 ' Black piece
CONST BQ% = -5
CONST BN% = -4
CONST BB% = -3
CONST BR% = -2
CONST BP% = -1
CONST Empty% = 0 ' Empty field
CONST WP% = 1 ' White piece
CONST WR% = 2
CONST WB% = 3
CONST WN% = 4
CONST WQ% = 5
CONST WK% = 6
CONST Edge% = 100 ' The edge of the chess board
' Material value of the Pieces
CONST MatP% = 100
CONST MatR% = 500
CONST MatB% = 350
CONST MatN% = 325
CONST MatQ% = 900
CONST MatK% = 0 ' As both Sides have just one king,
' the value can be set to 0
' Assessment for Mate
CONST MateValue% = 32000
CONST MaxPos% = MatB% ' Maximum of the position assessment
' Bonus for main variants and Killer moves
' used for the sorting of moves
CONST MainVariantBonus% = 500
CONST Killer1Bonus% = 250
CONST Killer2Bonus% = 150
' Total material value in the initial position
CONST MaterialSum% = 4 * (MatR% + MatB% + MatN%) + (2 * MatQ%)
CONST EndgameMaterial% = 4 * MatR% + 2 * MatB%
' Field numbers of frequently used Fields
' ("if Board(E1%)=WK%" means "if Board(25)=6")
CONST A1% = 21
CONST B1% = 22
CONST C1% = 23
CONST D1% = 24
CONST E1% = 25
CONST F1% = 26
CONST G1% = 27
CONST H1% = 28
CONST C2% = 33
CONST H2% = 38
CONST A3% = 41
CONST C3% = 43
CONST D3% = 44
CONST E3% = 45
CONST A6% = 71
CONST C6% = 73
CONST D6% = 74
CONST E6% = 75
CONST H6% = 78
CONST A7% = 81
CONST C7% = 83
CONST H7% = 88
CONST A8% = 91
CONST B8% = 92
CONST C8% = 93
CONST D8% = 94
CONST E8% = 95
CONST F8% = 96
CONST G8% = 97
CONST H8% = 98
' Values of columns and rows
CONST ARow% = 1
CONST BRow% = 2
CONST CRow% = 3
CONST DRow% = 4
CONST ERow% = 5
CONST FRow% = 6
CONST GRow% = 7
CONST HRow% = 8
CONST Column1% = 2
CONST Column2% = 3
CONST Column3% = 4
CONST Column4% = 5
CONST Column5% = 6
CONST Column6% = 7
CONST Column7% = 8
CONST Column8% = 9
' Castling numbering (Index into castling array)
' of move is not a castling move
CONST NoCastlingMove% = 0
CONST ShortCastlingMove% = 1
CONST LongCastlingMove% = 2
' Color of the man who is moving
CONST White% = 1
CONST Black% = -1
' Symbolic logical Constants
CONST True% = 1
CONST False% = 0
CONST Legal% = 1
CONST Illegal% = 0
'-------------------------------------------------------------------
' Definition of data types.
'-------------------------------------------------------------------
' Information for one move, the data type of the move stacks
TYPE MoveType
from AS INTEGER ' From field
too AS INTEGER ' To field
CapturedPiece AS INTEGER ' Captured piece
PromotedPiece AS INTEGER ' Promoted piece
CastlingNr AS INTEGER ' Type of castling move
EpField AS INTEGER ' Enpassant field
Value AS INTEGER ' Assessment for the sorting of moves
END TYPE
' Index of the pieces in the offset list and long/short paths
' (used by the move generator)
TYPE PieceOffsetType
Start AS INTEGER
Ends AS INTEGER
Longpaths AS INTEGER
END TYPE
' Information on pawn/piece constellations
TYPE BothColourTypes
White AS INTEGER
Black AS INTEGER
END TYPE
' Information on From/Too field (Moves without additional Information)
' Used for the storing promising moves in (main variants,
' killer moves)
TYPE FromTooType
from AS INTEGER
too AS INTEGER
END TYPE
' Data structure for storing killer moves.
TYPE KillerType
Killer1 AS FromTooType
Killer2 AS FromTooType
END TYPE
'--------------------------------------------------------------------
' Definition of global variables and tables
'--------------------------------------------------------------------
DIM SHARED Board(BoardDim%) AS INTEGER
DIM SHARED EpField(MaxDepth%) AS INTEGER
DIM SHARED MoveStack(MoveStackDim%) AS MoveType
DIM SHARED MoveControl(H8%) AS INTEGER
' Counts how often a piece has moved from
' a field. Used to determine castling
' rights (also useable for
' Assessment)
DIM SHARED Castling(2) AS INTEGER ' Has White/Black already Castled?
DIM SHARED Index AS INTEGER ' Index in MoveStack
' Saves the position in the MoveStack. Moves of Depth 'n' are stored in
' range (StackLimit(n), StackLimit(n+1)) in MoveStack.
DIM SHARED StackLimit(MaxDepth%) AS INTEGER
DIM SHARED MVar(MaxDepth%, MaxDepth%) AS FromTooType ' Main variants table
DIM SHARED KillerTab(MaxDepth%) AS KillerType ' Killer moves table
' Tables for Assessment function
DIM SHARED PawnControlled(BoardDim%) AS BothColourTypes ' Fields that are
' controlled by pawns
DIM SHARED Pawns(HRow% + 1) AS BothColourTypes ' Number of pawns per row
DIM SHARED Rooks(HRow% + 1) AS BothColourTypes ' Number of rooks per row
DIM SHARED Mobility(MaxDepth%) AS INTEGER ' Mobility of bishops and rooks
DIM SHARED TooFeld(MaxDepth%) AS INTEGER ' TooField of the moves`, used for
' Sorting of moves and for extension
' of searches
DIM SHARED wKing AS INTEGER ' Position of the white king
DIM SHARED bKing AS INTEGER ' Position of the black king
DIM SHARED MaterialBalance(MaxDepth%) AS INTEGER ' Material balance between White/Black
DIM SHARED MaterialTotal(MaxDepth%) AS INTEGER ' Total material on board
DIM SHARED Colour AS INTEGER ' Who is to make a move
DIM SHARED PlayerPlayer AS INTEGER ' Player vs Player (Memo)Mode on/off
DIM SHARED Printing AS INTEGER ' Printing moves off/on
DIM SHARED MinDepth AS INTEGER ' Generally searches are performed
' until MinDepth
DIM SHARED MaxExtension AS INTEGER ' Extensions in search tree because of
' Checks and Captures are only carried out
' until MaxExtension (otheriwse the
' search can explode)
DIM SHARED Depth AS INTEGER ' Search depth = the number of half moves
' from the initial position
DIM SHARED NodeCount AS LONG ' Number of examined positions/nodes
DIM SHARED LastMove AS INTEGER ' Last performed move
DIM SHARED InCheck AS INTEGER ' Player is being checked
DIM SHARED MoveCount AS INTEGER ' Number of half moves done so far
DIM SHARED IsWhiteLast AS INTEGER ' For printing control
' InitialPosition of 10 by 12 Board
DIM SHARED InitialPosition(BoardDim%) AS INTEGER
FOR i% = 0 TO BoardDim%
READ InitialPosition(i%)
NEXT i%
DATA 100,100,100,100,100,100,100,100,100,100
DATA 100,100,100,100,100,100,100,100,100,100
DATA 100, 2 , 4 , 3 , 5 , 6 , 3 , 4 , 2 ,100
DATA 100, 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,100
DATA 100, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,100
DATA 100, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,100
DATA 100, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,100
DATA 100, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,100
DATA 100,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,100
DATA 100,-2 ,-4 ,-3 ,-5 ,-6 ,-3 ,-4 ,-2 ,100
DATA 100,100,100,100,100,100,100,100,100,100
DATA 100,100,100,100,100,100,100,100,100,100
' Move generator tables
DIM SHARED Offset(Movedirections%) AS INTEGER
Offset(0) = -9 ' Diagonal paths
Offset(1) = -11
Offset(2) = 9
Offset(3) = 11
Offset(4) = -1 ' Straight paths
Offset(5) = 10
Offset(6) = 1
Offset(7) = -10
Offset(8) = 19 ' Knight paths
Offset(9) = 21
Offset(10) = 12
Offset(11) = -8
Offset(12) = -19
Offset(13) = -21
Offset(14) = -12
Offset(15) = 8
DIM SHARED FigOffset(PieceTypes%) AS PieceOffsetType
FigOffset(Empty%).Start = 0 ' Empty field
FigOffset(Empty%).Ends = 0
FigOffset(Empty%).Longpaths = False%
FigOffset(WP%).Start = -1 ' Pawn Moves are produced seperately
FigOffset(WP%).Ends = -1
FigOffset(WP%).Longpaths = False%
FigOffset(WR%).Start = 4 ' Rook
FigOffset(WR%).Ends = 7
FigOffset(WR%).Longpaths = True%
FigOffset(WB%).Start = 0 ' Bishop
FigOffset(WB%).Ends = 3
FigOffset(WB%).Longpaths = True%
FigOffset(WN%).Start = 8 ' Knight
FigOffset(WN%).Ends = 15
FigOffset(WN%).Longpaths = False%
FigOffset(WQ%).Start = 0 ' Queen
FigOffset(WQ%).Ends = 7
FigOffset(WQ%).Longpaths = True%
FigOffset(WK%).Start = 0 ' King
FigOffset(WK%).Ends = 7
FigOffset(WK%).Longpaths = False%
' Centralization tables. We only need files 0..H8, as
' piece can't stand on a field outside H8.
' The lower edge is preserved as we would otherwise have to
' transform board coordinates into centrality coordinates.
' H1 is is further away from the center than is G1. In spite of this,
' H1 has a better center value than G1.
' This Table is used i.e. for king Assessment.
' The Values of G1,H1 imply that the king remains on G1
' after castling and doesn't perform the unnecessary move G1-H1.
' (The knight is neither very well placed on G1 nor H1).
DIM SHARED CenterTable(H8%) AS INTEGER
FOR i% = 0 TO H8%
READ CenterTable(i%)
NEXT i%
' --- A B C D E F G H ---
DATA 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
DATA 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
DATA 0, 4, 0, 8, 12, 12, 8, 0, 4, 0
DATA 0, 4, 8, 12, 16, 16, 12, 8, 4, 0
DATA 0, 8, 12, 16, 20, 20, 16, 12, 8, 0
DATA 0, 12, 16, 20, 24, 24, 20, 16, 12, 0
DATA 0, 12, 16, 20, 24, 24, 20, 16, 12, 0
DATA 0, 8, 12, 16, 20, 20, 16, 12, 8, 0
DATA 0, 4, 8, 12, 16, 16, 12, 8, 4, 0
DATA 0, 4, 0, 8, 12, 12, 8, 0, 4
' Assessment of the fields for the pawns.
' Is used the position assessment.
' Center pawns on the 2nd row is bad (they belong in the front).
' F-H pawns should be behind for protection of the king.
DIM SHARED wPFieldValue(H7%) AS INTEGER ' White pawns
FOR i% = 0 TO H7%
READ wPFieldValue(i%)
NEXT i%
' --- A B C D E F G H ---
DATA 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
DATA 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
DATA 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
DATA 0, 4, 4, 0, 0, 0, 6, 6, 6, 0
DATA 0, 6, 6, 8, 8, 8, 4, 6, 6, 0
DATA 0, 8, 8, 16, 22, 22, 4, 4, 4, 0
DATA 0, 10, 10, 20, 26, 26, 10, 10, 10, 0
DATA 0, 12, 12, 22, 28, 28, 14, 14, 14, 0
DATA 0, 18, 18, 28, 32, 32, 20, 20, 20
' No pawn can stay on the 8th row.
DIM SHARED bPFieldValue(H7%) AS INTEGER ' Black pawns
FOR i% = 0 TO H7%
READ bPFieldValue(i%)
NEXT i%
' --- A B C D E F G H ---
DATA 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
DATA 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
DATA 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
DATA 0, 18, 18, 28, 32, 32, 20, 20, 20, 0
DATA 0, 12, 12, 22, 28, 28, 14, 14, 14, 0
DATA 0, 10, 10, 20, 26, 26, 10, 10, 10, 0
DATA 0, 8, 8, 16, 22, 22, 4, 4, 4, 0
DATA 0, 6, 6, 8, 8, 8, 4, 6, 6, 0
DATA 0, 4, 4, 0, 0, 0, 6, 6, 6, 0
' No pawn can stay on the 8th row.
' Material value of the pieces
DIM SHARED PieceMaterial(PieceTypes%) AS INTEGER
PieceMaterial(Empty%) = 0 ' Emptyfield
PieceMaterial(WP%) = MatP% ' Pawn
PieceMaterial(WR%) = MatR% ' Rook
PieceMaterial(WB%) = MatB% ' Bishop
PieceMaterial(WN%) = MatN% ' Knight
PieceMaterial(WQ%) = MatQ% ' Queen
PieceMaterial(WK%) = MatK% ' King
' Symbolic representation of the pieces
DIM SHARED FigSymbol(PieceTypes%) AS STRING * 1
FigSymbol(Empty%) = "." ' Emptyfield
FigSymbol(WP%) = "P" ' Pawn
FigSymbol(WR%) = "R" ' Rook
FigSymbol(WB%) = "B" ' Bishop
FigSymbol(WN%) = "N" ' Knight
FigSymbol(WQ%) = "Q" ' Queen
FigSymbol(WK%) = "K" ' King
' Symbolic representations of the pieces for printing
DIM SHARED PrintSymbol(PieceTypes%) AS STRING * 1
PrintSymbol(Empty%) = " " ' Emptyfield
PrintSymbol(WP%) = " " ' Pawn
PrintSymbol(WR%) = "R" ' Rook
PrintSymbol(WB%) = "B" ' Bishop
PrintSymbol(WN%) = "N" ' Knight
PrintSymbol(WQ%) = "Q" ' Queen
PrintSymbol(WK%) = "K" ' King
' Color symbols
DIM SHARED ColourSymbol(2) AS STRING * 1
ColourSymbol(0) = "." ' Black
ColourSymbol(1) = "." ' Empty field
ColourSymbol(2) = "*" ' White
'--------------------------------------------------------------------
' The actual program begins here.
'--------------------------------------------------------------------
CLS
'CALL PrintLogo
CALL Initialize
CALL CommandLoop
'--------------------------------------------------------------------
' Here ends the Program
'--------------------------------------------------------------------
'--------------------------------------------------------------------
' AlphaBeta: Function
' Alpha-Beta Tree search
' Returns Assessment from the viewpoint of the player who is to make
' a move. "Alpha" is lower limit, "Beta" is the upper limit and "Distance"
' the Number of half-moves until the horizon.
' If "Distance" positive, a normal Alpha-Beta search is performed,
' if less than 0 the quiescense search.
' Returns the NegaMax-Value form the point of view of the player who is
' to make a move.
' This procedure is called recursively.
' Locale Variables: i%, Value%, BestValue%, Check%
' Calls: GenerateMoves; PerformMove; TakeBackMove;CopyMainVariant;
' AssessPosition;NextBestMove;
' Calledby: ComputerMove
'---------------------------------------------------------------------
FUNCTION AlphaBeta% (Alpha%, Beta%, Distance%)
NodeCount = NodeCount + 1 ' Additional position examined
MVar(Depth, Depth).from = 0 ' Delete current main variant
' Position is always assessed, i.e. also inside of tree.
' This is necessary to recognize checkmate and stalemate. Also,
' the assessment is used to control search/extension.
' The number of nodes inside the tree is much smaller than that at
' the Horizon. i.e. the program does not become significantly slower
' because of that.
' Assessment from the viewpoint of the player who is to make a move
Value% = AssessPosition%(Alpha%, Beta%, Colour)
' In the case of check, the search is extended, by up to four
' half moves total. Otherwise it may happen that the search tree
' becomes extremely large thru mutual checks and capture sequences.
' As a rule, these move sequences are completely meaningless.
Check% = InCheck
' Side that is moving is in check, extend the search
Condition1% = (Check% = True% AND Depth + Distance% < MaxExtension + 1)
' By capture and re-capture on the same field, the search is
' extended if the material balance remains approximately
' the same and we didn't make too many extensions
' so far.
Condition2% = (Depth >= 2 AND Depth + Distance% < MaxExtension)
Condition2% = Condition2% AND TooFeld(Depth) = TooFeld(Depth - 1)
Condition2% = Condition2% AND Value% >= Alpha% - 150 AND Value% <= Beta% + 150
IF Condition1% OR Condition2% THEN Distance% = Distance% + 1
' If more than 5 moves were already performed in the quiescense search
' or the opponent is checkmated or we have reached maximunm search
' depth imposed by data structures, end the search.
IF Distance% < -5 OR Value% = MateValue% - Depth OR Depth >= MaxDepth% THEN
AlphaBeta% = Value%
EXIT FUNCTION
END IF
' If during the quiescence search - the player who is to move has a
' good position, the search is aborted since by definition the value
' can only become beter during the quiescense search.
' Warning: Aborts already at Distance 1, i.e. a half-move before the
' horizon, in case the player who is to move is not
' being checked. This is a selective deviation from
' the brute-force-Alpha-Beta scheme.
IF Value% >= Beta% AND Distance% + Check% <= 1 THEN
AlphaBeta% = Value%
EXIT FUNCTION
END IF
' Compute Moves. If Distance <= 0 then (quiescense search) only
' capture moves and promotion moves are computed.
CALL GenerateMoves(Distance%) ' Examine if any moves are available
IF Distance% > 0 THEN ' is directly done by determining
BestValue% = -MateValue% ' BestValue.
ELSE ' In quiescence search, the current
BestValue% = Value% ' position assessment is the lower limit
END IF ' of the search value.
i% = NextBestMove% ' Examine all moves in sorted sequence.
DO WHILE i% >= 0 ' So Long as any moves are left.
CALL PerformMove(i%)
' NegaMax principal: The sign is reversed and
' the roles of alpha and beta exchanged.
Value% = -AlphaBeta(-Beta%, -Alpha%, Distance% - 1)
CALL TakeBackMove(i%)
IF Value% > BestValue% THEN ' new best value found
BestValue% = Value%
IF Value% >= Beta% THEN ' Cutoff found
' Inside the tree, the main variants are still saved
IF Distance% > 0 THEN CALL CopyMainVariant(i%)
GOTO Done
END IF
IF Value% > Alpha% THEN ' Value is the improved lower limit
IF Distance% > 0 THEN CALL CopyMainVariant(i%) ' Main variants Saved
Alpha% = Value% ' Improved alpha value
END IF
END IF
i% = NextBestMove%
LOOP
Done:
' A good move showing cutoff is entered into the killer table.
' Keep the best killer so far as the 2nd best killer.
IF Value% >= Beta% AND i% >= 0 THEN
KillerTab(Depth).Killer2 = KillerTab(Depth).Killer1
KillerTab(Depth).Killer1.from = MoveStack(i%).from
KillerTab(Depth).Killer1.too = MoveStack(i%).too
END IF
' If player has no more legal moves...
IF BestValue% = -(MateValue% - (Depth + 1)) THEN
IF Check% = False% THEN ' ... but isn't being checked
AlphaBeta% = 0 ' it's stalemate
EXIT FUNCTION
END IF
END IF
AlphaBeta% = BestValue%
END FUNCTION
'--------------------------------------------------------------------
' AssessPosition: Function
' PositionsAssessment
' Returns value from the viewpoint of "Side".
' If material value deviates too far from the Alpha-Beta window
' only material is assessed.
' If "Side" is checkmating, returns (CheckMateValue -Depth).
' If "Side" is being checked, the variable InCheck is changed
' to "True".
' Warning: The Function assumes, both for check/checkmate and for the
' king opposition, that "Side" is the player who is to make move.
'
' Local Variables:
' Value%, PosValue%, i%, j%, k%, Feld%, wBishlop%, bBishlop%
' PawnCount%, MatNum%, wRookon7%, bRookon2%
' wDeveloped%, bDeveloped%
' Calls: InitAssessment; PerformMove; TakeBackMove; AttackingField;
' Calledby: AttackingField; BPAssessment; QPAssessment; CommandLoop;
' AlphaBeta; ComputerMove
'--------------------------------------------------------------------
FUNCTION AssessPosition% (Alpha%, Beta%, Side%)
' First examine if opponent is checkmated
' of "Side" is being checked.
IF Side% = White% THEN
IF AttackingField%(bKing, White%) = True% THEN
AssessPosition% = MateValue% - Depth
EXIT FUNCTION
END IF
InCheck = AttackingField%(wKing, Black%) ' Is white being checked?
ELSE
IF AttackingField%(wKing, Black%) = True% THEN
AssessPosition% = MateValue% - Depth
EXIT FUNCTION
END IF
InCheck = AttackingField%(bKing, White%) ' Is Black being checked?
END IF
' Positional Assessments factors do not outwiegh a heavy material
' imbalance. Hence, we omit the position assessment in this case
' Exception: The late endgame. Free Pawns have a high value.
' A minor piece without pawns is without effect.
Value% = MaterialBalance(Depth)
MatNum% = MaterialTotal(Depth)
IF MatNum% > MatB% + MatB% THEN
IF Value% < Alpha% - MaxPos% OR Value% > Beta% + MaxPos% THEN
AssessPosition% = Value%
EXIT FUNCTION
END IF
END IF
' Initialize the lines of rooks and pawns as well as the pawn controls.
' This could be computed incrementally significantly faster when
' performing (and taking back) the moves. However, this incremental
' computation is difficult and error-prone due to the special cases
' castling, EnPassant, and promotion.
' You could also build a list of pieces in 'IninAssessment' and
' in the second turn go thru this list (and no longer the entire
' board).
' The fastest solution consists of computing this list of pieces
' incrementally, too. This complicates, however, the functions
' "PerformMove" and "TakeBackMove".
' Following the KISS prinipal (Keep It Simple Stupid) this
' solution was chosen in MiniMAX.
CALL InitAssessment
PosValue% = 0
' Used for Assessing the Bishop pair.
bBishlop% = 0
wBishlop% = 0
' Used for determining insufficient material.
PawnCount% = 0
' White rooks on 7/8th row, black rooks on the 1/2nd
wRookon7% = 0
bRookon2% = 0
' Developement state: Castled and minor piece developed.
wDeveloped% = Castling(White% + 1)
bDeveloped% = Castling(Black% + 1)
' Knight on B1 developed?
IF MoveControl(B1%) > 0 THEN wDeveloped% = wDeveloped% + 1
' Bishop on C1 developed?
IF MoveControl(C1%) > 0 THEN wDeveloped% = wDeveloped% + 1
' Bishop on F1 developed?
IF MoveControl(F1%) > 0 THEN wDeveloped% = wDeveloped% + 1
' Knight on G1 developed?
IF MoveControl(G1%) > 0 THEN wDeveloped% = wDeveloped% + 1
' Knight on B8 developed?
IF MoveControl(B8%) > 0 THEN bDeveloped% = bDeveloped% + 1
' Bishop on C8 developed?
IF MoveControl(C8%) > 0 THEN bDeveloped% = bDeveloped% + 1
' Bishop on F8 developed?
IF MoveControl(F8%) > 0 THEN bDeveloped% = bDeveloped% + 1
' Knight on G8 developed?
IF MoveControl(G8%) > 0 THEN bDeveloped% = bDeveloped% + 1
' Read the entire board and assess each piece.
' The assessment takes white's point of view. For the black
' pieces, a positive assessment means that this evaluation
' is unfavorable for black.
FOR i% = Column1% TO Column8%
Feld% = i% * 10
FOR j% = ARow% TO HRow%
Feld% = Feld% + 1
SELECT CASE Board(Feld%)
CASE BK%
IF MatNum% < EndgameMaterial% THEN ' Endgame assessment for king
' Centralisze the king in the endgame.
PosValue% = PosValue% - CenterTable(Feld%)
ELSE
' Not yet castled, but castling rights lost
IF Castling(Black% + 1) = False% THEN
IF MoveControl(E8%) > 0 OR (MoveControl(H8%) > 0 AND MoveControl(A8%) > 0) THEN
PosValue% = PosValue% + 35
END IF
END IF
' King preferably not in the center
PosValue% = PosValue% + 4 * CenterTable(Feld%)
FOR k% = -1 TO 1
' Bonus for pawn shield before the king
IF Board(Feld% - 10 + k%) = BP% THEN PosValue% = PosValue% - 15
' Pawn shield 2 rows before the king
IF Board(Feld% - 20 + k%) = BP% THEN PosValue% = PosValue% - 6
' Deduct for half-open line occupied by
' enemy rook.
IF Pawns(j% + k%).White = 0 AND Rooks(j% + k%).White > 0 THEN
PosValue% = PosValue% + 12
END IF
NEXT k%
END IF
CASE BQ%
' Avoid Queen outings in the opening of the game.
IF bDeveloped% < 4 THEN
IF Feld% < A8% THEN PosValue% = PosValue% + 15
ELSE ' If development is completed, place the queen near
' the enemy king. Column and row distance.
' between queen and enemy king should be small.
ColumnnDiff% = ABS(wKing \ 10 - Feld% \ 10)
RownDiff% = ABS(wKing MOD 10 - Feld% MOD 10)
PosValue% = PosValue% + 2 * (ColumnnDiff% + RownDiff%)
END IF
CASE BN% ' Black Knight
PosValue% = PosValue% - CenterTable(Feld%) / 2 ' Centralize knight
CASE BB%
' Bishop should not impede black d7/e7 Pawns
' Bishop is also assessed by variable mobility
' in the move generator.
IF (Feld% = D6% OR Feld% = E6%) AND Board(Feld% + 10) = BP% THEN
PosValue% = PosValue% + 20
END IF
bBishlop% = bBishlop% + 1 ' No. of bishops for the bishop pair
CASE BR% ' Rook influences the king assessment
' Black rook has penetrated row 1 or 2
IF Feld% <= H2% THEN bRookon2% = bRookon2% + 1
' Bring rooks from a and h Columns into the center
IF j% >= CRow% AND j% <= ERow% THEN PosValue% = PosValue% - 4
' Rooks on half open and open lines
IF Pawns(j%).White = 0 THEN
PosValue% = PosValue% - 8 ' Rook on half open line
' Rook on open line
IF Pawns(j%).Black = 0 THEN PosValue% = PosValue% - 5
END IF
CASE BP% ' Pawn assessment is relatively complex.
' thus it is accomplised in a seperate routine.
PosValue% = PosValue% - BPAssessment%((Feld%), (i%), (j%), (bDeveloped%))
PawnCount% = PawnCount% + 1
CASE Empty%
' Do nothing
CASE WP% ' White Assessment is analogous to the black
PosValue% = PosValue% + WPAssessment%((Feld%), (i%), (j%), (wDeveloped%))
PawnCount% = PawnCount% + 1
CASE WR%
' White rook on 7th or 8th row
IF Feld% >= A7% THEN wRookon7% = wRookon7% + 1
' Bring rooks from a and h columns into the center
IF j% >= CRow% AND j% <= ERow% THEN PosValue% = PosValue% + 4
' Rooks on half open and open lines
IF Pawns(j%).Black = 0 THEN
PosValue% = PosValue% + 8 ' Rook on half open line
' Rook on open line
IF Pawns(j%).White = 0 THEN PosValue% = PosValue% + 5
END IF
CASE WB%
' Bishop should not block pawns on D3/E3.
IF (Feld% = D3% OR Feld% = E3%) AND Board(Feld% - 10) = WP% THEN
PosValue% = PosValue% - 20
END IF
wBishlop% = wBishlop% + 1
CASE WN%
PosValue% = PosValue% + CenterTable(Feld%) \ 2
CASE WQ%
' Avoid queen outings in the begining of the game.
IF wDeveloped% < 4 THEN
IF Feld% > H1% THEN PosValue% = PosValue% - 15
ELSE ' Place the queen near the enemy king.
' Column and row distance.
' between queen and enemy king should be small.
ColumnnDiff% = ABS(bKing \ 10 - Feld% \ 10)
RownDiff% = ABS(bKing MOD 10 - Feld% MOD 10)
PosValue% = PosValue% - 2 * (ColumnnDiff% + RownDiff%)
END IF
CASE WK%
IF MatNum% < EndgameMaterial% THEN ' Endgame assessment for king
' Centralize the king in the endgame
PosValue% = PosValue% + CenterTable(Feld%)
' Near opposition of the kings
IF ABS(Feld% - bKing) = 20 OR ABS(Feld% - bKing) = 2 THEN
k% = 10
' Opposition in the pawn endgame
IF MatNum% = 0 THEN k% = 30
IF Colour = White% THEN
PosValue% = PosValue% - k%
ELSE
PosValue% = PosValue% + k%
END IF
END IF
ELSE
' Not castled yet, but Castling rights lost.
IF Castling(White% + 1) = False% THEN
IF MoveControl(E1%) > 0 OR (MoveControl(H1%) > 0 AND MoveControl(A1%) > 0) THEN
PosValue% = PosValue% - 35
END IF
END IF
' king preferable not in the center
PosValue% = PosValue% - 4 * CenterTable(Feld%)
FOR k% = -1 TO 1
' Bonus for pawn shield before the king
IF Board(Feld% + 10 + k%) = WP% THEN PosValue% = PosValue% + 15
' Pawns shield 2 rows before the king
IF Board(Feld% + 20 + k%) = WP% THEN PosValue% = PosValue% + 6
' Deduct for half open lines occupied by
' enemy rook.
IF Pawns(j% + k%).Black = 0 AND Rooks(j% + k%).Black > 0 THEN
PosValue% = PosValue% - 12
END IF
NEXT k%
END IF
END SELECT
NEXT j%
NEXT i%
' No pawns left on board and insufficient material
' Recognized all elementary draw situations.
' KK, KLK, KSK, KSSK, KLKL, KSKL.
IF PawnCount% = 0 THEN
Bed1% = MatNum% <= MatB% ' Less than a bishop
Bed2% = MatNum% = 2 * MatN% ' Two knights
' Two bishops, but material differece less than a pawn
Bed3% = MatNum% <= 2 * MatB% AND ABS(MaterialBalance(Depth)) < MatP%
IF Bed1% OR Bed2% OR Bed3% THEN
AssessPosition% = 0
EXIT FUNCTION
END IF
END IF
' Bishop pare bonus for White
IF wBishlop% >= 2 THEN PosValue% = PosValue% + 15
' Bishop pair bonus for Black
IF bBishlop% >= 2 THEN PosValue% = PosValue% - 15
' White rooks on 7/8th row and black king also
' on these rows
IF wRookon7% > 0 AND bKing >= A7% THEN
PosValue% = PosValue% + 10
' Double rooks extra dangerous
IF wRookon7% > 1 THEN PosValue% = PosValue% + 25
END IF
' Black rooks on 1/2nd row and white king also
' on these rows
IF bRookon2% > 0 AND wKing <= H2% THEN
PosValue% = PosValue% - 10
IF bRookon2% > 1 THEN PosValue% = PosValue% - 25
END IF
IF Side% = Black% THEN ' Assessment was from white's point of view,
PosValue% = -PosValue% ' changed sign for black
END IF
' Consider the mobility of bishop and rooks
' by the move generator. Mobility(Depth) is the
' mobility of the oppenent, Mobility(Depth-1) that of
' "Side" (before the oppenent has made a move).
IF Depth >= 1 THEN
PosValue% = PosValue% - ((Mobility(Depth) - Mobility(Depth - 1)) / 16)
END IF
AssessPosition% = Value% + PosValue%
END FUNCTION
'--------------------------------------------------------------------
' AttackingField: Function
' Examine whether Player "Side" is attacking the field "Field".
' Returns "True" if field is attacked by "Side", otherwise "False".
' Algorithm: Imagine "Field" occupied by a super piece, that can move
' in any direction. If this super piece 'captures' e.g.
' a rook belonging to "Side" then the rook is actually
' attacking the field.
'
' Locale Variables: i%, Direction%, too%, Piece%, slide%
' Calls:
' Calledby: AssessPosition; GenerateMoves; InputMove; MoveList
'--------------------------------------------------------------------
FUNCTION AttackingField% (Feld%, Side%)
' First test the special case of pawns. They have the same direction
' as bishops but don't slide.
IF Side% = White% THEN
' Must go in the opposite direction of pawns. D5 is attacked
' by pawn on E4.
IF Board(Feld% - 9) = WP% OR Board(Feld% - 11) = WP% THEN
AttackingField% = True%
EXIT FUNCTION
END IF
END IF
IF Side% = Black% THEN
IF Board(Feld% + 9) = BP% OR Board(Feld% + 11) = BP% THEN
AttackingField% = True%
EXIT FUNCTION
END IF
END IF
' Examine the knight
FOR i% = 8 TO 15 ' Knight directions
too% = Feld% + Offset(i%)
IF Board(too%) = Empty% OR Board(too%) = Edge% THEN GOTO w1
IF Side% = White% THEN
IF Board(too%) = WN% THEN
AttackingField% = True%
EXIT FUNCTION
END IF
ELSEIF Board(too%) = BN% THEN
AttackingField% = True%
EXIT FUNCTION
END IF
w1:
NEXT i%
' Examine sliding pieces and king.
FOR i% = 0 TO 7
too% = Feld%
Direction% = Offset(i%)
Slide% = 0
Slideon1:
Slide% = Slide% + 1
too% = too% + Direction%
IF Board(too%) = Empty% THEN
GOTO Slideon1
END IF
' When the edge is reached then new direction
IF Board(too%) = Edge% THEN GOTO w2
' Hit a piece. Piece must be color side.
' Also, the current direction must be a possible move direction
' of the piece. The king can only do one step.
Piece% = Board(too%)
IF Side% = White% THEN
IF Piece% > 0 THEN ' White Ppece
IF Piece% = WK% THEN
IF Slide% <= 1 THEN ' king is slow paced
AttackingField% = True%
EXIT FUNCTION
END IF
ELSE
' As far as sliding pieces are concerned, the current
' direction muse be a possible move diection of the piece.
IF FigOffset(Piece%).Start <= i% THEN
IF FigOffset(Piece%).Ends >= i% THEN
AttackingField% = True%
EXIT FUNCTION
END IF
END IF
END IF
END IF
ELSE
IF Piece% < 0 THEN ' Black piece
IF Piece% = BK% THEN
IF Slide% <= 1 THEN
AttackingField% = True%
EXIT FUNCTION
END IF
ELSE
IF FigOffset(-Piece%).Start <= i% THEN
IF FigOffset(-Piece%).Ends >= i% THEN
AttackingField% = True%
EXIT FUNCTION
END IF
END IF
END IF
END IF
END IF
w2:
NEXT i%
' All directions exhausted, didn't hit a piece.
' I.e. Side in not attacking the field.
AttackingField% = False%
END FUNCTION
'--------------------------------------------------------------------
' BPAssessment: Function
' Assessment of one black pawn. Besides passed parameters, the
' pawn controls, pawn lines, and rook lines must be correctly
' engaged.
' Returns the assessment from black's point of view.
' Calls:
' Calledby: AssessPosition
'--------------------------------------------------------------------
FUNCTION BPAssessment% (Feld%, Column%, row%, developed%)
Column% = (Column8% + Column1%) - Column% ' Flip row. This makes higher
'row = better as for white.
IF MaterialTotal(Depth) > EndgameMaterial% THEN ' Opening or midgame
Value% = bPFieldValue(Feld%)
' If development incomplete, don't push edge pawns forward
IF developed% < 4 THEN
IF (row% >= FRow% OR row% <= BRow%) AND Column% > Column3% THEN
Value% = Value% - 15
END IF
END IF
ELSE ' In the endgame, all lines are equally good.
' Bring pawns forward.
Value% = Column% * 4
END IF
' Is the pawn isolated?
' Edge pawns don't require extra treatment. Pawns(ARow-1) is
' the left edge, Pawns(HRow+1) the right edge. No pawn is
' placed on these edges.
IF Pawns(row% - 1).Black = 0 AND Pawns(row% + 1).Black = 0 THEN
Value% = Value% - 12 ' Isolated
' Isolated double pawn
IF Pawns(row%).Black > 1 THEN Value% = Value% - 12
END IF
' double pawn
IF Pawns(row%).Black > 1 THEN Value% = Value% - 15
' Duo or guarded pawns get a bonus
' e.g. e5,d5 is a Duo, d6 guards e5
IF PawnControlled(Feld%).Black > 0 OR PawnControlled(Feld% - 10).Black > 0 THEN
Value% = Value% + Column%
END IF
IF Pawns(row%).White = 0 THEN ' Half-open column
' Pawn left behind on half-open column:
' Left-behind pawn is not guarded by its fellow pawns..
Condition1% = PawnControlled(Feld%).Black = 0
' ... and can't advance because of enemy pawns
' control the field in front of him.
Condition2% = PawnControlled(Feld% - 10).White > PawnControlled(Feld% - 10).Black
IF Condition1% AND Condition2% THEN
Value% = Value% - 10
' Rook impeded by left-behind pawn
IF Rooks(row%).White > 0 THEN Value% = Value% - 8
ELSE
' Pawn is a free pawn, on an half-open column and the
' fields ahead on his column are not controlled by
' enemy pawns.
FOR j% = Feld% TO A3% STEP -10 ' Until 3rd Row
IF PawnControlled(j%).White > 0 THEN
BPAssessment% = Value%
EXIT FUNCTION
END IF
NEXT j%
' Found a free pawn. In the endgame, a free pawn is more important
' than in midgame.
IF MaterialTotal(Depth) < EndgameMaterial% THEN
Value% = Value% + Column% * 16 ' The more advanced, the better
' Rook guards a free pawn on the same column
IF Rooks(row%).Black > 0 THEN Value% = Value% + Column% * 2
' Enemy rook on the same column
IF Rooks(row%).White > 0 THEN Value% = Value% - Column% * 2
' Pure pawn endgame. Free pawn particularly valuable.
IF MaterialTotal(Depth) = 0 THEN Value% = Value% + Column% * 8
' Guarded free pawn
IF PawnControlled(Feld%).Black > 0 OR PawnControlled(Feld% - 10).Black > 0 THEN
Value% = Value% + Column% * 4
END IF
' Free pawn blocked by a white piece. This piece is not
' threatened by fellow pawns.
IF Board(Feld% - 10) < 0 AND PawnControlled(Feld% - 10).Black = 0 THEN
Value% = Value% - Column% * 4
END IF
ELSE ' Free pawn in the midgame
Value% = Value% + Column% * 8
' Guarded free pawn
IF PawnControlled(Feld%).Black > 0 OR PawnControlled(Feld% - 10).Black > 0 THEN
Value% = Value% + Column% * 2
END IF
END IF
END IF
END IF
BPAssessment% = Value%
END FUNCTION
'--------------------------------------------------------------------
' CommandLoop:
' Reads the player's commands in a loop and calls
' the appropriate functions. The loop is terminated by
' the "EN" command.
' If the input is not a command it is interpreted as a
' move (on the form "e2e4" (from-field,to-field).
' and ignored as a command. See also: PrintLogo
' Calls: Gameover; Initialize; Displayboard; InputPosition;
' ComputerMove; Flipboard; MoveList; MoveBack;
' ComputingDepth; InitGameTree; AssessPosition;
' Calledby: Main
'--------------------------------------------------------------------
SUB CommandLoop
Ends% = False%
DO
CALL DisplayBoard(False%) ' my new line
INPUT "Your Input: ", Commands$
Commands$ = UCASE$(Commands$) ' Change to upper case
SELECT CASE Commands$
CASE "EN"
Ends% = True%
CALL GameOver
CASE "NG"
PRINT " New Game"
CALL Initialize
CASE "DB"
CALL DisplayBoard(False%)
CASE "CP"
CALL InputPosition
CASE "PL"
CALL ComputerMove
CASE "FB"
CALL FlipBoard
CASE "PR"
PRINT " Printing ";
IF Printing = False% THEN
Printing = True%
PRINT "on"
ELSE
Printing = False%
PRINT "off"
END IF
CASE "MM"
PRINT " Player-Player ";
IF PlayerPlayer = False% THEN
PlayerPlayer = True%
PRINT "on"
ELSE
PlayerPlayer = False%
PRINT "off"
END IF
CASE "ML"
CALL MoveList
CASE "TB"
CALL MoveBack
CASE "SD"
CALL ComputingDepth
CASE "DA"
CALL InitGameTree
PRINT " Assessment= "; AssessPosition%(-MateValue%, MateValue%, Colour)
CASE ELSE
IF InputMove%(Commands$) = False% THEN
PRINT " Illegal move or unknown Command"
ELSEIF PlayerPlayer = False% THEN
CALL ComputerMove
END IF
END SELECT
LOOP WHILE Ends% = False%
END SUB
'--------------------------------------------------------------------
' ComputerMove:
' Computes the next computer move.
' The search is iteratively deepened until MinDepth.
' The search uses "Aspiration"-Alpha-Beta.
' The search process can be interupted by
' a keypress.
' If the search wasn't interupted and no checkmate/stalemate
' exists, the best move is performed.
' Calls: InitGameTree; GenerateMoves; DisplayMove; TakeBackMove;
' PerformMove; CopyMainVariant; DisplayMVar; PrintMove
' AssessPosition;
' Calledby: CopyMainVariant; AlphaBeta; AssessPosition; CommandLoop
'--------------------------------------------------------------------
SUB ComputerMove
DIM tmp AS MoveType ' Temporary MoveType Variable
CALL InitGameTree
' Assess the initial position. End search if opponent is already checkmate.
Value% = AssessPosition%(-MateValue%, MateValue%, Colour)
IF Value% = MateValue% THEN
PRINT " Checkmate!"
EXIT SUB
END IF
' Store "Checked state". Required to recognize
' stalemate at the end of the search.
Check% = InCheck
NodeCount = 0
' Start time of the computation. Used for displaying nodes/second.
Starttime = TIMER
' Generate all pseudo-legal moves
CALL GenerateMoves(1)
' You should/could remove all illegal moves from the MoveStack
' here and only keep computing with legal moves.
' (Has only an optical effect, however, as the search is always aborted
' immediately after performing an illegal move).
' Iterative deepening: Distance is the number of half-moves until the
' horizon. Is not equal to the depth, however, as the distance can
' increased during the search process (e.g. by checks).
FOR Distance% = 1 TO MinDepth
IF Distance% = 1 THEN ' On Depth 1, we compute with open windows
Alpha% = -MateValue% ' We have no good assessment value for
Beta% = MateValue% ' the position yet.
ELSE ' On the higher levels, the result shold not
Beta% = Alpha% + 100 ' differ significantly from the result of the
Alpha% = Alpha% - 100 ' previous depth.
END IF
' For capture moves and checks, the search is extended.
' this variable limits the extensions.
MaxExtension = Distance% + 3
LOCATE 0 + Distance%, 52
PRINT Distance%;
' PRINT " Alpha-Beta Window = ["; Alpha%; ","; Beta%; "]"
MovesInLine% = 0
' PRINT " ";
' Compute the value of each move
FOR i% = 0 TO StackLimit(1) - 1
IF INKEY$ <> "" THEN
' Stop the calculation if a key is pressed.
PRINT " Computation interrupted!"
EXIT SUB
END IF
MovesInLine% = MovesInLine% + 1
' Initialize the main variant and display
' the move just examined.
MVar(Depth, Depth).from = 0
CALL DisplayMove(i%)
IF MovesInLine% MOD 9 = 8 THEN ' Eight moves per line
MovesInLine% = 0
' PRINT " ";
END IF
' Perform move, compute value, take back move.
CALL PerformMove((i%))
Value% = -AlphaBeta(-Beta%, -Alpha%, Distance% - 1)
CALL TakeBackMove((i%))
IF i% = 0 THEN ' Was it the first move (the best yet)?
' This move requires an exact value.
IF Value% < Alpha% THEN
' Search for the best move until now 'falls down' out the
' window (the program understands the mishap). Requires
' a renewed search with windows opened 'below'.
Alpha% = -MateValue%
Beta% = Value%
' PRINT "? ["; Alpha%; ","; Beta%; "]"
MovesInLine% = 0
' PRINT " ";
CALL PerformMove((i%))
Value% = -AlphaBeta(-Beta%, -Alpha%, Distance% - 1)
CALL TakeBackMove((i%))
ELSEIF Value% >= Beta% THEN ' Falls up
Alpha% = Value%
Beta% = MateValue%
' PRINT "! ["; Alpha%; ","; Beta%; "]"
MovesInLine% = 0
' PRINT " ";
CALL PerformMove((i%))
Value% = -AlphaBeta(-Beta%, -Alpha%, Distance% - 1)
CALL TakeBackMove((i%))
END IF
' There is just a slim chance that a subsequent move is t,
' even better. We continue calculating with a null window
' as this expedites the search.
Alpha% = Value%
Beta% = Alpha% + 1
LOCATE 15, 51
PRINT " Best Move: ";
CALL DisplayMove(i%)
LOCATE 16, 51
PRINT "Value ="; Value%
CALL CopyMainVariant(i%)
' LOCATE 0 + Distance%, 58
CALL DisplayMVar
MovesInLine% = 0
' PRINT " ";
ELSE ' Already computed the best move yet to SearchDepth
IF Value% > Alpha% THEN
' New best move found. Currently, it is only known
' that it is better. The exact value must be computed
' again with an open window.
BestValue% = Alpha%
Alpha% = Value%
Beta% = MateValue%
CALL PerformMove((i%))
Value% = -AlphaBeta(-Beta%, -Alpha%, Distance% - 1)
CALL TakeBackMove((i%))
' Is it also better with the open window?
' Solely applying alpha-beta, the move must always
' be better with the open window. Since the window is
' considered by the extensions and in the selectivity,
'the outcome may be different
' in our case.
IF Value% > BestValue% THEN
Alpha% = Value%
Beta% = Alpha% + 1
' PRINT " Best Move: ";
' CALL DisplayMove(i%)
' PRINT "Value ="; Value%
CALL CopyMainVariant(i%)
' LOCATE 0 + Distance%, 54
CALL DisplayMVar
MovesInLine% = 0
' PRINT " ";
' Place the best move at the start of the MoveList.
' Push the other moves one position up.
tmp = MoveStack(i%)
FOR j% = i% TO 1 STEP -1
MoveStack(j%) = MoveStack(j% - 1)
NEXT j%
MoveStack(0) = tmp
END IF
END IF
END IF
NEXT i%
NEXT Distance%
Endtime = TIMER
IF Alpha% > -(MateValue% - 1) THEN
' PRINT " Computer Player: ";
CALL DisplayMove(0) ' Best Move is always sorted into
' position 0 of the Movestacks
' LOCATE 17, 51
' PRINT " Value ="; Alpha%; ", Positions ="; NodeCount;
Time% = Endtime - Starttime
Comtime% = Comtime% + Time%: LOCATE 20, 50: PRINT "TOT "; Comtime%
' Prevent division by zero on nodes/second
IF Time% = 0 THEN Time% = 1
LOCATE 18, 51
PRINT ", Time="; Time%; "Sec., Positions/Sec. ="; NodeCount \ Time%
CALL PerformMove(0)
CALL PrintMove(0)
IF Alpha% >= MateValue% - 10 THEN
PRINT " I checkmate in "; (MateValue% - 2 - Alpha%) \ 2; " moves "
ELSE
IF Alpha% <= -MateValue% + 10 THEN
PRINT " I'm checkmate in "; (Alpha% + MateValue% - 1) \ 2; " moves"
END IF
END IF
ELSE
IF Check% = True% THEN
PRINT " Congratulations: MiniMAX is checkmated!"
ELSE
PRINT " Stalemate!"
END IF
END IF
END SUB
' -----------------------------------------------------------------
' ComputingDepth:
' Input minimum computing depth
' Calls: None
' Calledby:
' -----------------------------------------------------------------
SUB ComputingDepth
PRINT " Computing depth is"; MinDepth
INPUT " New computing depth: ", Inputs$
tmp% = VAL(Inputs$)
IF tmp% > 0 AND tmp% < MaxDepth% - 9 THEN
MinDepth = tmp%
ELSE
PRINT " Invalid computing depth"
END IF
END SUB
'--------------------------------------------------------------------
' CopyMainVariant:
' Saves the current move in the Main variant and copies
' the continuation that was found on the next depth.
' Calls:
' Calledby: ComputerMove;
'--------------------------------------------------------------------
SUB CopyMainVariant (CurrMove%)
' New main variant is a continuation of this variant
MVar(Depth, Depth).from = MoveStack(CurrMove%).from
MVar(Depth, Depth).too = MoveStack(CurrMove%).too
i% = 0
DO
i% = i% + 1
MVar(Depth, Depth + i%) = MVar(Depth + 1, Depth + i%)
LOOP UNTIL MVar(Depth + 1, Depth + i%).from = 0
END SUB
'--------------------------------------------------------------------
' DisplayBoard:
' Display of the game board and the game/Board state
' Only displays game/board state if "BoardOnly" is false
'
' The SGN-Function (Sign) returns the sign, i.e. -1 or +1
' The ABS-Function returns the absolute value (without sign)
' Calls: Fieldnotation
' Calledby:
'--------------------------------------------------------------------
SUB DisplayBoard (BoardOnly%)
' Display board
LOCATE 1, 1
FOR i% = Column8% TO Column1% STEP -1 ' For all rows
PRINT i% - 1; " "; ' Row coordinates
FOR j% = ARow% TO HRow% ' For all lines
Piece% = Board(i% * 10 + j%)
Side% = SGN(Piece%) ' Compute color from piece.
' Empty field has Color 0
Piece% = ABS(Piece%) ' Piece type
PRINT ColourSymbol(Side% + 1); FigSymbol(Piece%); " ";
NEXT j%
NEXT i%
PRINT " ";
FOR j% = ARow% TO HRow% ' Line coordinates 'a'...'h'
PRINT " "; CHR$(ASC("a") - 1 + j%);
NEXT j%
PRINT ' Empty line
PRINT ' Empty line
EXIT SUB 'my new line
IF BoardOnly% THEN EXIT SUB
' Remaining board/game state
IF Colour = White% THEN
PRINT " White";
ELSE
PRINT " Black";
END IF
PRINT " is to make a move"
PRINT " Material balance = "; MaterialBalance(Depth)
PRINT " En Passant Field = "; Fieldnotation$(EpField(Depth))
' Castling is in principle possible if the king and appropriate
' rook have not moved.
PRINT " Castling state black = ";
IF MoveControl(E8%) + MoveControl(H8%) = 0 THEN PRINT "0-0 ";
IF MoveControl(E8%) + MoveControl(A8%) = 0 THEN PRINT "0-0-0";
PRINT " Castling State white = ";
IF MoveControl(E1%) + MoveControl(H1%) = 0 THEN PRINT "0-0 ";
IF MoveControl(E1%) + MoveControl(A1%) = 0 THEN PRINT "0-0-0";
END SUB
'--------------------------------------------------------------------
' DisplayMove:
' Display the current move in chess notation.
' Castling is 'E1-G1' and not O-O
' CurrMove% is the index of the move into MoveStack.
' Calls:
' Calledby:
'--------------------------------------------------------------------
SUB DisplayMove (CurrMove%)
from% = MoveStack(CurrMove%).from
too% = MoveStack(CurrMove%).too
' PRINT FigSymbol(ABS(Board(from%))); ' Type of piece
' PRINT Fieldnotation$(from%); ' Initial field
IF MoveStack(CurrMove%).CapturedPiece = Empty% THEN
' PRINT "-"; ' Normal move
ELSE
' PRINT "x"; ' Capture move
END IF
' PRINT Fieldnotation$(too%); ' Target field
' If promoted, add promotion piece
IF MoveStack(CurrMove%).PromotedPiece <> Empty% THEN
' PRINT FigSymbol(MoveStack(CurrMove%).PromotedPiece);
END IF
' PRINT " ";
END SUB
'--------------------------------------------------------------------
' DisplayMVar:
' Display the current main variant, Only the from-to fields
' are output.
' Calls: Fieldnotation;
' Calledby:
'--------------------------------------------------------------------
SUB DisplayMVar
'PRINT " Main variants: ";
i% = 0
DO WHILE MVar(0, i%).from <> 0
LOCATE 1 + i%, 56
PRINT Fieldnotation$(MVar(0, i%).from); "-";
PRINT Fieldnotation$(MVar(0, i%).too); " ";
i% = i% + 1
LOOP
END SUB
'-----------------------------------------------------------------------
' Fieldnotation: Function
' Converts internal FieldNumber to Fieldnotation.
' Returns '--' if the number is not on the board
'
' Notes:
' The \ Operator is integer division.
' The Mod (Modulo) operator returns the remainder of an intege division.
' Calls:
' Calledby:
'-----------------------------------------------------------------------
FUNCTION Fieldnotation$ (Fieldnum%)
' See if correct
IF Fieldnum% < A1% OR Fieldnum% > H8% OR Board(Fieldnum%) = Edge% THEN
Fieldnotation$ = "--"
ELSE
s$ = CHR$(ASC("A") - 1 + Fieldnum% MOD 10) ' Line
s$ = s$ + CHR$(ASC("1") - 2 + Fieldnum% \ 10) ' Row
Fieldnotation$ = LCASE$(s$)
END IF
END FUNCTION
'--------------------------------------------------------------------
' Fieldnumber: Function
' Converts Fieldnotation (e.g. "A1") to internal Fieldnumber.
' Returns "Illegal" if input is incorrect
' Line coordinates must be passed as uppercase letters.
' Calls:
' Calledby: Fieldnotation
'--------------------------------------------------------------------
FUNCTION Fieldnumber% (Fieldnote$)
row$ = LEFT$(Fieldnote$, 1)
Column$ = MID$(Fieldnote$, 2, 1)
' See if correct
IF row$ < "A" OR row$ > "H" OR Column$ < "1" OR Column$ > "8" THEN
Fieldnumber% = Illegal%
EXIT FUNCTION
END IF
Fieldnumber% = (ASC(row$) - ASC("A") + 1) + 10 * (ASC(Column$) - ASC("1") + 2)
END FUNCTION
'---------------------------------------------------------------------
' FlipBoard:
' Flips the representation of the board on the monitor
' Note: Not implemented in version 1.0
' Calls:
' Calledby:
'---------------------------------------------------------------------
SUB FlipBoard
END SUB
'--------------------------------------------------------------------
' GameOver:
' Stores the game and game parameters on the harddisk.
' Note: Not implemented in version 1.0
' Calls:
' Calledby:
'--------------------------------------------------------------------
SUB GameOver
END SUB
'--------------------------------------------------------------------
' GenerateMoves:
' Generates moves and places them on the MoveStack
' Returns the number of moves.
' If "AllMoves" is greater than 0, all pseudo-legal
' moves are produced, otherwise all pseudo-legal capture moves,
' promotions, En Passant, and castling moves.
' Calls: SavePromotion; SaveMove; SaveCaptureMove; SaveEpMove
' AttackingField;
' Calledby: AttackingField, InputMove, MoveList, AlphaBeta,ComputerMove
'--------------------------------------------------------------------
SUB GenerateMoves (AllMoves%)
Index = StackLimit(Depth) ' Start of MoveList on current depth
Mobility(Depth) = 0
' Search the board for pieces
FOR from% = A1% TO H8%
Piece% = Board(from%)
' Empty and edge fields make no moves
IF Piece% = Empty% OR Piece% = Edge% THEN GOTO NextField
' Piece must also be of correct color
IF Colour = White% AND Piece% < 0 THEN GOTO NextField
IF Colour = Black% AND Piece% > 0 THEN GOTO NextField
Piece% = ABS(Piece%) ' Type of Piece. Color doesn't influence
' (except for pawns) the move diretion.
IF Piece% = WP% THEN ' Pawns moves
IF Colour = White% THEN
IF Board(from% + 10) = Empty% THEN
IF from% >= A7% THEN
CALL SavePromotion(from%, from% + 10)
ELSEIF AllMoves% > 0 THEN
CALL SaveMove(from%, from% + 10)
' double-step possible?
IF from% <= H2% AND Board(from% + 20) = Empty% THEN
CALL SaveMove(from%, from% + 20)
' Move has already increased Index
MoveStack(Index - 1).EpField = from% + 10
END IF
END IF
END IF
IF Board(from% + 11) < 0 THEN ' Pawn can capture black piece
IF from% >= A7% THEN
CALL SavePromotion(from%, from% + 11)
ELSE
CALL SaveCaptureMove(from%, from% + 11)
END IF
END IF
IF Board(from% + 9) < 0 THEN ' Likewise in other capture direction
IF from% >= A7% THEN
CALL SavePromotion(from%, from% + 9)
ELSE
CALL SaveCaptureMove(from%, from% + 9)
END IF
END IF
ELSEIF Colour = Black% THEN ' Same for black pawns
IF Board(from% - 10) = Empty% THEN
IF from% <= H2% THEN
CALL SavePromotion(from%, from% - 10)
ELSEIF AllMoves% > 0 THEN
CALL SaveMove(from%, from% - 10)
' double-steps possible?
IF from% >= A7% AND Board(from% - 20) = Empty% THEN
CALL SaveMove(from%, from% - 20)
' Move has already increased Index
MoveStack(Index - 1).EpField = from% - 10
END IF
END IF
END IF
' For black pawns also examine the edge,
' not for white as the edge > 0.
IF Board(from% - 11) > 0 AND Board(from% - 11) <> Edge% THEN
IF from% <= H2% THEN
CALL SavePromotion(from%, from% - 11)
ELSE
CALL SaveCaptureMove(from%, from% - 11)
END IF
END IF
IF Board(from% - 9) > 0 AND Board(from% - 9) <> Edge% THEN
IF from% <= H2% THEN
CALL SavePromotion(from%, from% - 9)
ELSE
CALL SaveCaptureMove(from%, from% - 9)
END IF
END IF
END IF
GOTO NextField ' Examine next field
END IF
' Moves for all other pieces are computed
' by way of move offset.
Longpaths% = FigOffset(Piece%).Longpaths
FOR i% = FigOffset(Piece%).Start TO FigOffset(Piece%).Ends
Direction% = Offset(i%)
too% = from%
Slideon2:
too% = too% + Direction%
IF Board(too%) = Empty% THEN
IF AllMoves% > 0 THEN
CALL SaveMove(from%, too%)
END IF
IF Longpaths% THEN ' Bishop, rook and queen
GOTO Slideon2
ELSE ' Knight and king
GOTO NextDirection
END IF
END IF
IF Board(too%) = Edge% THEN ' Hit the edge, keep searching
GOTO NextDirection ' in an another direction.
END IF
' Hit a piece. Must be of the correct color.
CaptureMove% = Colour = White% AND Board(too%) < 0
CaptureMove% = CaptureMove% OR (Colour = Black% AND Board(too%) > 0)
IF CaptureMove% THEN CALL SaveCaptureMove(from%, too%)
NextDirection:
NEXT i%
NextField:
NEXT from%
' En Passant Move
IF EpField(Depth) <> Illegal% THEN
ep% = EpField(Depth)
IF Colour = White% THEN
IF Board(ep% - 9) = WP% THEN
CALL SaveEpMove(ep% - 9, ep%, ep% - 10)
END IF
IF Board(ep% - 11) = WP% THEN
CALL SaveEpMove(ep% - 11, ep%, ep% - 10)
END IF
ELSE
IF Board(ep% + 9) = BP% THEN
CALL SaveEpMove(ep% + 9, ep%, ep% + 10)
END IF
IF Board(ep% + 11) = BP% THEN
CALL SaveEpMove(ep% + 11, ep%, ep% + 10)
END IF
END IF
END IF
' Castling is also performed in the quiescence search because it has a
' strong influence on the assessment. (Whether this is appropriate,
' is a matter of dispute even amoung leading programmers).
' Compute castling
IF Colour = White% THEN
IF wKing = E1% AND MoveControl(E1%) = 0 THEN
' Is short castling allowed?
OK% = Board(H1%) = WR% AND MoveControl(H1%) = 0
OK% = OK% AND Board(F1%) = Empty% AND Board(G1%) = Empty%
OK% = OK% AND AttackingField%(E1%, Black%) = False%
OK% = OK% AND AttackingField%(F1%, Black%) = False%
OK% = OK% AND AttackingField%(G1%, Black%) = False%
IF OK% THEN
CALL SaveMove(E1%, G1%) ' Save king's move
MoveStack(Index - 1).CastlingNr = ShortCastlingMove%
END IF
' Is long castling allowed?
OK% = Board(A1%) = WR% AND MoveControl(A1%) = 0
OK% = OK% AND Board(D1%) = Empty%
OK% = OK% AND Board(C1%) = Empty%
OK% = OK% AND Board(B1%) = Empty%
OK% = OK% AND AttackingField%(E1%, Black%) = False%
OK% = OK% AND AttackingField%(D1%, Black%) = False%
OK% = OK% AND AttackingField%(C1%, Black%) = False%
IF OK% THEN
CALL SaveMove(E1%, C1%) ' Save king's move
' Save type of castling
MoveStack(Index - 1).CastlingNr = LongCastlingMove%
END IF
END IF
ELSE ' Black is to make a move
IF bKing = E8% AND MoveControl(E8%) = 0 THEN
' Is short castling allowed?
OK% = Board(H8%) = BR% AND MoveControl(H8%) = 0
OK% = OK% AND Board(F8%) = Empty% AND Board(G8%) = Empty%
OK% = OK% AND AttackingField%(E8%, White%) = False%
OK% = OK% AND AttackingField%(F8%, White%) = False%
OK% = OK% AND AttackingField%(G8%, White%) = False%
IF OK% THEN
CALL SaveMove(E8%, G8%) ' Save king's move
MoveStack(Index - 1).CastlingNr = ShortCastlingMove%
END IF
' Is long castling allowed?
OK% = Board(A8%) = BR% AND MoveControl(A8%) = 0
OK% = OK% AND Board(D8%) = Empty%
OK% = OK% AND Board(C8%) = Empty%
OK% = OK% AND Board(B8%) = Empty%
OK% = OK% AND AttackingField%(E8%, White%) = False%
OK% = OK% AND AttackingField%(D8%, White%) = False%
OK% = OK% AND AttackingField%(C8%, White%) = False%
IF OK% THEN
CALL SaveMove(E8%, C8%) ' Save king's move
' Save type of castling
MoveStack(Index - 1).CastlingNr = LongCastlingMove%
END IF
END IF
END IF
StackLimit(Depth + 1) = Index ' Mark end of MoveList
END SUB
'--------------------------------------------------------------------
' InitAssessment:
' Compute the Pawn controls and the columns on which pawns and
' rooks are placed. Called by the assessment function
' for initialization.
' Calls:
' Calledby:
'--------------------------------------------------------------------
SUB InitAssessment
' Delete pawn controls
FOR i% = A1% TO H8%
PawnControlled(i%).White = 0
PawnControlled(i%).Black = 0
NEXT i%
' Also initialize edges. This eliminates the
' need to examine edge columns.
FOR i% = ARow% - 1 TO HRow% + 1
Pawns(i%).White = 0
Pawns(i%).Black = 0
Rooks(i%).White = 0
Rooks(i%).Black = 0
NEXT i%
FOR i% = A1% TO H8%
IF Board(i%) = Empty% OR Board(i%) = Edge% THEN GOTO NextFeld
SELECT CASE Board(i%)
CASE WP%
PawnControlled(i% + 9).White = PawnControlled(i% + 9).White + 1
PawnControlled(i% + 11).White = PawnControlled(i% + 11).White + 1
Pawns(i% MOD 10).White = Pawns(i% MOD 10).White + 1
CASE BP%
PawnControlled(i% - 9).Black = PawnControlled(i% - 9).Black + 1
PawnControlled(i% - 11).Black = PawnControlled(i% - 11).Black + 1
Pawns(i% MOD 10).Black = Pawns(i% MOD 10).Black + 1
CASE BR%
Rooks(i% MOD 10).Black = Rooks(i% MOD 10).Black + 1
CASE WR%
Rooks(i% MOD 10).White = Rooks(i% MOD 10).White + 1
CASE ELSE
END SELECT
NextFeld:
NEXT i%
END SUB
'--------------------------------------------------------------------
' InitGameTree:
' Initialize the GameTree
' Calls:
' Calledby:
'--------------------------------------------------------------------
SUB InitGameTree
' In Depth 0 nothing has been computed, game tree already initialized
IF Depth = 0 THEN EXIT SUB
EpField(0) = EpField(1)
MaterialBalance(0) = MaterialBalance(1)
MaterialTotal(0) = MaterialTotal(1)
Depth = 0
END SUB
'--------------------------------------------------------------------
' Initialize:
' Initialize the board and the game status
' Calls: None
' Calledby: Main
'--------------------------------------------------------------------
SUB Initialize
' Board Initialization, build InitialPosition
MoveCount = 0 ' Counts the half-moves in the game
FOR i% = 0 TO BoardDim%
Board(i%) = InitialPosition(i%)
NEXT i%
' Positions of the kings in the InitialPosition
wKing = E1%
bKing = E8%
' No castling yet
FOR i% = 0 TO 2
Castling(i%) = False%
NEXT i%
FOR i% = A1% TO H8%
MoveControl(i%) = 0 ' Initially no piece has moved
NEXT i%
EpField(0) = Illegal% ' En Passant status
MaterialTotal(0) = MaterialSum% ' Material value (of pieces) in InitialPosition
MaterialBalance(0) = 0 ' Material balance even
PlayerPlayer = False%
StackLimit(0) = 0 ' Limit of Movestacks
MinDepth = 4 ' Default ComputingDepth
Depth = 0 ' Current depth in the game tree
Colour = White% ' White has the first move
END SUB
'--------------------------------------------------------------------
' InputMove: Function
' Attempts to interpret the passed string as a move.
' IF it's a legal move, that move is performed and the function
' returns the value "True%". If no (legal) move can be identified
' the function returns the value 'False%'.
' Calls: GenerateMoves; InitGameTree; DisplayMove; PerformMove
' TakeBackMove; PrintMove; AttackingField; Fieldnotation;
' Fieldnumber;
' Calledby:
'--------------------------------------------------------------------
FUNCTION InputMove% (Move$)
IF LEN(Move$) < 4 THEN ' Only from-to representation is allowed
InputMove% = False%
EXIT FUNCTION
END IF
from% = Fieldnumber%(Move$)
too% = Fieldnumber%(MID$(Move$, 3, 2))
CALL GenerateMoves(1)
FOR i% = StackLimit(Depth) TO StackLimit(Depth + 1) - 1
IF MoveStack(i%).from = from% AND MoveStack(i%).too = too% THEN
IF MoveStack(i%).PromotedPiece <> Empty% THEN ' Promotions
IF MID$(Move$, 5, 1) = "N" THEN ' in the sequence queen, knight
i% = i% + 1 ' bishop and rook
ELSEIF MID$(Move$, 5, 1) = "B" THEN
i% = i% + 2
ELSEIF MID$(Move$, 5, 1) = "R" THEN
i% = i% + 3
END IF
END IF
CALL InitGameTree
PRINT " Your Move: ";
CALL DisplayMove(i%)
tmp% = LastMove ' Temp storage for last move so far.
CALL PerformMove(i%) ' Warning: PerformMove changes
' the Color. Next inquiry of color must
' compensate for this.
IF Colour = Black% THEN
IF AttackingField%(wKing, Black%) = True% THEN
PRINT " White king on "; Fieldnotation$(wKing); " is being checked"
CALL TakeBackMove((i%))
LastMove = tmp% ' No new move made. Restore
InputMove% = False% ' last move.
EXIT FUNCTION
END IF
ELSEIF AttackingField%(bKing, White%) = True% THEN
PRINT " Blackr king on "; Fieldnotation$(bKing); " is being checked"
CALL TakeBackMove((i%))
LastMove = tmp%
InputMove% = False%
EXIT FUNCTION
END IF
CALL PrintMove(i%)
InputMove% = True%
EXIT FUNCTION
END IF
NEXT i%
InputMove% = False% ' The input move was not found in MoveList
END FUNCTION
'--------------------------------------------------------------------
' InputPosition:
' Input of any position
' Calls: DisplayBoard; PrintPosition; ReadPiece;
' Calledby: CommandLoop
'--------------------------------------------------------------------
SUB InputPosition
Depth = 0 ' Position becomes root of the search tree
PieceInput:
INPUT " Delete Board (Y/N) ", Inputs$
Inputs$ = UCASE$(Inputs$) ' change to upper case
IF Inputs$ = "Y" THEN
FOR i% = Column1% TO Column8%
FOR j% = ARow% TO HRow%
Board(i% * 10 + j%) = Empty%
NEXT j%
NEXT i%
END IF
' Do not interpret "Y" as no
PRINT " White:"
ReadPiece (White%)
PRINT " Black:"
ReadPiece (Black%)
' Compute material balance and examine if each side
' has just one king.
MaterialBalance(0) = 0
MaterialTotal(0) = 0
wKings% = 0
bKings% = 0
FOR i% = A1% TO H8% ' Read each field
IF Board(i%) = Empty% OR Board(i%) = Edge% THEN
GOTO Continue ' Empty or edge field found, go to next field
END IF
' New Material balance
' White piece positively affects the balance, back negatively
MaterialBalance(0) = MaterialBalance(0) + SGN(Board(i%)) * PieceMaterial(ABS(Board(i%)))
IF ABS(Board(i%)) <> WP% THEN
MaterialTotal(0) = MaterialTotal(0) + PieceMaterial(ABS(Board(i%)))
END IF
IF Board(i%) = WK% THEN
wKings% = wKings% + 1 ' Number and position of white kings
wKing = i%
END IF
IF Board(i%) = BK% THEN
bKings% = bKings% + 1 ' Black kings
bKing = i%
END IF
Continue:
NEXT i%
IF bKings% <> 1 OR wKings% <> 1 THEN
PRINT "Illegal position, each side must have exactly one king"
CALL DisplayBoard(True%)
GOTO PieceInput
END IF
Repeat: ' The entry must be complete with a legal position
' otherwise the movegenerator doesn't work.
INPUT " Whose move (W/B): "; Inputs$
Inputs$ = UCASE$(Inputs$)
IF Inputs$ = "W" THEN
Colour = White%
ELSEIF Inputs$ = "B" THEN
Colour = Black% ' Material balance was computed from
MaterialBalance(0) = -MaterialBalance(0) 'white's viewpoint until now.
ELSE
GOTO Repeat
END IF
FOR i% = A1% TO H8% ' To simplify, we assume here that
MoveControl(i%) = 1 ' all pieces have already moved once.
NEXT i% ' Otherwise, the assessment function
' believes this is an
' Initial position.
MoveControl(E1%) = 0
MoveControl(A1%) = 0 ' Single exception: The king and rook
MoveControl(H1%) = 0 ' fields represent the castling state
MoveControl(E8%) = 0 ' and must therefore be reset
MoveControl(A8%) = 0 ' to zero.
MoveControl(H8%) = 0
EpField(0) = Illegal%
INPUT " Change the status (Y/N): "; Inputs$
Inputs$ = UCASE$(Inputs$)
IF Inputs$ = "Y" THEN
' Input the enpassant Field. if following input ins't correct,
' enpassant is not possible.
INPUT " En passant column: "; Inputs$
Inputs$ = UCASE$(Inputs$)
ep$ = LEFT$(Inputs$, 1)
IF ep$ >= "A" AND ep$ <= "H" THEN
IF Colour = White% THEN
EpField(0) = A6% + ASC(ep$) - ASC("A")
ELSE
EpField(0) = A3% + ASC(ep$) - ASC("A")
END IF
END IF
' Black short castling. By default, castling is possible.
INPUT " Black 0-0 legal (Y/N) : "; Inputs$
Inputs$ = UCASE$(Inputs$)
IF Inputs$ = "N" THEN
MoveControl(H8%) = 1 ' Move the rook. This eliminates
' the castling.
END IF
INPUT " Black 0-0-0 legal (Y/N): "; Inputs$
Inputs$ = UCASE$(Inputs$)
IF Inputs$ = "N" THEN
MoveControl(A8%) = 1
END IF
INPUT " White 0-0 legal (Y/N) : "; Inputs$
Inputs$ = UCASE$(Inputs$)
IF Inputs$ = "N" THEN
MoveControl(H1%) = 1
END IF
INPUT " White 0-0-0 legal (Y/N) : "; Inputs$
Inputs$ = UCASE$(Inputs$)
IF Inputs$ = "N" THEN
MoveControl(A1%) = 1
END IF
END IF
MoveCount = 0 ' Reset the move count
CALL DisplayBoard(False%) ' Display the new board
CALL PrintPosition
END SUB
' -----------------------------------------------------------------
' MoveBack:
' Takes back a move
' Since the pllayer moves aaaaare not stored, a mizimun of
' one move can be taken back.
' Calls: TakeBackMove; DisplayBoard; PrintBack
' Calledby:
' -----------------------------------------------------------------
SUB MoveBack
IF Depth <> 1 THEN
PRINT " Unfortunately not possible."
EXIT SUB
END IF
CALL TakeBackMove(LastMove)
CALL DisplayBoard(False%)
CALL PrintBack
END SUB
' -----------------------------------------------------------------
' MoveList:
' Generate all moves and display them on the monitor
' Calls: GenerateMoves; DisplayMove; AttackingField;
' Calledby:
' -----------------------------------------------------------------
SUB MoveList
CALL GenerateMoves(1)
IF Colour = White% THEN
CheckMated% = AttackingField%(bKing, White%)
ELSE
CheckMated% = AttackingField%(wKing, Black%)
END IF
IF CheckMated% THEN
PRINT " The king cannot be captured"
EXIT SUB
END IF
PRINT " "; Index - StackLimit(Depth); "pseudo legal moves`"
FOR i% = StackLimit(Depth) TO Index - 1
CALL DisplayMove(i%)
IF (i% - StackLimit(Depth)) MOD 9 = 8 THEN ' After 8 moves start
PRINT ' a new line.
END IF
NEXT i%
PRINT ' Carriage return
END SUB
'--------------------------------------------------------------------
' NextBestMove: Function
' From the possible moves of a certain depth the best,
' not-yet-played move is selected. Returns the index of the move
' into MoveStack. If all moves were already played, an
' impossible index (-1) is returned.
' The value of a move is determined by the move generator.
' This function finishes the move sorting in the search.
' Calls:
' Calledby:
'--------------------------------------------------------------------
FUNCTION NextBestMove%
BestMove% = -1
BestValue% = -MateValue%
FOR i% = StackLimit(Depth) TO StackLimit(Depth + 1) - 1
IF MoveStack(i%).Value > BestValue% THEN ' Found new best move
BestMove% = i%
BestValue% = MoveStack(i%).Value
END IF
NEXT i%
' Mark the selected move so it isn't selected again
' on the next call.
IF BestMove% >= 0 THEN MoveStack(BestMove%).Value = -MateValue%
NextBestMove% = BestMove%
END FUNCTION
'--------------------------------------------------------------------
' PerformMove:
' Performs a move at the board and updates the status and
' the search depth.
' CurrMove% is the index of the move into MoveStack.
' Calls:
' Calledby:
'--------------------------------------------------------------------
SUB PerformMove (CurrMove%)
MoveCount = MoveCount + 1 ' Increase move count by one half-move
from% = MoveStack(CurrMove%).from
too% = MoveStack(CurrMove%).too
ep% = MoveStack(CurrMove%).EpField
LastMove = CurrMove%
Depth = Depth + 1 ' One step deeper in the tree
TooFeld(Depth) = too% ' Used for move sorting and extension
' of the search.
EpField(Depth) = Illegal%
' Material balance is always seen from the viewpoint of the player who is
' to make a move. Therefore, flip the sign.
MaterialBalance(Depth) = -MaterialBalance(Depth - 1)
MaterialTotal(Depth) = MaterialTotal(Depth - 1)
' The piece is moving from the 'from' field to the 'to' field
MoveControl(from%) = MoveControl(from%) + 1
MoveControl(too%) = MoveControl(too%) + 1
IF ep% <> Illegal% THEN
IF Board(ep%) = Empty% THEN ' Pawn move from 2nd to 4th row
EpField(Depth) = ep%
ELSE ' Enemy pawn is captured enpassant
Board(ep%) = Empty% ' Remove captured pawn
MaterialBalance(Depth) = MaterialBalance(Depth) - MatP%
END IF
ELSE ' If a piece is captured, change the material balance
IF MoveStack(CurrMove%).CapturedPiece <> Empty% THEN ' Piece was captured
MatChange% = PieceMaterial(MoveStack(CurrMove%).CapturedPiece)
MaterialBalance(Depth) = MaterialBalance(Depth) - MatChange%
' Sum up only the officer's material value
IF MatChange% <> MatP% THEN
MaterialTotal(Depth) = MaterialTotal(Depth) - MatChange%
END IF
END IF
END IF
Board(too%) = Board(from%) ' Place onto board
Board(from%) = Empty%
' Now the special cases promotion and castling
IF MoveStack(CurrMove%).PromotedPiece <> Empty% THEN ' Pawn promotion
Board(too%) = Colour * MoveStack(CurrMove%).PromotedPiece
MatChange% = PieceMaterial(MoveStack(CurrMove%).PromotedPiece) - MatP%
MaterialBalance(Depth) = MaterialBalance(Depth) - MatChange%
' Pawns are not included in MaterialTotal.
MaterialTotal(Depth) = MaterialTotal(Depth) + MatChange% + MatP%
ELSE
IF MoveStack(CurrMove%).CastlingNr = ShortCastlingMove% THEN
Board(too% + 1) = Empty% ' 'to' is G1 or G8 (depending on color)
Board(too% - 1) = Colour * WR% ' Put white/black Rook on F1/F8
Castling(Colour + 1) = True%
ELSEIF MoveStack(CurrMove%).CastlingNr = LongCastlingMove% THEN
Board(too% - 2) = Empty% ' 'to' is C1 or C8
Board(too% + 1) = Colour * WR%
Castling(Colour + 1) = True%
END IF
END IF
' If king has moved, update the king's position
IF Board(too%) = WK% THEN
wKing = too%
ELSEIF Board(too%) = BK% THEN
bKing = too%
END IF
' Flip the color (the Side who is to make the move)
Colour = -Colour
END SUB
'--------------------------------------------------------------------
' PrintBack:
' Print the take-back command
' Calls:
' Calledby:
'--------------------------------------------------------------------
SUB PrintBack
IF Printing = False% THEN EXIT SUB ' Only if printing is on
IF Colour = White% THEN
LPRINT " Back"
IsWhiteLast = False%
ELSE
LPRINT USING "###. Back!"; MoveCount \ 2 + 1; CHR$(9);
IsWhiteLast = True%
END IF
END SUB
'--------------------------------------------------------------------
' PrintLogo:
' Displays the program logo/menu on the monitor (see CommandLoop
' Calls: None
' Calledby: Main;
'--------------------------------------------------------------------
SUB PrintLogo
CLS
PRINT "***********************************************************"
PRINT "* MiniMAX 1.0 (Basic) *"
PRINT "* *"
PRINT "* by Dieter Steinwender *"
PRINT "* and Chrilly Donninger *"
PRINT "* *"
PRINT "* Input a move (e.g. G1F3) *"
PRINT "* or one ot the following commands: *"
PRINT "* *"
PRINT "* NG --> New game *"
PRINT "* EN --> End the program *"
PRINT "* DB --> Display board on the monitor *"
PRINT "* CP --> Input position (Chess problem) *"
PRINT "* PL --> Play, computer move *"
PRINT "* PR --> Printing on/off *"
PRINT "* MM --> Multi moves input Player-Player *"
PRINT "* DL --> Display move list *"
PRINT "* TB --> Take back one move *"
PRINT "* SD --> Set computing depth *"
PRINT "* DA --> Display assessment *"
PRINT "***********************************************************"
END SUB
'--------------------------------------------------------------------
' PrintMove:
' Prints the current move.
' WARNING: Don't change the format of this output as it will cause
' malfunction of the Noname driver the CHESS232 board
' and the Autoplayer AUTO232.
'
' Notes:
' CHR$(9) is the tab character
' Calls: Fieldnotation;
' Calledby:
'--------------------------------------------------------------------
SUB PrintMove (CurrMove%)
IF Printing = False% THEN EXIT SUB ' Only if Printing is on
IF Colour = Black% THEN ' If black is to make a move
' the last move was by white.
LPRINT USING "###. "; MoveCount \ 2 + 1;
IsWhiteLast = True%
ELSE ' Black move
IF IsWhiteLast = True% THEN
LPRINT " ";
ELSE
LPRINT USING "###. ... ! "; MoveCount \ 2 + 1; CHR$(9);
END IF
IsWhiteLast = False%
END IF
IF MoveStack(CurrMove%).CastlingNr = NoCastlingMove% THEN
LPRINT PrintSymbol(ABS(Board(MoveStack(CurrMove%).too)));
from$ = LCASE$(Fieldnotation$(MoveStack(CurrMove%).from))
LPRINT from$;
IF MoveStack(CurrMove%).CapturedPiece <> Empty% THEN
LPRINT "x";
ELSE
LPRINT "-";
END IF
too$ = LCASE$(Fieldnotation$(MoveStack(CurrMove%).too))
LPRINT too$;
IF MoveStack(CurrMove%).PromotedPiece <> Empty% THEN
LPRINT PrintSymbol(MoveStack(CurrMove%).PromotedPiece);
END IF
ELSEIF MoveStack(CurrMove%).CastlingNr = ShortCastlingMove% THEN
LPRINT " 0-0 ";
ELSE
LPRINT " 0-0-0"
END IF
' Finish with the tab character for a white move
' or a charriage return for a black move
IF Colour = Black% THEN
LPRINT CHR$(9);
ELSE
LPRINT
END IF
END SUB
'--------------------------------------------------------------------
' PrintPosition:
' Prints the current position im ChessBase / Fritz format.
' WARNING: Don't change the format of this output as it will
' cause malfunction of the Chess332 driver.
' Calls: Fieldnotation;
' Calledby:
'--------------------------------------------------------------------
SUB PrintPosition
IF Printing = False% THEN EXIT SUB
IF IsWhiteLast = True% THEN
LPRINT
IsWhiteLast = False%
END IF
LPRINT "(wK";
LPRINT Fieldnotation$(wKing); ' First the king
FOR i% = A1% TO H8% ' Remaining white pieces
IF Board(i%) > 0 AND Board(i%) < WK% THEN
LPRINT ","; FigSymbol(Board(i%));
LPRINT Fieldnotation$(i%);
END IF
NEXT i%
LPRINT "; sK";
LPRINT Fieldnotation$(bKing); ' First the king
FOR i% = A1% TO H8% ' Remaining black pieces
IF Board(i%) < 0 AND Board(i%) > BK% THEN
LPRINT ","; FigSymbol(ABS(Board(i%)));
LPRINT Fieldnotation$(i%);
END IF
NEXT i%
LPRINT ")"
END SUB
'--------------------------------------------------------------------
' ReadPiece:
' Reads the Piece for the "Side"
' Format is:
' "." is "empty field", i.e. removes any piece from that field.
' Calls: Fieldnumber;
' Calledby:
'--------------------------------------------------------------------
SUB ReadPiece (Side%)
NextPiece:
INPUT Inputs$
IF Inputs$ = "" THEN EXIT SUB ' Exit if input is void
IF LEN(Inputs$) < 3 THEN GOTO BadInput ' Input to short
Inputs$ = UCASE$(Inputs$) ' Uppercase
Piece$ = LEFT$(Inputs$, 1)
Feld$ = MID$(Inputs$, 2, 2)
FOR i% = 0 TO PieceTypes% ' From empty field to king
IF Piece$ = FigSymbol(i%) THEN
' Converts chess notation into field value
' First character of input was already used for the Piece
Feld% = Fieldnumber%(Feld$)
IF Feld% = Illegal% THEN GOTO BadInput
IF i% = WP% THEN ' Pawns only legal on 2nd thru 7th row
IF Feld% <= H1% OR Feld% >= A8% THEN GOTO BadInput
END IF
Piece% = i% * Side% ' If color is black the sign
' of the piece is reversed.
Board(Feld%) = Piece% ' Place piece on the board
GOTO NextPiece
END IF
NEXT i%
BadInput:
PRINT " Bad Input Entered "
GOTO NextPiece
END SUB
'--------------------------------------------------------------------
' SaveCaptureMove:
' Save a capture move in MoveStack.
' Calls:
' Calledby:
'--------------------------------------------------------------------
SUB SaveCaptureMove (from%, too%)
' King cannot be captured
IF Board(too%) = WK% OR Board(too%) = BK% THEN EXIT SUB
FigValue% = PieceMaterial(ABS(Board(too%)))
MoveStack(Index).from = from%
MoveStack(Index).too = too%
MoveStack(Index).CapturedPiece = ABS(Board(too%))
' Rule for move sorting: Capturee the mose valuable piece
' using the the least valuable piece
MoveStack(Index).Value = FigValue% - (PieceMaterial(ABS(Board(from%))) \ 8)
' Extra Bonus for capturing the piece just moved
IF Depth > 0 THEN
IF too% = TooFeld(Depth - 1) THEN
MoveStack(Index).Value = MoveStack(Index).Value + 300
END IF
END IF
' Bonus for Main variant moves and "killer" moves
Killer1% = KillerTab(Depth).Killer1.from = from%
Killer1% = Killer1% AND KillerTab(Depth).Killer1.too = too%
Killer2% = KillerTab(Depth).Killer2.from = from%
Killer2% = Killer2% AND KillerTab(Depth).Killer2.too = too%
MVarMove% = MVar(0, Depth).from = from% AND MVar(0, Depth).too = too%
IF MVarMove% THEN
MoveStack(Index).Value = MoveStack(Index).Value + MainVariantBonus%
ELSEIF Killer1% THEN
MoveStack(Index).Value = MoveStack(Index).Value + Killer1Bonus%
ELSEIF Killer2% THEN
MoveStack(Index).Value = MoveStack(Index).Value + Killer2Bonus%
END IF
MoveStack(Index).PromotedPiece = Empty%
MoveStack(Index).CastlingNr = NoCastlingMove%
MoveStack(Index).EpField = Illegal%
IF Index < MoveStackDim% THEN ' Prevent MoveStack overflow
Index = Index + 1
ELSE
PRINT " ERROR: Move stack overflow"
SYSTEM ' Exit to DOS
END IF
END SUB
'--------------------------------------------------------------------
' SaveEpMove:
' Save En Passant Move in the MoveStack.
' Calls:
' Calledby:
'--------------------------------------------------------------------
SUB SaveEpMove (from%, too%, ep%)
' King cannot be captured
IF Board(too%) = WK% OR Board(too%) = BK% THEN EXIT SUB
MoveStack(Index).from = from%
MoveStack(Index).too = too%
MoveStack(Index).CapturedPiece = WP%
MoveStack(Index).PromotedPiece = Empty%
MoveStack(Index).CastlingNr = NoCastlingMove%
MoveStack(Index).EpField = ep%
MoveStack(Index).Value = MatP%
IF Index < MoveStackDim% THEN ' Prevent MoveStack overflow
Index = Index + 1
ELSE
PRINT " ERROR: Move stack overflow"
SYSTEM ' Exit to DOS
END IF
END SUB
'---------------------------------------------------------------------
' SaveMove:
' Save a normal move in the MoveStack.
' As a side effect, this procedure provides the mobility of bishop
' and rook, as well as the value of the move for the pre-sorting.
' Calls:
' Calledby:
'---------------------------------------------------------------------
SUB SaveMove (from%, too%)
' Increse the mobility of the bishop and Rook.
' Mobility in the center is rated higher
' than mobility at the edge.
IF Colour = White% THEN
IF Board(from%) = WB% OR Board(from%) = WR% THEN
Mobility(Depth) = Mobility(Depth) + CenterTable(too%)
END IF
ELSE
IF Board(from%) = BB% OR Board(from%) = BR% THEN
Mobility(Depth) = Mobility(Depth) + CenterTable(too%)
END IF
END IF
' Assess the move for move sorting. Bonus for main variant or "killer"
Killer1% = KillerTab(Depth).Killer1.from = from%
Killer1% = Killer1% AND KillerTab(Depth).Killer1.too = too%
Killer2% = KillerTab(Depth).Killer2.from = from%
Killer2% = Killer2% AND KillerTab(Depth).Killer2.too = too%
MVarMove% = MVar(0, Depth).from = from% AND MVar(0, Depth).too = too%
IF MVarMove% THEN
MoveStack(Index).Value = MainVariantBonus%
ELSEIF Killer1% THEN
MoveStack(Index).Value = Killer1Bonus%
ELSEIF Killer2% THEN
MoveStack(Index).Value = Killer2Bonus%
ELSE
MoveStack(Index).Value = Empty%
END IF
MoveStack(Index).from = from%
MoveStack(Index).too = too%
MoveStack(Index).CapturedPiece = Empty%
MoveStack(Index).PromotedPiece = Empty%
MoveStack(Index).CastlingNr = NoCastlingMove%
MoveStack(Index).EpField = Illegal%
IF Index < MoveStackDim% THEN ' Prevent MoveStack overflow
Index = Index + 1
ELSE
PRINT " ERROR: Move stack overflowed"
SYSTEM ' In this case "ease out" to DOS
END IF
END SUB
'--------------------------------------------------------------------
' SavePromotion:
' Produce all possible pawn promotions
' Calls: SaveMove; SaveCaptureMove;
' Calledby:
'--------------------------------------------------------------------
SUB SavePromotion (from%, too%)
IF Board(too%) = Empty% THEN
FOR i% = WQ% TO WR% STEP -1 ' Sequence queen,knight,bishop,rook
CALL SaveMove(from%, too%)
MoveStack(Index - 1).PromotedPiece = i%
NEXT i%
ELSE ' Promotion with capture
FOR i% = WQ% TO WR% STEP -1
CALL SaveCaptureMove(from%, too%)
MoveStack(Index - 1).PromotedPiece = i%
NEXT i%
END IF
END SUB
'--------------------------------------------------------------------
' TakeBackMove:
' Takes back a move in the tree.
' CurrMove% is the index of the move in MoveStack.
' Calls:
' Calledby:
'--------------------------------------------------------------------
SUB TakeBackMove (CurrMove%)
MoveCount = MoveCount - 1
from% = MoveStack(CurrMove%).from
too% = MoveStack(CurrMove%).too
ep% = MoveStack(CurrMove%).EpField
Colour = -Colour ' Other side to move
Depth = Depth - 1 ' One level higher in tree
Board(from%) = Board(too%) ' Put back the piece
Board(too%) = Empty%
IF ep% <> Illegal% AND MoveStack(CurrMove%).CapturedPiece = WP% THEN
Board(ep%) = -Colour ' WP%=White%, BP%=Black%
' Put back captured piece
ELSEIF MoveStack(CurrMove%).CapturedPiece <> Empty% THEN
Board(too%) = (-Colour) * MoveStack(CurrMove%).CapturedPiece
END IF
' Adjust move counter
MoveControl(from%) = MoveControl(from%) - 1
MoveControl(too%) = MoveControl(too%) - 1
' If castling put back rook
IF MoveStack(CurrMove%).CastlingNr = ShortCastlingMove% THEN
Board(too% + 1) = Colour * WR%
Board(too% - 1) = Empty%
Castling(Colour + 1) = False%
ELSEIF MoveStack(CurrMove%).CastlingNr = LongCastlingMove% THEN
Board(too% - 2) = Colour * WR%
Board(too% + 1) = Empty%
Castling(Colour + 1) = False%
END IF
IF MoveStack(CurrMove%).PromotedPiece <> Empty% THEN
Board(from%) = Colour ' Take back pawn promotion
END IF
' IF the king has moved, update the king's Position
IF Board(from%) = WK% THEN
wKing = from%
ELSEIF Board(from%) = BK% THEN
bKing = from%
END IF
END SUB
'--------------------------------------------------------------------
' WPAssessment: Function
' Assessment of one white Pawn.
' Analogous to the assessment of black pawns.
' Returns the assessment from white's viewpoint.
' Calls:
' Calledby: AssessPosition
'--------------------------------------------------------------------
FUNCTION WPAssessment% (Feld%, Column%, row%, developed%)
IF MaterialTotal(Depth) > EndgameMaterial% THEN ' Opening of midgame
Value% = wPFieldValue(Feld%)
' If development incomplete, don't push edge pawns forward
IF developed% < 4 THEN
IF (row% >= FRow% OR row% <= BRow%) AND Column% > Column3% THEN
Value% = Value% - 15
END IF
END IF
ELSE ' In then endgame, all lines are equally good.
Value% = Column% * 4 ' Bring pawns forward.
END IF
' Is the pawn isolated?
' Edge pawns don't require extra treatment. Pawns(ARow-1) is
' the left edge, Pawns(HRow+1) the right edge. No pawn is
' placed on these edges.
IF Pawns(row% - 1).White = 0 AND Pawns(row% + 1).White = 0 THEN
Value% = Value% - 12 ' Isolated
' Isolated double pawn
IF Pawns(row%).White > 1 THEN Value% = Value% - 12
END IF
' Double pawns
IF Pawns(row%).White > 1 THEN Value% = Value% - 15
' Duo or guarded pawn gets a bonus
IF PawnControlled(Feld%).White > 0 OR PawnControlled(Feld% + 10).White > 0 THEN
Value% = Value% + Column%
END IF
IF Pawns(row%).Black = 0 THEN ' Half-open column
' Pawn left behind on half-open column:
' Left behind pawn is not guarded by its fellow pawns..
Condition1% = PawnControlled(Feld%).White = 0
' ... and can't advance because of enemy pawns
' control the field in front of him.
Condition2% = PawnControlled(Feld% + 10).Black > PawnControlled(Feld% + 10).White
IF Condition1% AND Condition2% THEN
Value% = Value% - 10
' Rook impeded by left-behind pawn
IF Rooks(row%).Black > 0 THEN Value% = Value% - 8
ELSE
' Pawn is a free pawn, on a half-open column and the
' fields ahead on his column are not controlled by
' enemy pawns.
FOR j% = Feld% TO H6% STEP 10 ' Until 6th row
IF PawnControlled(j%).Black > 0 THEN
WPAssessment% = Value%
EXIT FUNCTION
END IF
NEXT j%
' Free pawn found. In the endgame, a free pawn is more important
' than in midgame.
IF MaterialTotal(Depth) < EndgameMaterial% THEN
Value% = Value% + Column% * 16 ' The more advanced the better
' Rook guards free pawn on the same column
IF Rooks(row%).White > 0 THEN Value% = Value% + Column% * 2
' Enemy rook on the same column.
IF Rooks(row%).Black > 0 THEN Value% = Value% - Column% * 2
' Pure pawn endgame. Free pawn particularly valuable.
IF MaterialTotal(Depth) = 0 THEN Value% = Value% + Column% * 8
' Guarded free pawn
IF PawnControlled(Feld%).White > 0 OR PawnControlled(Feld% + 10).White > 0 THEN
Value% = Value% + Column% * 4
END IF
' Free pawn blocked by a black piece. This piece is not
' threatened by fellow pawns.
IF Board(Feld% + 10) < 0 AND PawnControlled(Feld% + 10).White = 0 THEN
Value% = Value% - Column% * 4
END IF
ELSE ' Free pawn in the midgame
Value% = Value% + Column% * 8
' Guarded free pawn
IF PawnControlled(Feld%).White > 0 OR PawnControlled(Feld% + 10).White > 0 THEN
Value% = Value% + Column% * 2
END IF
END IF
END IF
END IF
WPAssessment% = Value%
END FUNCTION
Comments
Post a Comment
🤔