You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
knights/knights/logic.cpp

1496 lines
36 KiB

/***************************************************************************
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( 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 )
{
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);
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;
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 )
{
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 )
{
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 )
{
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 */
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 );
}
}