/*************************************************************************** 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 /////////////////////////////////////// // // 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( TQString( 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)TQString( 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( TQString( chessMove.SAN ).lower() == "o-o-o" ) chessMove.toFile = 2; if( TQString( 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 " << TQString( 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; TQString 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 // /////////////////////////////////////// TQString logic::board( void ) { TQString 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 TQString &board, const short ppf ) { TQChar 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 TQString &fen) { clearBoard(); int j = 0; //position in string int r = 7; //rank int f = 0; //file int p = 63; //chessman number TQChar 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 ); } }