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 }