diff options
Diffstat (limited to 'knights/logic.cpp')
-rw-r--r-- | knights/logic.cpp | 1495 |
1 files changed, 1495 insertions, 0 deletions
diff --git a/knights/logic.cpp b/knights/logic.cpp new file mode 100644 index 0000000..9d889d4 --- /dev/null +++ b/knights/logic.cpp @@ -0,0 +1,1495 @@ +/*************************************************************************** + logic.cpp - description + ------------------- + begin : Sat Sep 29 2001 + copyright : (C) 2003 by Troy Corbin Jr. + email : tcorbin@users.sourceforge.net + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "logic.h" +#include "dlg_promote.h" +#include "command.h" +#include <stdlib.h> + +/////////////////////////////////////// +// +// logic::logic +// +/////////////////////////////////////// +logic::logic( resource *Rsrc, match_param *param ) +{ + Resource = Rsrc; + Param = param; + int tmp; + + GameType = Type_Standard; + for( tmp = 0; tmp < 64; tmp++ ) + { + current[tmp].File = ( tmp % 8 ); + current[tmp].Rank = ( tmp >> 3 ); + } + clearBoard(); +} +/////////////////////////////////////// +// +// logic::~logic +// +/////////////////////////////////////// +logic::~logic() +{ +} +/////////////////////////////////////// +// +// logic::Pointer +// +/////////////////////////////////////// +int logic::Pointer( const char File, const char Rank ) +{ + if( ( File < 0 ) || ( File > 7 ) ) return Null; + if( ( Rank < 0 ) || ( Rank > 7 ) ) return Null; + return ( ( Rank << 3 ) + File ); +} +/////////////////////////////////////// +// +// logic::CalcPointer +// +/////////////////////////////////////// +int logic::CalcPointer( const char File, const char Rank ) +{ + char tmpFile, tmpRank; + + tmpFile = chessman[ManPtr].File + File; + tmpRank = chessman[ManPtr].Rank + Rank; + return Pointer( tmpFile, tmpRank ); +} +/////////////////////////////////////// +// +// logic::isChessman +// +/////////////////////////////////////// +bool logic::isChessman( const char ChessmanPtr ) +{ + char BoardPtr = Pointer( chessman[ChessmanPtr].File, chessman[ChessmanPtr].Rank ); + if( ( BoardPtr < 0 ) || ( BoardPtr > 63 ) ) return FALSE; + if( current[BoardPtr].ManPtr != ChessmanPtr ) return FALSE; + return TRUE; +} +/////////////////////////////////////// +// +// logic::clearBoard +// +/////////////////////////////////////// +void logic::clearBoard( void ) +{ + for( register int tmp = 0; tmp < 64; tmp++ ) + { + chessman[tmp].Type = Null; + current[tmp].ManPtr = Null; + current[tmp].Note = NOTE_NONE; + } +} +/////////////////////////////////////// +// +// logic::Init +// +/////////////////////////////////////// +void logic::Init( const int Var ) +{ + GameType = Var; + switch( GameType ) + { + case Type_Standard: // Fall Through + default: + Init_Standard(); + break; + } +} +/////////////////////////////////////// +// +// logic::Init_Standard +// +/////////////////////////////////////// +void logic::Init_Standard( void ) +{ + register int tmp; + + clearBoard(); + for( tmp = 0; tmp < 32; tmp++ ) + { + if( tmp < 16 ) + { + chessman[tmp].Army = WHITE; + chessman[tmp].Rank = 0; + } + else + { + chessman[tmp].Army = BLACK; + chessman[tmp].Rank = 7; + } + switch( tmp % 16 ) + { + case 0: + chessman[tmp].Type = King; + chessman[tmp].File = 4; + break; + case 1: + chessman[tmp].Type = Queen; + chessman[tmp].File = 3; + break; + case 2: + chessman[tmp].Type = Bishop; + chessman[tmp].File = 2; + break; + case 3: + chessman[tmp].Type = Bishop; + chessman[tmp].File = 5; + break; + case 4: + chessman[tmp].Type = Knight; + chessman[tmp].File = 1; + break; + case 5: + chessman[tmp].Type = Knight; + chessman[tmp].File = 6; + break; + case 6: + chessman[tmp].Type = Rook; + chessman[tmp].File = 0; + break; + case 7: + chessman[tmp].Type = Rook; + chessman[tmp].File = 7; + break; + default: + chessman[tmp].Type = Pawn; + chessman[tmp].File = ( tmp % 16 ) - 8; + if( chessman[tmp].Army == WHITE ) chessman[tmp].Rank = 1; + else chessman[tmp].Rank = 6; + break; + } + current[ Pointer( chessman[tmp].File, chessman[tmp].Rank ) ].ManPtr = tmp; + CastleFlag[0] = CF_King + CF_RookQ + CF_RookK; + CastleFlag[1] = CF_King + CF_RookQ + CF_RookK; + OnMove = WHITE; + } +} +/////////////////////////////////////// +// +// logic::parseCAN +// +/////////////////////////////////////// +bool logic::parseCAN( const bool Army ) +{ + if( chessMove.CAN == NULL ) return FALSE; + if( ( chessMove.CAN[0] != 'o' ) && ( chessMove.CAN[0] != 'O' ) ) + { + if( ( chessMove.CAN[0] < 'a' ) || ( chessMove.CAN[0] > 'h' ) ) return FALSE; + chessMove.fromFile = chessMove.CAN[0] - 97; + if( ( chessMove.CAN[1] < '1' ) || ( chessMove.CAN[1] > '8' ) ) return FALSE; + chessMove.fromRank = chessMove.CAN[1] - 49; + if( ( chessMove.CAN[2] < 'a' ) || ( chessMove.CAN[2] > 'h' ) ) return FALSE; + chessMove.toFile = chessMove.CAN[2] - 97; + if( ( chessMove.CAN[3] < '1' ) || ( chessMove.CAN[3] > '8' ) ) return FALSE; + chessMove.toRank = chessMove.CAN[3] - 49; + if( strlen( chessMove.CAN ) == 5 ) chessMove.Promote = chessMove.CAN[4]; + else chessMove.Promote = Null; + } + /* + For some reason some engines w/ CAN output + express castling using SAN, not to name names GNUChess v4 + */ + else + { + chessMove.fromFile = 4; + if( QString( chessMove.CAN ).lower() == "o-o" ) chessMove.toFile = 6; + else chessMove.toFile = 2; + if( Army == WHITE ) + { + chessMove.fromRank = 0; + chessMove.toRank = 0; + } + else + { + chessMove.fromRank = 7; + chessMove.toRank = 7; + } + } + return TRUE; +} +/////////////////////////////////////// +// +// logic::parseSAN +// +/////////////////////////////////////// +bool logic::parseSAN( void ) +{ + bool Army(OnMove); + char Type(Pawn); + char SANPtr(0), tmp(0); + + chessMove.fromFile = Null; + chessMove.fromRank = Null; + chessMove.toFile = Null; + chessMove.toRank = Null; + chessMove.Promote = Null; + chessMove.ManTaken = Null; + chessMove.NAG = 0; + while( SANPtr < (signed)QString( chessMove.SAN ).length() ) + { + /* Parse a character */ + switch( chessMove.SAN[SANPtr] ) + { + case 'K': + if( SANPtr == 0 ) + Type = King; + else + chessMove.Promote = 'k'; + break; + case 'Q': + if( SANPtr == 0 ) + Type = Queen; + else + chessMove.Promote = 'q'; + break; + case 'B': + if( SANPtr == 0 ) + Type = Bishop; + else + chessMove.Promote = 'b'; + break; + case 'N': + if( SANPtr == 0 ) + Type = Knight; + else + chessMove.Promote = 'n'; + break; + case 'R': + if( SANPtr == 0 ) + Type = Rook; + else + chessMove.Promote = 'r'; + break; + /* Parse castle */ + case 'o': + case 'O': + if( SANPtr != 0 ) + break; + Type = King; + if( Army == WHITE ) + chessMove.toRank = 0; + else + chessMove.toRank = 7; + if( QString( chessMove.SAN ).lower() == "o-o-o" ) + chessMove.toFile = 2; + if( QString( chessMove.SAN ).lower() == "o-o" ) + chessMove.toFile = 6; + break; + /* Ignore some symbols... these fall through */ + case 'x': + case '=': + case '#': + case '+': + case '-': + case 'P': + break; + /* Handle annotations */ + case '!': + chessMove.NAG = 1; + if( chessMove.SAN[SANPtr - 1] == '!' ) + chessMove.NAG = 3; + if( chessMove.SAN[SANPtr - 1] == '?' ) + chessMove.NAG = 6; + break; + case '?': + chessMove.NAG = 2; + if( chessMove.SAN[SANPtr - 1] == '!' ) + chessMove.NAG = 5; + if( chessMove.SAN[SANPtr - 1] == '?' ) + chessMove.NAG = 4; + break; + default: + if( ( chessMove.SAN[SANPtr] >= '1' ) && ( chessMove.SAN[SANPtr] <= '8' ) ) + { + if( chessMove.toRank != Null ) + chessMove.fromRank = chessMove.toRank; + chessMove.toRank = chessMove.SAN[SANPtr] - 49; + break; + } + if( ( chessMove.SAN[SANPtr] >= 'a' ) && ( chessMove.SAN[SANPtr] <= 'h' ) ) + { + if( chessMove.toFile != Null ) + chessMove.fromFile = chessMove.toFile; + chessMove.toFile = chessMove.SAN[SANPtr] - 97; + break; + } + /* Unknown symbol... Can not process this chessMove */ +// kdDebug() << "logic::ParseSAN: Unknown Symbol: " << chessMove.SAN[SANPtr] << "\n"; + return FALSE; + break; + } + SANPtr++; + } + for( tmp = 0; tmp < 64; tmp++ ) + { + if( chessman[tmp].Type != Type ) + continue; + if( chessman[tmp].Army != Army ) + continue; + if( ( chessMove.fromFile != Null ) && ( chessman[tmp].File != chessMove.fromFile ) ) + continue; + if( ( chessMove.fromRank != Null ) && ( chessman[tmp].Rank != chessMove.fromRank ) ) + continue; + if( !isChessman( tmp ) ) + continue; + HashLegal( tmp ); + if( current[ ( chessMove.toRank << 3 ) + chessMove.toFile ].Note < NOTE_MOVE ) + continue; + chessMove.fromFile = chessman[tmp].File; + chessMove.fromRank = chessman[tmp].Rank; + break; + } + if( tmp == 64 ) + { +// kdWarning() << "logic::ParseSAN could not make a legal chessMove out of " << QString( chessMove.SAN ) << endl; +// kdWarning() << (int)Army << " " << (int)Type << " " << (int)chessMove.fromFile << " " << (int)chessMove.fromRank +// << " " << (int)chessMove.toFile << " " << (int)chessMove.toRank << endl; + return FALSE; + } + return TRUE; +} +/////////////////////////////////////// +// +// logic::writeCAN +// +/////////////////////////////////////// +void logic::writeCAN( void ) +{ + chessMove.CAN[0] = chessMove.fromFile + 97; + chessMove.CAN[1] = chessMove.fromRank + 49; + chessMove.CAN[2] = chessMove.toFile + 97; + chessMove.CAN[3] = chessMove.toRank + 49; + if( chessMove.Promote == Null ) chessMove.CAN[4] = 0; + else + { + chessMove.CAN[4] = chessMove.Promote; + chessMove.CAN[5] = 0; + } +} +/////////////////////////////////////// +// +// logic::writeSAN +// +/////////////////////////////////////// +void logic::writeSAN( void ) +{ + Position backup[64]; + bool SANambig(FALSE); + bool SANambig2(FALSE); + register char tmp, manPtr, toPtr, fromPtr; + QString SAN; + + /* + writeSAN calls HashLegal(), which writes on current[], + which we need intact for Move()... so we need a backup + copy of current[]. Removing this breaks en passant moves + */ + copyPositions( current, backup ); + fromPtr = ( chessMove.fromRank << 3 ) + chessMove.fromFile; + toPtr = ( chessMove.toRank << 3 ) + chessMove.toFile; + if( ( fromPtr > 63 ) || ( toPtr > 63 ) ) return; + if( ( fromPtr < 0 ) || ( toPtr < 0 ) ) return; + manPtr = current[fromPtr].ManPtr; + if( manPtr == Null ) return; + + /* Check ambiguity for SAN notation */ + for( tmp = 0; tmp < 64; tmp++ ) + { + if( tmp == manPtr ) continue; + if( !isChessman( tmp ) ) continue; + if( chessman[tmp].Army == chessman[manPtr].Army ) + { + if( chessman[tmp].Type == chessman[manPtr].Type ) + { + HashLegal( tmp ); + if( current[toPtr].Note >= NOTE_MOVE ) + { + SANambig = TRUE; + if( chessman[tmp].File == chessman[manPtr].File ) + SANambig2 = TRUE; + } + } + /* + This IF was added to fix an ambiguity that occurs when a pawn + on B file and a Bishop can attack the same spot + */ + else + if( ( ( chessman[manPtr].Type == Bishop ) && ( chessman[tmp].Type == Pawn ) ) || + ( ( chessman[manPtr].Type == Pawn ) && ( chessman[tmp].Type == Bishop ) ) ) + { + if( ( chessman[manPtr].File == 1 ) || ( chessman[tmp].File == 1 ) ) + { + HashLegal( tmp ); + if( current[toPtr].Note >= NOTE_MOVE ) + { + SANambig = TRUE; + SANambig2 = TRUE; + } + } + } + } + } + /* Go ahead and restore the backup. */ + copyPositions( backup, current ); + if( ( ( current[toPtr].Note == NOTE_ATTACK ) || + ( current[toPtr].Note == NOTE_ENPASSANT ) ) && + ( chessman[manPtr].Type == Pawn ) ) + { + SANambig = TRUE; + } + /* Write SAN Notation */ + if( current[toPtr].Note == NOTE_CASTLE ) + { + if( chessMove.toFile == 6 ) SAN = "O-O"; + if( chessMove.toFile == 2 ) SAN = "O-O-O"; + } + else + { + switch( chessman[manPtr].Type ) + { + case King: + SAN += 'K'; + break; + case Queen: + SAN += 'Q'; + break; + case Bishop: + SAN += 'B'; + break; + case Knight: + SAN += 'N'; + break; + case Rook: + SAN += 'R'; + break; + case Pawn: +// if( SANambig2 ) SAN += 'P'; + break; + default: + break; + } + if( SANambig == TRUE ) + { + SAN += char( chessMove.fromFile + 97 ); + if( SANambig2 ) SAN += char( chessMove.fromRank + 49 ); + } + if( ( current[toPtr].Note == NOTE_ATTACK ) || + ( current[toPtr].Note == NOTE_ENPASSANT ) ) + SAN += 'x'; + SAN += char( chessMove.toFile + 97 ); + SAN += char( chessMove.toRank + 49 ); + switch( chessMove.Promote ) + { + case 'q': + chessman[manPtr].Type = Queen; + SAN += "=Q"; + break; + case 'b': + chessman[manPtr].Type = Bishop; + SAN += "=B"; + break; + case 'n': + chessman[manPtr].Type = Knight; + SAN += "=N"; + break; + case 'r': + chessman[manPtr].Type = Rook; + SAN += "=R"; + break; + default: + break; + } + } + strcpy( chessMove.SAN, SAN.latin1() ); +} +/////////////////////////////////////// +// +// logic::board +// +/////////////////////////////////////// +QString logic::board( void ) +{ + QString output; + register int tmp(0), tmpMan(0), cR(7), cF(0); + + while( tmp != 64 ) + { + tmpMan = current[ Pointer( cF, cR ) ].ManPtr; + if( tmpMan == Null ) + output += '-'; + else + { + switch( chessman[ tmpMan ].Type ) + { + case King: + if( chessman[ tmpMan ].Army == WHITE ) + output += 'K'; + else + output += 'k'; + break; + case Queen: + if( chessman[ tmpMan ].Army == WHITE ) + output += 'Q'; + else + output += 'q'; + break; + case Bishop: + if( chessman[ tmpMan ].Army == WHITE ) + output += 'B'; + else + output += 'b'; + break; + case Knight: + if( chessman[ tmpMan ].Army == WHITE ) + output += 'N'; + else + output += 'n'; + break; + case Rook: + if( chessman[ tmpMan ].Army == WHITE ) + output += 'R'; + else + output += 'r'; + break; + case Pawn: // Fall through + default: + if( chessman[ tmpMan ].Army == WHITE ) + output += 'P'; + else + output += 'p'; + break; + } + } + cF++; + tmp++; + if( cF == 8 ) + { + cF = 0; + cR--; + } + } + + if( CastleFlag[WHITE] & ( CF_King | CF_RookK ) ) + output += '1'; + else + output += '0'; + if( CastleFlag[WHITE] & ( CF_King | CF_RookQ ) ) + output += '1'; + else + output += '0'; + if( CastleFlag[BLACK] & ( CF_King | CF_RookK ) ) + output += '1'; + else + output += '0'; + if( CastleFlag[BLACK] & ( CF_King | CF_RookQ ) ) + output += '1'; + else + output += '0'; + + return output; +} +/////////////////////////////////////// +// +// logic::setBoard +// +/////////////////////////////////////// +void logic::setBoard( const QString &board, const short ppf ) +{ + QChar piece; + int tmp(0), tmp2(0), cR(7), cF(0); + + clearBoard(); + if( board.length() < 64 ) + { + kdWarning() << "logic::setBoard: Was passed a string that is less than 64 bytes long." << endl; + return; + } + while( tmp != 64 ) + { + piece = board.at(tmp++); + switch( piece.lower() ) + { + case 'k': + if( piece == 'K' ) chessman[tmp2].Army = WHITE; + else chessman[tmp2].Army = BLACK; + chessman[tmp2].Type = King; + break; + case 'q': + if( piece == 'Q' ) chessman[tmp2].Army = WHITE; + else chessman[tmp2].Army = BLACK; + chessman[tmp2].Type = Queen; + break; + case 'b': + if( piece == 'B' ) chessman[tmp2].Army = WHITE; + else chessman[tmp2].Army = BLACK; + chessman[tmp2].Type = Bishop; + break; + case 'n': + if( piece == 'N' ) chessman[tmp2].Army = WHITE; + else chessman[tmp2].Army = BLACK; + chessman[tmp2].Type = Knight; + break; + case 'r': + if( piece == 'R' ) chessman[tmp2].Army = WHITE; + else chessman[tmp2].Army = BLACK; + chessman[tmp2].Type = Rook; + break; + case 'p': + if( piece == 'P' ) chessman[tmp2].Army = WHITE; + else chessman[tmp2].Army = BLACK; + chessman[tmp2].Type = Pawn; + break; + default: + break; + } + if( piece != '-' ) + { + chessman[tmp2].Rank = cR; + chessman[tmp2].File = cF; + current[ Pointer( cF, cR ) ].ManPtr = tmp2; + tmp2++; + } + cF++; + if( cF == 8 ) + { + cF = 0; + cR--; + } + } + CastleFlag[WHITE] = 0; + CastleFlag[BLACK] = 0; + if( board.at(64) == '1' ) CastleFlag[WHITE] += CF_RookK; + if( board.at(65) == '1' ) CastleFlag[WHITE] += CF_RookQ; + if( board.at(66) == '1' ) CastleFlag[BLACK] += CF_RookK; + if( board.at(67) == '1' ) CastleFlag[BLACK] += CF_RookQ; + if( CastleFlag[WHITE] ) CastleFlag[WHITE] += CF_King; + if( CastleFlag[BLACK] ) CastleFlag[BLACK] += CF_King; + /* Update enpassant record */ + if( ppf != -2 ) + { + enPassant[ !OnMove ] = Null; + if( ppf != -1 ) + enPassant[ OnMove ] = ppf + 24 + ( ( OnMove == BLACK ) << 3 ); + } +} +/////////////////////////////////////// +// +// logic::getKing +// +/////////////////////////////////////// +int logic::getKing( const bool Army ) +{ + register int tmp; + for( tmp = 0; tmp < 64; tmp++ ) + { + if( ( chessman[tmp].Army == Army ) && ( chessman[tmp].Type == King ) ) + if( isChessman( tmp ) ) + { + return Pointer( chessman[tmp].File, chessman[tmp].Rank ); + } + } + return Null; +} +/////////////////////////////////////// +// +// logic::isCheck +// +/////////////////////////////////////// +bool logic::isCheck( const bool Army ) +{ + register char tmp(0), currentKing( getKing(Army) ); + + for( tmp = 0; tmp < 64; tmp++ ) + { + if( chessman[tmp].Army != Army ) + if( isChessman( tmp ) ) + { + HashLegal( tmp ); + if( current[ currentKing ].Note == NOTE_ATTACK ) return TRUE; + } + } + return FALSE; +} +/////////////////////////////////////// +// +// logic::isLegal +// +/////////////////////////////////////// +bool logic::isLegal( const bool Army ) +{ + register int tmp(0), tmp2(0), count(0); + + for( tmp2 = 0; tmp2 < 64; tmp2++ ) + { + if( chessman[tmp2].Army == Army ) + if( isChessman( tmp2 ) ) + { + ManPtr = tmp2; + _HashLegal(); + count = 0; + tmp = 0; + while( tmp < 64 ) count += ( current[tmp++].Note >= NOTE_MOVE ); + if( count ) return TRUE; + } + } + return FALSE; +} +/////////////////////////////////////// +// +// logic::isDraw +// +/////////////////////////////////////// +bool logic::isDraw( const bool Army ) +{ + bool haveBishop(FALSE); + bool haveBishopDiag(FALSE); + bool haveKnight(FALSE); + bool EnemyBishop(FALSE); + bool EnemyBishopDiag(TRUE); + bool EnemyKnight(FALSE); + int tmp(0); + + if( !isLegal( Army ) ) return TRUE; + for( tmp = 0; tmp < 64; tmp++ ) + { + if( !isChessman( tmp ) ) continue; + if( chessman[tmp].Type == Queen ) return FALSE; + if( chessman[tmp].Type == Pawn ) return FALSE; + if( chessman[tmp].Type == Rook ) return FALSE; + /* Enemy guys */ + if( chessman[tmp].Army != Army ) + { + if( chessman[tmp].Type == Bishop ) + { + if( EnemyBishop == TRUE ) return FALSE; + EnemyBishopDiag = abs( ( current[tmp].Rank % 2 ) - ( current[tmp].File % 2 ) ); + EnemyBishop = TRUE; + } + if( chessman[tmp].Type == Knight ) + { + if( EnemyKnight == TRUE ) return FALSE; + EnemyKnight = TRUE; + } + continue; + } + /* Our guys */ + if( chessman[tmp].Type == Bishop ) + { + if( haveBishop == TRUE ) return FALSE; + haveBishopDiag = abs( ( current[tmp].Rank % 2 ) - ( current[tmp].File % 2 ) ); + haveBishop = TRUE; + } + if( chessman[tmp].Type == Knight ) + { + if( haveKnight == TRUE ) return FALSE; + haveKnight = TRUE; + } + } + if( haveKnight && EnemyKnight ) return FALSE; + if( haveBishop && EnemyKnight ) return FALSE; + if( haveKnight && EnemyBishop ) return FALSE; + if( haveKnight && haveBishop ) return FALSE; + if( EnemyKnight && EnemyBishop ) return FALSE; + if( ( haveBishop && EnemyBishop ) && ( haveBishopDiag != EnemyBishopDiag ) ) return FALSE; + return TRUE; +} +/////////////////////////////////////// +// +// logic::setBoardFromFen +// +/////////////////////////////////////// +void logic::setBoardFromFen(const QString &fen) +{ + clearBoard(); + int j = 0; //position in string + int r = 7; //rank + int f = 0; //file + int p = 63; //chessman number + QChar c; //current letter + + for (j=0;j<120 && p>=0 ;j++) { + c = fen[j]; + + if (c.isLetter()) { + //describing a piece + if (c == 'r') { + chessman[p].Army = BLACK; + chessman[p].Type = Rook; + + } + if (c=='q') { + chessman[p].Army = BLACK; + chessman[p].Type = Queen; + } + if (c=='k') { + chessman[p].Army = BLACK; + chessman[p].Type = King; + } + if (c=='p') { + chessman[p].Army = BLACK; + chessman[p].Type = Pawn; + } + if (c=='n') { + chessman[p].Army = BLACK; + chessman[p].Type = Knight; + } + if (c=='b') { + chessman[p].Army = BLACK; + chessman[p].Type = Bishop; + } + //black pieces + if (c == 'R') { + chessman[p].Army = WHITE; + chessman[p].Type = Rook; + + } + if (c=='Q') { + chessman[p].Army = WHITE; + chessman[p].Type = Queen; + } + if (c=='K') { + chessman[p].Army = WHITE; + chessman[p].Type = King; + } + if (c=='P') { + chessman[p].Army = WHITE; + chessman[p].Type = Pawn; + } + if (c=='N') { + chessman[p].Army = WHITE; + chessman[p].Type = Knight; + } + if (c=='B') { + chessman[p].Army = WHITE; + chessman[p].Type = Bishop; + } + chessman[p].Rank = r; + chessman[p].File = f; + current[ Pointer( chessman[p].File, chessman[p].Rank ) ].ManPtr = p; + p--; + f++; + } + if (c.isNumber()) { + //describing blank squares + + p = p - c.digitValue(); + f = f + c.digitValue(); + } + + if (c == '/') { + //describing new rank + r--; + f=0; + } + + } + + do { + j++; + c = fen[j]; + } while (c == '/' || c == ' '); + if (c=='w') + OnMove = WHITE; + if (c=='b') + OnMove = BLACK; + +} +/////////////////////////////////////// +// +// logic::Move +// +/////////////////////////////////////// +bool logic::Move( void ) +{ + dlg_promote *ProDlg; + int tmp; + int fromPtr, toPtr, manPtr; + + fromPtr = Pointer( chessMove.fromFile, chessMove.fromRank ); + toPtr = Pointer( chessMove.toFile, chessMove.toRank ); + if( ( fromPtr == Null ) || ( toPtr == Null ) ) + return FALSE; + manPtr = current[fromPtr].ManPtr; + if( manPtr == Null ) + return FALSE; + + HashLegal( manPtr ); + /* Only proceed if this is a move */ + if( current[toPtr].Note < NOTE_MOVE ) + return FALSE; // This depends on all moves being higher value than NOTE_MOVE, + // while all non-moves are less. + + /* Take care of moving the rook in a caste */ + if( current[toPtr].Note == NOTE_CASTLE ) + { + if( chessMove.toFile == 6 ) + { + for( tmp = 0; tmp < 64; tmp++ ) + { + if( ( chessman[tmp].Army == chessman[manPtr].Army ) && + ( chessman[tmp].Type == Rook ) && + ( chessman[tmp].File == 7 ) ) + { + chessman[tmp].File = 5; + current[ Pointer( 7, ( 7 * ( chessman[tmp].Army == BLACK ) ) ) ].ManPtr = Null; + current[ Pointer( 5, ( 7 * ( chessman[tmp].Army == BLACK ) ) ) ].ManPtr = tmp; + break; + } + } + } + if( chessMove.toFile == 2 ) + { + for( tmp = 0; tmp < 64; tmp++ ) + { + if( ( chessman[tmp].Army == chessman[manPtr].Army ) && + ( chessman[tmp].Type == Rook ) && + ( chessman[tmp].File == 0 ) ) + { + chessman[tmp].File = 3; + current[ Pointer( 0, ( 7 * ( chessman[tmp].Army == BLACK ) ) ) ].ManPtr = Null; + current[ Pointer( 3, ( 7 * ( chessman[tmp].Army == BLACK ) ) ) ].ManPtr = tmp; + break; + } + } + } + } + /* Handle the 50 Move Rule */ + MoveCounter++; + if( chessman[manPtr].Type == Pawn ) MoveCounter = 0; + if( current[ toPtr ].ManPtr != Null ) + { + MoveCounter = 0; + chessMove.ManTaken = current[ toPtr ].ManPtr; + } + /* Check for Pawn Promotion */ + if( ( chessMove.toRank == ( 7 * ( chessman[manPtr].Army == WHITE ) ) ) && + ( chessman[manPtr].Type == Pawn ) ) + { + if( ( ( OnMove == WHITE ) && ( Param->type(WHITE) == PLAYERLOCAL ) ) || + ( ( OnMove == BLACK ) && ( Param->type(BLACK) == PLAYERLOCAL ) ) ) + { + if( Resource->OPTION_Auto_Queen == TRUE ) chessMove.Promote = 'q'; + else + { + /* Prompt user for promotion */ + ProDlg = new dlg_promote( 0, "promotedialog", Resource ); + ProDlg->Init( OnMove ); + chessMove.Promote = ProDlg->exec(); + delete ProDlg; + /* Default to Queen if the user quit the dialog without choosing */ + if( ( chessMove.Promote != 'q' ) && + ( chessMove.Promote != 'b' ) && + ( chessMove.Promote != 'n' ) && + ( chessMove.Promote != 'r' ) ) chessMove.Promote = 'q'; + } + } + } + /* Write CAN & SAN Notation for this move */ + writeCAN(); + writeSAN(); + /* Make the move */ + chessman[manPtr].File = chessMove.toFile; + chessman[manPtr].Rank = chessMove.toRank; + current[fromPtr].ManPtr = Null; + current[toPtr].ManPtr = manPtr; + /* Remove pawns taken en passant */ + if( current[toPtr].Note == NOTE_ENPASSANT ) + { + MoveCounter = 0; + chessMove.ManTaken = current[ enPassant[ 1 - chessman[manPtr].Army ] ].ManPtr; + current[ enPassant[ 1 - chessman[manPtr].Army ] ].ManPtr = Null; + } + /* Take care of en passant data */ + if( current[toPtr].Note == NOTE_PAWN_DOUBLE ) + enPassant[ chessman[manPtr].Army ] = toPtr; + enPassant[ 1 - chessman[manPtr].Army ] = Null; + /* Handle castle flags */ + if( chessman[manPtr].Type == King ) + CastleFlag[ chessman[manPtr].Army ] = 0; + if( ( chessman[manPtr].Type == Rook ) && ( chessMove.fromFile == 0 ) ) + CastleFlag[ chessman[manPtr].Army ] -= CF_RookQ; + if( ( chessman[manPtr].Type == Rook ) && ( chessMove.fromFile == 7 ) ) + CastleFlag[ chessman[manPtr].Army ] -= CF_RookK; + return TRUE; +} +/////////////////////////////////////// +// +// logic::HashLegal +// +/////////////////////////////////////// +void logic::HashLegal( const char Man, const bool Recursion ) +{ + char tmp; + tmp = ManPtr; + ManPtr = Man; + _HashLegal( Recursion ); + ManPtr = tmp; +} +void logic::_HashLegal( const bool Recursion ) +{ + /* Used for loops and positions */ + register int Ptr(0), tmp(0), tmp2(0); + + /* Used to calculate a position relative to a given position */ + int dirF(0), dirR(0); + + /* Used to monitor the King inside the Monster */ + int currentKing(0), _castleFlag(0); + + if( !isChessman(ManPtr) ) + return; + + copyPositions( current, hash ); + + while( tmp < 64 ) + hash[tmp++].Note = NOTE_NONE; + + switch( chessman[ManPtr].Type ) + { + /* ROOK & QUEEN */ + case Rook: + case Queen: + /* Positive Rank Movement */ + for( tmp = 1; tmp < 8; tmp++ ) + { + Ptr = CalcPointer( 0, tmp ); + if( Ptr == Null ) break; + if( hash[Ptr].ManPtr == Null ) + { + hash[Ptr].Note = NOTE_MOVE; + continue; + } + if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army ) + { + hash[Ptr].Note = NOTE_ATTACK; + break; + } + if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK; + break; + } + /* Negitive Rank Movement */ + for( tmp = -1; tmp > -8; tmp-- ) + { + Ptr = CalcPointer( 0, tmp ); + if( Ptr == Null ) break; + if( hash[Ptr].ManPtr == Null ) + { + hash[Ptr].Note = NOTE_MOVE; + continue; + } + if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army ) + { + hash[Ptr].Note = NOTE_ATTACK; + break; + } + if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK; + break; + } + /* Positive File Movement */ + for( tmp = 1; tmp < 8; tmp++ ) + { + Ptr = CalcPointer( tmp, 0 ); + if( Ptr == Null ) break; + if( hash[Ptr].ManPtr == Null ) + { + hash[Ptr].Note = NOTE_MOVE; + continue; + } + if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army ) + { + hash[Ptr].Note = NOTE_ATTACK; + break; + } + if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK; + break; + } + /* Negative File Movement */ + for( tmp = -1; tmp > -8; tmp-- ) + { + Ptr = CalcPointer( tmp, 0 ); + if( Ptr == Null ) break; + if( hash[Ptr].ManPtr == Null ) + { + hash[Ptr].Note = NOTE_MOVE; + continue; + } + if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army ) + { + hash[Ptr].Note = NOTE_ATTACK; + break; + } + if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK; + break; + } + if( chessman[ManPtr].Type == Rook ) break; + /* Bishop & Queen */ + case Bishop: + /* NE Movement */ + for( tmp = 1; tmp < 8; tmp++ ) + { + Ptr = CalcPointer( tmp, tmp ); + if( Ptr == Null ) break; + if( hash[Ptr].ManPtr == Null ) + { + hash[Ptr].Note = NOTE_MOVE; + continue; + } + if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army ) + { + hash[Ptr].Note = NOTE_ATTACK; + break; + } + if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK; + break; + } + /* NW Movement */ + for( tmp = -1; tmp > -8; tmp-- ) + { + Ptr = CalcPointer( tmp, abs(tmp) ); + if( Ptr == Null ) break; + if( hash[Ptr].ManPtr == Null ) + { + hash[Ptr].Note = NOTE_MOVE; + continue; + } + if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army ) + { + hash[Ptr].Note = NOTE_ATTACK; + break; + } + if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK; + break; + } + /* SW Movement */ + for( tmp = -1; tmp > -8; tmp-- ) + { + Ptr = CalcPointer( tmp, tmp ); + if( Ptr == Null ) break; + if( hash[Ptr].ManPtr == Null ) + { + hash[Ptr].Note = NOTE_MOVE; + continue; + } + if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army ) + { + hash[Ptr].Note = NOTE_ATTACK; + break; + } + if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK; + break; + } + /* SE Movement */ + for( tmp = -1; tmp > -8; tmp-- ) + { + Ptr = CalcPointer( abs(tmp), tmp ); + if( Ptr == Null ) break; + if( hash[Ptr].ManPtr == Null ) + { + hash[Ptr].Note = NOTE_MOVE; + continue; + } + if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army ) + { + hash[Ptr].Note = NOTE_ATTACK; + break; + } + if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK; + break; + } + break; + /* Knight */ + case Knight: + for( tmp = 0; tmp < 8; tmp++ ) + { + switch( tmp ) + { + case 0: + Ptr = CalcPointer( -1, 2 ); + break; + case 1: + Ptr = CalcPointer( 1, 2 ); + break; + case 2: + Ptr = CalcPointer( 2, 1 ); + break; + case 3: + Ptr = CalcPointer( 2, -1 ); + break; + case 4: + Ptr = CalcPointer( 1, -2 ); + break; + case 5: + Ptr = CalcPointer( -1, -2 ); + break; + case 6: + Ptr = CalcPointer( -2, -1 ); + break; + case 7: + Ptr = CalcPointer( -2, 1 ); + break; + default: + break; + } + if( Ptr != Null ) + { + if( hash[Ptr].ManPtr == Null ) + hash[Ptr].Note = NOTE_MOVE; + else + { + if( Recursion == TRUE ) + hash[Ptr].Note = NOTE_ATTACK; + if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army ) + hash[Ptr].Note = NOTE_ATTACK; + } + } + } + break; + /* King */ + case King: + dirF = -1; + dirR = 1; + while(1) + { + Ptr = CalcPointer( dirF, dirR ); + if( Ptr != Null ) + { + if( hash[Ptr].ManPtr == Null ) hash[Ptr].Note = NOTE_MOVE; + else + { + if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK; + if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army ) hash[Ptr].Note = NOTE_ATTACK; + } + } + dirF++; + if( dirF == 2 ) + { + dirF = -1; + dirR--; + } + if( dirR == -2 ) break; + if( ( dirR == 0 ) && ( dirF == 0 ) ) dirF++; + } + /* Check for castles */ + if( Recursion == FALSE ) + { + /* Can the King castle at all? */ + if( CastleFlag[ chessman[ ManPtr ].Army ] & CF_King ) + { + dirR = 0; + /* How about with the Queen's Rook? */ + if( CastleFlag[ chessman[ ManPtr ].Army ] & CF_RookQ ) + { + if( hash[ CalcPointer( -1, dirR ) ].ManPtr == Null ) + { + if( hash[ CalcPointer( -2, dirR ) ].ManPtr == Null ) + { + if( hash[ CalcPointer( -3, dirR ) ].ManPtr == Null ) + { + hash[ CalcPointer( -2, dirR ) ].Note = NOTE_CASTLE; + _castleFlag |= CF_RookQ; + } + } + } + } + /* King's Rook? */ + if( CastleFlag[ chessman[ ManPtr ].Army ] & CF_RookK ) + { + if( hash[ CalcPointer( 1, dirR ) ].ManPtr == Null ) + { + if( hash[ CalcPointer( 2, dirR ) ].ManPtr == Null ) + { + hash[ CalcPointer( 2, dirR ) ].Note = NOTE_CASTLE; + _castleFlag |= CF_RookK; + } + } + } + } + } + break; + /* PAWN */ + default: + /* Get direction of movement */ + if( chessman[ManPtr].Army == WHITE ) dirR = 1; + else dirR = -1; + if( Recursion == FALSE ) + { + /* Forward 1 square */ + Ptr = CalcPointer( 0, dirR ); + if( ( Ptr != Null ) && ( hash[Ptr].ManPtr == Null ) ) + { + hash[Ptr].Note = NOTE_MOVE; + tmp = 1 + ( 5 * ( chessman[ManPtr].Army == BLACK ) ); + if( chessman[ManPtr].Rank == tmp ) + { + /* Forward 2 squares */ + dirR = dirR << 1; + Ptr = CalcPointer( 0, dirR ); + if( ( Ptr != Null ) && ( hash[Ptr].ManPtr == Null ) ) hash[Ptr].Note = NOTE_PAWN_DOUBLE; + dirR = dirR >> 1; + } + } + } + if( Recursion == TRUE ) + { + /* Attack Left */ + Ptr = CalcPointer( -1, dirR ); + if( Ptr != Null ) hash[Ptr].Note = NOTE_ATTACK; + /* Attack Right */ + Ptr = CalcPointer( 1, dirR ); + if( Ptr != Null ) hash[Ptr].Note = NOTE_ATTACK; + } + else + { + /* Attack Left */ + Ptr = CalcPointer( -1, dirR ); + if( ( Ptr != Null ) && ( hash[Ptr].ManPtr != Null ) ) + { + if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army ) hash[Ptr].Note = NOTE_ATTACK; + } + /* Attack Right */ + Ptr = CalcPointer( 1, dirR ); + if( ( Ptr != Null ) && ( hash[Ptr].ManPtr != Null ) ) + { + if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army ) hash[Ptr].Note = NOTE_ATTACK; + } + /* Attack en Passant Left */ + Ptr = CalcPointer( -1, 0 ); + if( ( Ptr != Null ) && ( enPassant[ 1 - chessman[ManPtr].Army ] == Ptr ) ) + { + Ptr = CalcPointer( -1, dirR ); + hash[Ptr].Note = NOTE_ENPASSANT; + } + /* Attack en Passant Right */ + Ptr = CalcPointer( 1, 0 ); + if( ( Ptr != Null ) && ( enPassant[ 1 - chessman[ManPtr].Army ] == Ptr ) ) + { + Ptr = CalcPointer( 1, dirR ); + hash[Ptr].Note = NOTE_ENPASSANT; + } + } + break; + } + /* THE MONSTER */ + /* Remove all possible moves that would either put your */ + /* king into check or wouldn't stop a check in progress */ + if( Recursion == FALSE ) + { + /* Make Backups */ + copyPositions( hash, hashBackup ); + copyChessmen( chessman, chessmanBackup ); + + /* Find the King's Position */ + currentKing = getKing( chessman[ManPtr].Army ); + + /* Remove castles under specific conditions */ + if( _castleFlag ) + { + for( tmp = 0; tmp < 64; tmp++ ) + { + if( !isChessman( tmp ) ) continue; + if( ( chessman[tmp].Army != chessman[ManPtr].Army ) && ( chessman[tmp].Type != Null ) ) + { + HashLegal( tmp, TRUE ); + /* Is a check in progress? */ + if( hash[ currentKing ].Note == NOTE_ATTACK ) + { + for( tmp2 = 0; tmp2 < 64; tmp2++ ) + if( hashBackup[tmp2].Note == NOTE_CASTLE ) hashBackup[tmp2].Note = NOTE_NONE; + break; + } + else + { + /* Store ManPtr in dirF so we can use ManPtr */ + dirF = ManPtr; + ManPtr = hashBackup[ currentKing ].ManPtr; + /* Is the path to Queenside in check? */ + if( _castleFlag & CF_RookQ ) + { + if( ( hash[ CalcPointer( -1, 0 ) ].Note == NOTE_MOVE ) || + ( hash[ CalcPointer( -2, 0 ) ].Note == NOTE_MOVE ) ) + { + hashBackup[ CalcPointer( -2, 0 ) ].Note = NOTE_NONE; + _castleFlag -= CF_RookQ; + } + } + /* Is the path to Kingside in check? */ + if( _castleFlag & CF_RookK ) + { + if( ( hash[ CalcPointer( 1, 0 ) ].Note == NOTE_MOVE ) || + ( hash[ CalcPointer( 2, 0 ) ].Note == NOTE_MOVE ) ) + { + hashBackup[ CalcPointer( 2, 0 ) ].Note = NOTE_NONE; + _castleFlag -= CF_RookK; + } + } + /* Restore ManPtr */ + ManPtr = dirF; + } + } + } + } // <- End Castle Checks + + /* Check all possible moves */ + for( tmp = 0; tmp < 64; tmp++ ) + { + /* Only proceed if this is a move */ + if( hashBackup[tmp].Note < NOTE_MOVE ) + continue; // This depends on all moves being higher value than NOTE_MOVE, + // while all non-moves are less. + + /* Pretend we moved here... what would happen? */ + current[ Pointer( chessman[ManPtr].File, chessman[ManPtr].Rank ) ].ManPtr = Null; + chessman[ManPtr].File = hashBackup[tmp].File; + chessman[ManPtr].Rank = hashBackup[tmp].Rank; + current[tmp].ManPtr = ManPtr; + if( current[tmp].Note == NOTE_ENPASSANT ) + { + current[ enPassant[ 1 - chessman[ManPtr].Army ] ].ManPtr = Null; + } + + /* Recalc King pos, as we may have just moved him */ + currentKing = getKing( chessman[ManPtr].Army ); + + /* Rehash in new position. If King is now under check, then */ + /* we can't use this move and it's removed from contention */ + for( tmp2 = 0; tmp2 < 64; tmp2++ ) + { + if( chessman[tmp2].Army != chessman[ManPtr].Army ) + if( isChessman( tmp2 ) ) + { + HashLegal( tmp2, TRUE ); + if( hash[ currentKing ].Note == NOTE_ATTACK ) + { + hashBackup[tmp].Note = NOTE_NONE; + } + } + } + + /* Restore the playground */ + copyPositions( hashBackup, current ); + copyChessmen( chessmanBackup, chessman ); + } // <- End of 'Check All Moves' loop + + copyPositions( hashBackup, current ); + copyPositions( hashBackup, hash ); + copyChessmen( chessmanBackup, chessman ); + } +} |