File indexing completed on 2024-05-12 04:04:16

0001 /*
0002     This file is part of Knights, a chess board for KDE SC 4.
0003     SPDX-FileCopyrightText: 2009, 2010, 2011 Miha Čančula <miha@noughmad.eu>
0004 
0005     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 #include "rules/chessrules.h"
0009 #include <gamemanager.h>
0010 #include "core/move.h"
0011 #include "knightsdebug.h"
0012 
0013 
0014 using namespace Knights;
0015 
0016 ChessRules::ChessRules() {
0017     lineDirs.insert ( E, Pos ( 1, 0 ) );
0018     lineDirs.insert ( W, Pos ( -1, 0 ) );
0019     lineDirs.insert ( S, Pos ( 0, -1 ) );
0020     lineDirs.insert ( N, Pos ( 0, 1 ) );
0021 
0022     diagDirs.insert ( NW, lineDirs[N] + lineDirs[W] );
0023     diagDirs.insert ( NE, lineDirs[N] + lineDirs[E] );
0024     diagDirs.insert ( SW, lineDirs[S] + lineDirs[W] );
0025     diagDirs.insert ( SE, lineDirs[S] + lineDirs[E] );
0026 
0027     const QMap<Direction, Pos>* const dirs[] = {&lineDirs, &diagDirs};
0028     for (auto* dirs : dirs) {
0029         for (auto it = dirs->begin(), end = dirs->end(); it != end; ++it) {
0030             directions.insert(it.key(), it.value());
0031         }
0032     }
0033 
0034     knightDirs << Pos ( 1, 2 );
0035     knightDirs << Pos ( -1, 2 );
0036     knightDirs << Pos ( 1, -2 );
0037     knightDirs << Pos ( -1, -2 );
0038     knightDirs << Pos ( 2, 1 );
0039     knightDirs << Pos ( -2, 1 );
0040     knightDirs << Pos ( 2, -1 );
0041     knightDirs << Pos ( -2, -1 );
0042 
0043     queenRookStartPos[White] = Pos ( 1, 1 );
0044     queenRookStartPos[Black] = Pos ( 1, 8 );
0045 
0046     kingRookStartPos[White] = Pos ( 8, 1 );
0047     kingRookStartPos[Black] = Pos ( 8, 8 );
0048 
0049     kingPos[White] = Pos ( 5, 1 );
0050     kingPos[Black] = Pos ( 5, 8 );
0051 }
0052 
0053 bool ChessRules::hasLegalMoves ( Color color ) {
0054     Grid::const_iterator it = m_grid->constBegin();
0055     Grid::const_iterator end = m_grid->constEnd();
0056     for ( ; it != end; ++it ) {
0057         if ( it.value() && it.value()->color() == color && !legalMoves ( it.key() ).isEmpty() )
0058             return true;
0059     }
0060     return false;
0061 }
0062 
0063 Color ChessRules::winner() {
0064     if ( isKingAttacked ( Black ) && !hasLegalMoves ( Black ) )
0065         return White;
0066     if ( isKingAttacked ( White ) && !hasLegalMoves ( White ) )
0067         return Black;
0068     return NoColor;
0069 }
0070 
0071 PieceDataMap ChessRules::startingPieces ( ) {
0072     PieceDataMap pieces;
0073     // First, the white pieces
0074     int baseLine = 0;
0075     int pawnLine = 0;
0076     const int kingRow = 5;
0077     const int queenRow = 4;
0078     baseLine = 1;
0079     pawnLine = 2;
0080 
0081     pieces.insert ( Pos ( kingRow, baseLine ), PieceData ( White, King ) );
0082     pieces.insert ( Pos ( queenRow, baseLine ), PieceData ( White, Queen ) );
0083     for ( int i = 0; i < 2; ++i ) {
0084         pieces.insert ( Pos ( 1 + i*7, baseLine ), PieceData ( White, Rook ) );
0085         pieces.insert ( Pos ( 2 + i*5, baseLine ), PieceData ( White, Knight ) );
0086         pieces.insert ( Pos ( 3 + i*3, baseLine ), PieceData ( White, Bishop ) );
0087     }
0088     for ( int i = 1; i < 9; ++i )
0089         pieces.insert ( Pos ( i, pawnLine ), PieceData ( White, Pawn ) );
0090 
0091     // And now the black ones
0092 
0093     baseLine = 8;
0094     pawnLine = 7;
0095     pieces.insert ( Pos ( kingRow, baseLine ), PieceData ( Black, King ) );
0096     pieces.insert ( Pos ( queenRow, baseLine ), PieceData ( Black, Queen ) );
0097     for ( int i = 0; i < 2; ++i ) {
0098         pieces.insert ( Pos ( 1 + i*7, baseLine ), PieceData ( Black, Rook ) );
0099         pieces.insert ( Pos ( 2 + i*5, baseLine ), PieceData ( Black, Knight ) );
0100         pieces.insert ( Pos ( 3 + i*3, baseLine ), PieceData ( Black, Bishop ) );
0101     }
0102     for ( int i = 1; i < 9; ++i )
0103         pieces.insert ( Pos ( i, pawnLine ), PieceData ( Black, Pawn ) );
0104     return pieces;
0105 }
0106 
0107 bool ChessRules::isKingAttacked ( Color color, Grid* grid ) {
0108     return isAttacked ( kingPos[color], color, grid );
0109 }
0110 
0111 QList<Move> ChessRules::legalMoves ( const Pos& pos ) {
0112     QList<Move> moves;
0113     bool isKingMoving = false;
0114     Color color = m_grid->value ( pos )->color();
0115     switch ( m_grid->value ( pos )->pieceType() ) {
0116     case King:
0117         for ( const Pos& d : std::as_const(directions) ) {
0118             for ( const Move& m : movesInDirection ( d, pos, 1 ) )
0119                 moves << m;
0120         }
0121         isKingMoving = true;
0122         moves << castlingMoves ( pos );
0123         break;
0124     case Queen:
0125         for ( const Pos& d : std::as_const(directions) )
0126             moves << movesInDirection ( d, pos );
0127         break;
0128     case Bishop:
0129         for ( const Pos& d : std::as_const(diagDirs) )
0130             moves << movesInDirection ( d, pos );
0131         break;
0132     case Rook:
0133         for ( const Pos& d : std::as_const(lineDirs) )
0134             moves << movesInDirection ( d, pos );
0135         break;
0136     case Knight:
0137         for ( const Pos& d : std::as_const(knightDirs) ) {
0138             if ( !Board::isInBoard ( pos + d ) )
0139                 continue;
0140             if ( !m_grid->contains ( pos + d ) )
0141                 moves << Move ( pos, pos + d );
0142             else if ( m_grid->value ( pos + d )->color() != color )
0143                 moves << Move ( pos, pos + d, Move::Take );
0144         }
0145         break;
0146     case Pawn:
0147         moves << pawnMoves ( pos );
0148         for ( const Move& m : std::as_const(m_enPassantMoves) ) {
0149             if ( m.from() == pos )
0150                 moves << m;
0151         }
0152         break;
0153     default:
0154         break;
0155     }
0156 
0157     //from the list of possible moves, remove those moves where the king would be under attack
0158     Pos currKingPos = kingPos[color];
0159     for ( const Move& m : std::as_const(moves) ) {
0160         Grid t_grid = *m_grid;
0161         t_grid.insert ( m.to(), t_grid.take ( pos ) );
0162         if ( ( isKingMoving && isAttacked ( m.to(), color, &t_grid ) ) || ( !isKingMoving && isAttacked ( currKingPos, color, &t_grid ) ) ) {
0163             moves.removeOne ( m );
0164         }
0165     }
0166     return moves;
0167 }
0168 
0169 QList<Move> ChessRules::legalAttackMoves ( const Pos& pos, Grid* grid ) {
0170     if ( !grid )
0171         grid = m_grid;
0172     // Defending your pieces is counted here, hence all there 'true's in movesInDirection calls
0173     QList<Move> moves;
0174     switch ( grid->value ( pos )->pieceType() ) {
0175     case King:
0176         for ( const Pos& d : std::as_const(directions) )
0177             moves << movesInDirection ( d, pos, 1, true, grid );
0178         break;
0179     case Queen:
0180         for ( const Pos& d : std::as_const(directions) )
0181             moves << movesInDirection ( d, pos, 8, true, grid );
0182         break;
0183     case Bishop:
0184         for ( const Pos& d : std::as_const(diagDirs) )
0185             moves << movesInDirection ( d, pos, 8, true, grid );
0186         break;
0187     case Rook:
0188         for ( const Pos& d : std::as_const(lineDirs) )
0189             moves << movesInDirection ( d, pos, 8, true, grid );
0190         break;
0191     case Knight:
0192         for ( const Pos& d : std::as_const(knightDirs) ) {
0193             if ( Board::isInBoard ( pos + d ) )
0194                 moves << Move ( pos, pos + d );
0195         }
0196         break;
0197     case Pawn:
0198         if ( grid->value ( pos )->color() == White ) {
0199             moves << movesInDirection ( directions[NE], pos, 1, true, grid );
0200             moves << movesInDirection ( directions[NW], pos, 1, true );
0201         } else {
0202             moves << movesInDirection ( directions[SE], pos, 1, true, grid );
0203             moves << movesInDirection ( directions[SW], pos, 1, true, grid );
0204         }
0205         break;
0206     default:
0207         break;
0208     }
0209     return moves;
0210 }
0211 
0212 ChessRules::~ChessRules() = default;
0213 
0214 Rules::Directions ChessRules::legalDirections ( PieceType type ) {
0215     switch ( type ) {
0216     case Queen:
0217     case King:
0218     case Knight:
0219         return AllDirections;
0220 
0221     case Pawn:
0222         return N;
0223 
0224     case Rook:
0225         return LineDirections;
0226 
0227     case Bishop:
0228         return DiagDirections;
0229     default:
0230         return None;
0231     }
0232 }
0233 
0234 
0235 bool ChessRules::isAttacked ( const Pos& pos, Color color, Grid* grid ) {
0236     if ( !grid )
0237         grid = m_grid;
0238     Grid::const_iterator it = grid->constBegin();
0239     Grid::const_iterator end = grid->constEnd();
0240     for ( ; it != end; ++it ) {
0241         if ( it.value() && it.value()->color() != color && legalAttackMoves ( it.key(), grid ).contains ( Move ( it.key(), pos ) ) )
0242             return true;
0243     }
0244     return false;
0245 }
0246 
0247 bool ChessRules::isAttacking ( const Pos& attackingPos ) {
0248     Q_ASSERT(m_grid->contains(attackingPos));
0249     Q_ASSERT(m_grid->value(attackingPos));
0250     const Color pieceColor = m_grid->value ( attackingPos )->color();
0251     const Color kingColor = oppositeColor ( pieceColor );
0252     return legalAttackMoves ( attackingPos, m_grid ).contains ( Move ( attackingPos, kingPos[kingColor] ) );
0253 }
0254 
0255 
0256 QList<Move> ChessRules::movesInDirection ( const Pos& dir, const Pos& pos, int length, bool attackYours, Grid* grid ) {
0257     if ( !grid )
0258         grid = m_grid;
0259     QList<Move> list;
0260     int num = 0;
0261     for ( Pos n = pos + dir; n.first > 0 && n.first < 9 && n.second > 0 && n.second < 9 && num < length; n += dir ) {
0262         if ( !grid->contains ( n ) )
0263             list << Move ( pos, n );
0264         else if ( attackYours || grid->value ( n )->color() != grid->value ( pos )->color() ) {
0265             list << Move ( pos, n, Move::Take );
0266             break;
0267         } else
0268             break;
0269         num++;
0270     }
0271     return list;
0272 }
0273 
0274 void ChessRules::checkSpecialFlags ( Move* move, Color color ) {
0275     qCDebug(LOG_KNIGHTS) << move->string() << colorName ( color );
0276     if ( !move->flag ( Move::Castle ) ) {
0277         if ( move->notation() == Move::Coordinate ) {
0278             QString str = move->string();
0279             move->setStringForNotation ( Move::Coordinate, str );
0280             changeNotation ( move, Move::Algebraic, color );
0281             move->setStringForNotation ( Move::Algebraic, move->string() );
0282             move->setString ( str );
0283         } else {
0284             move->setStringForNotation ( Move::Algebraic, move->string() );
0285             changeNotation ( move, Move::Coordinate, color );
0286             move->setStringForNotation ( Move::Coordinate, move->string() );
0287         }
0288     }
0289 
0290     Q_ASSERT ( move->notation() == Move::Coordinate || !move->isValid() || move->flag ( Move::Castle ) );
0291 
0292     Piece* p = m_grid->value ( move->from() );
0293     if ( !p ) {
0294         qCWarning(LOG_KNIGHTS) << "No piece at position" << move->from();
0295         move->setFlag(Move::Illegal, true);
0296         return;
0297     }
0298     if ( !legalMoves ( move->from() ).contains(*move) ) {
0299         qCWarning(LOG_KNIGHTS) << "Illegal move" << move;
0300         move->setFlag(Move::Illegal, true);
0301         return;
0302     }
0303     move->setPieceData ( qMakePair( p->color(), p->pieceType() ) );
0304 
0305     if (!move->flag ( Move::Castle )) {
0306         // The long algebraic notation can be constructed from the two above
0307         QString lan;
0308         if ( move->pieceData().second != Pawn )
0309             lan += Piece::charFromType ( move->pieceData().second );
0310         lan += move->from().string();
0311         if ( move->flag ( Move::Take ) )
0312             lan += QLatin1Char('x');
0313         else
0314             lan += QLatin1Char('-');
0315         lan += move->to().string();
0316         move->setStringForNotation ( Move::LongAlgebraic, lan );
0317     }
0318 
0319     move->setFlags ( move->flags() & ~(Move::Take | Move::Castle | Move::Check | Move::CheckMate | Move::EnPassant | Move::Promote) );
0320     if ( m_grid->contains ( move->to() ) ) {
0321         Piece* p = m_grid->value ( move->to() );
0322         move->addRemovedPiece ( move->to(), qMakePair ( p->color(), p->pieceType() ) );
0323         move->setFlag ( Move::Take, true );
0324     }
0325     if ( p->pieceType() == King && length ( *move ) == 2 ) {
0326         qCDebug(LOG_KNIGHTS) << "Castling";
0327         Move::CastlingSide side;
0328         if ( move->to().first > move->from().first )
0329             side = Move::KingSide;
0330         else
0331             side = Move::QueenSide;
0332         *move = Move::castling(side, color);
0333         Q_ASSERT ( move->additionalMoves().size() == 1 );
0334     }
0335     if (!move->flag ( Move::Castle )) {
0336         if ( p->pieceType() == Pawn ) {
0337             // Promotion?
0338             if ( ( p->color() == White && move->to().second == 8 ) || ( p->color() == Black && move->to().second == 1 ) )
0339                 move->setFlag ( Move::Promote, true );
0340             Pos delta = move->to() - move->from();
0341             // En Passant?
0342             if ( delta.first != 0 && !m_grid->contains ( move->to() ) ) {
0343                 move->setFlag ( Move::EnPassant, true );
0344                 Pos capturedPos = move->from() + Pos ( delta.first, 0 );
0345                 Piece* p = m_grid->value ( capturedPos );
0346                 move->addRemovedPiece ( capturedPos, qMakePair ( p->color(), p->pieceType() ) );
0347             }
0348         }
0349     }
0350 
0351     /**
0352      * Check for check after the move has been made
0353      */
0354     Grid afterMoveGrid = *m_grid;
0355     afterMoveGrid.insert(move->to(), afterMoveGrid.take(move->from()));
0356 
0357     Grid::ConstIterator it = afterMoveGrid.constBegin();
0358     Grid::ConstIterator end = afterMoveGrid.constEnd();
0359 
0360     const Color kingColor = oppositeColor ( color );
0361 
0362     for ( ; it != end; ++it ) {
0363 
0364         if ( it.value()->color() == color &&
0365                 legalAttackMoves ( it.key(), &afterMoveGrid ).contains ( Move ( it.key(), kingPos[kingColor] ) ) )
0366             move->setFlag ( Move::Check, true );
0367     }
0368 
0369     if ( move->flag(Move::Take) ) {
0370         Piece* p = m_grid->value ( move->to() );
0371         move->addRemovedPiece ( move->to(), qMakePair ( p->color(), p->pieceType() ) );
0372     }
0373 }
0374 
0375 int ChessRules::length ( const Move& move ) {
0376     Pos delta = move.to() - move.from();
0377     return qMax ( qAbs ( delta.first ), qAbs ( delta.second ) );
0378 }
0379 
0380 QList< Move > ChessRules::pawnMoves ( const Pos& pos ) {
0381     QList<Move> list;
0382     Pos forwardDirection;
0383     QList<Pos> captureDirections;
0384     QList<Pos> enPassantDirections;
0385     enPassantDirections << directions[E] << directions[W];
0386     int baseLine = 0;
0387     if ( m_grid->value ( pos )->color() == White ) {
0388         forwardDirection = directions[N];
0389         captureDirections << directions[NE] << directions[NW];
0390         baseLine = 2;
0391     } else {
0392         forwardDirection = directions[S];
0393         captureDirections << directions[SE] << directions[SW];
0394         baseLine = 7;
0395     }
0396     // Normal forward moves
0397     if ( !m_grid->contains ( pos + forwardDirection ) ) {
0398         list << Move ( pos, pos + forwardDirection );
0399         // Double forward moves
0400         if ( pos.second == baseLine && !m_grid->contains ( pos + 2*forwardDirection ) )
0401             list << Move ( pos, pos + 2*forwardDirection );
0402     }
0403     // Normal capturing
0404     for ( const Pos& captureDir : std::as_const(captureDirections) ) {
0405         if ( m_grid->contains ( pos + captureDir ) && m_grid->value ( pos + captureDir )->color() != m_grid->value ( pos )->color() )
0406             list << Move ( pos, pos + captureDir, Move::Take );
0407     }
0408     return list;
0409 }
0410 
0411 void ChessRules::moveMade ( const Move& m ) {
0412     qCDebug(LOG_KNIGHTS) << m.string();
0413     if ( !m_grid->contains(m.to()) )
0414         qCDebug(LOG_KNIGHTS) << *m_grid;
0415     m_enPassantMoves.clear();
0416     switch ( m_grid->value ( m.to() )->pieceType() ) {
0417     // For Kings and Rook, we track their movement for Castling
0418     case King:
0419         qCDebug(LOG_KNIGHTS) << "King moved to" << m.to();
0420         kingMoved[m_grid->value ( m.to() )->color() ] = true;
0421         kingPos[m_grid->value ( m.to() )->color() ] = m.to();
0422         break;
0423     case Rook:
0424         if ( m.from() == kingRookStartPos[White] )
0425             kingRookMoved[White] = true;
0426         else if ( m.from() == queenRookStartPos[White] )
0427             queenRookMoved[White] = true;
0428         else if ( m.from() == kingRookStartPos[Black] )
0429             kingRookMoved[Black] = true;
0430         else if ( m.from() == queenRookStartPos[Black] )
0431             queenRookMoved[Black] = true;
0432         break;
0433 
0434     // We track Pawns for en-passant
0435     case Pawn:
0436         if ( length ( m ) == 2 ) {
0437             Pos mid = ( m.to() + m.from() ) / 2;
0438             const QList<Direction> dirs {W, E};
0439             for ( Direction dir : dirs )
0440                 if ( m_grid->contains ( m.to() + directions[dir] ) ) {
0441                     Move enPassantMove;
0442                     enPassantMove.setFrom ( m.to() + directions[dir] );
0443                     enPassantMove.setTo ( mid );
0444                     enPassantMove.setFlag ( Move::EnPassant, true );
0445                     m_enPassantMoves << enPassantMove;
0446                 }
0447         }
0448     default:
0449         break;
0450     }
0451     MoveData data;
0452     data.m = m;
0453     moveHistory << data;
0454 }
0455 
0456 static PieceType gridPieceType(Grid *grid, const Pos& pos)
0457 {
0458     Piece *p = grid->value ( pos );
0459     return p ? p->pieceType() : NoType;
0460 }
0461 
0462 QList< Move > ChessRules::castlingMoves ( const Pos& pos ) {
0463     // TODO: move from a model which permanently stores king's and rooks' move history
0464     // to account for undone moves.
0465     QList<Move> moves;
0466     Color color = m_grid->value ( pos )->color();
0467     if ( hasKingMoved ( color ) )
0468         return QList<Move>();
0469     if ( !hasRookMoved ( color, Move::QueenSide ) && isPathClearForCastling ( pos, queenRookStartPos[color] ) && ( gridPieceType( m_grid, queenRookStartPos[color] ) == Rook ) )
0470         moves << Move::castling ( Move::QueenSide, color );
0471     if ( !hasRookMoved ( color, Move::KingSide ) && isPathClearForCastling ( pos, kingRookStartPos[color] ) && ( gridPieceType( m_grid, kingRookStartPos[color] ) == Rook ) )
0472         moves << Move::castling ( Move::KingSide, color );
0473     return moves;
0474 }
0475 
0476 bool ChessRules::isPathClearForCastling ( const Pos& kingPos, const Pos& rookPos ) {
0477     // This is called only from castlingMoves(), so we can assume it's about castling
0478     if ( rookPos == kingPos || !m_grid->contains( kingPos ) )
0479         return true;
0480     Pos delta = rookPos - kingPos;
0481     Pos dir;
0482     if ( delta.first == 0 || delta.second == 0 )
0483         dir = delta / abs ( delta.first + delta.second );
0484     else if ( delta.first == delta.second || delta.first == -delta.second )
0485         dir = delta / abs ( delta.first );
0486     else
0487         return false;
0488     for ( Pos p = kingPos + dir; p != rookPos; p += dir ) {
0489         if ( m_grid->contains ( p ) )
0490             return false;
0491     }
0492 
0493     Color kingColor = m_grid->value ( kingPos )->color();
0494     for ( int i = 0; i <= 2; ++i ) {
0495         // Only the squares the King passes through must be safe
0496         if ( isAttacked ( kingPos + i * dir, kingColor ) )
0497             return false;
0498     }
0499 
0500     return true;
0501 }
0502 
0503 void ChessRules::changeNotation ( Move* move, Move::Notation notation, Color color ) {
0504     qCDebug(LOG_KNIGHTS) << *move << color;
0505     if ( !move->isValid() ||  move->notation() == notation || move->flag ( Move::Castle ) )
0506         return;
0507 
0508     qCDebug(LOG_KNIGHTS) << move->string();
0509 
0510     if ( notation == Move::Coordinate ) {
0511         // Converting from Algebraic to Coordinate
0512         /**
0513          * Possible notations:
0514          * * e4 == Pe4 == pawn to e4
0515          * * Ne4 == Knight to e4
0516          * * Rbe1 == Rook from file b to e1
0517          * * cd4 == Pawn from file c to d4
0518          */
0519         PieceType type = NoType;
0520         int file = -1;
0521         int rank = -1;
0522         bool found = false;
0523         bool fileSet = false;
0524         bool typeSet = false;
0525         bool rankSet = false;
0526         PieceType promoteType = NoType;
0527 
0528         QString s = move->string().remove( QLatin1Char('x') ).remove( QLatin1Char(':') ).remove( QLatin1Char('=') );
0529         if  ( s.size() < 2 ) {
0530             qCWarning(LOG_KNIGHTS) << "Unknown move notation" << move->string();
0531             move->setFlag ( Move::Illegal, true );
0532             return;
0533         }
0534 
0535         if ( QByteArray("KQBNRPkqbnrp").contains ( s.right(1).toLatin1() ) ) {
0536             promoteType = Piece::typeFromChar ( s.right(1).at(0) );
0537             s.chop ( 1 );
0538             move->setPromotedType ( promoteType );
0539         }
0540 
0541         if ( !QByteArray("KQBNRP").contains ( s[0].toLatin1() ) )
0542             s = QLatin1Char('P') + s;
0543 
0544         type = Piece::typeFromChar ( s[0] );
0545         typeSet = true;
0546         s.remove ( 0, 1 );
0547 
0548         switch ( s.size() ) {
0549         case 2:
0550             // Only destination square
0551             break;
0552 
0553         case 3:
0554             // Either starting file or rank
0555             if ( QByteArray("abcdefgh").contains( s[0].toLatin1() ) ) {
0556                 file = Pos::numFromRow ( s[0] );
0557                 fileSet = true;
0558             } else if ( QByteArray("12345678").contains ( s[0].toLatin1() ) ) {
0559                 rank = QString( s[0] ).toInt();
0560                 rankSet = true;
0561             }
0562             break;
0563 
0564         case 4:
0565             // Both starting file and rank
0566             file = Pos::numFromRow ( s[0] );
0567             fileSet = true;
0568             rank = QString ( s[1] ).toInt();
0569             rankSet = true;
0570             break;
0571         }
0572         move->setTo( Pos(s.right(2)) );
0573 
0574         qCDebug(LOG_KNIGHTS) << "Conditions:";
0575         if ( typeSet ) qCDebug(LOG_KNIGHTS) << "Type == " << Piece::charFromType(type);
0576         if ( fileSet ) qCDebug(LOG_KNIGHTS) << "File == " << file;
0577         if ( rankSet ) qCDebug(LOG_KNIGHTS) << "Rank == " << rank;
0578 
0579         for ( Grid::const_iterator it = m_grid->constBegin(); it != m_grid->constEnd(); ++it ) {
0580             if ( it.value()->color() == color
0581                     && ( !typeSet || it.value()->pieceType() == type )
0582                     && ( !fileSet || it.key().first == file )
0583                     && ( !rankSet || it.key().second == rank )
0584                     && legalMoves(it.key()).contains( Move( it.key(), move->to() ) ) ) {
0585                 if ( found ) {
0586                     qCWarning(LOG_KNIGHTS) << "Found more than one possible move";
0587                     move->setFlag ( Move::Illegal, true );
0588                     return;
0589                 }
0590                 move->setFrom( it.key() );
0591                 found = true;
0592             }
0593         }
0594         if ( !found ) {
0595             qCWarning(LOG_KNIGHTS) << "No possible moves found" << move->string();
0596             move->setFlag ( Move::Illegal, true );
0597             return;
0598         }
0599     } else {
0600         // Converting from Coordinate to Algebraic
0601 
0602         QStringList possibilities;
0603 
0604         /*
0605          * We try (in this order):
0606          * Ne6, Nde6, N5e6, Nd5e6
0607          */
0608         if (!m_grid->contains(move->from()))
0609             return; //piece not found in the grid. invalid move?
0610 
0611         Piece* piece = m_grid->value(move->from());
0612         const PieceType type = piece->pieceType();
0613         const QString typeLetter = (type != Pawn) ? Piece::charFromType ( type ) : QString();
0614         QString end = move->to().string();
0615 
0616         if ( move->flag ( Move::Take ) )
0617             end = QLatin1Char('x') + end;
0618 
0619         if ( move->flag ( Move::Promote ) )
0620             end += QLatin1Char('=') + Piece::charFromType( move->promotedType() );
0621 
0622         possibilities << typeLetter + end;
0623         possibilities << typeLetter + Pos::row ( move->from().first ) + end;
0624         possibilities << typeLetter + QString::number ( move->from().second ) + end;
0625         possibilities << typeLetter + move->from().string() + end;
0626 
0627         qCDebug(LOG_KNIGHTS) << possibilities;
0628 
0629         for ( const QString& text : std::as_const(possibilities) ) {
0630             Move m ( text );
0631             checkSpecialFlags ( &m, color );
0632             if ( m.isValid() ) {
0633                 move->setString ( text );
0634                 break;
0635             }
0636         }
0637 
0638     }
0639 
0640     qCDebug(LOG_KNIGHTS) << move->string();
0641 
0642     Q_ASSERT ( move->notation() == notation );
0643 }
0644 
0645 bool ChessRules::hasKingMoved(Color color) {
0646     PieceData data = qMakePair ( color, King );
0647     for ( const Move& move : Manager::self()->moveHistory() ) {
0648         if ( move.pieceData() == data )
0649             return true;
0650     }
0651     return false;
0652 }
0653 
0654 bool ChessRules::hasRookMoved(Color color, Move::CastlingSide side) {
0655     PieceData data = qMakePair ( color, Rook );
0656     Pos rookPos = ( side == Move::KingSide ) ? kingRookStartPos[color] : queenRookStartPos[color];
0657     for ( const Move& move : Manager::self()->moveHistory() ) {
0658         if ( move.pieceData() == data && move.from() == rookPos )
0659             return true;
0660     }
0661     return false;
0662 }