File indexing completed on 2024-05-12 04:04:14
0001 /*************************************************************************** 0002 File : move.cpp 0003 Project : Knights 0004 Description : Class representing a chess move 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2016 Alexander Semke (alexander.semke@web.de) 0007 SPDX-FileCopyrightText: 2009-2011 Miha Čančula (miha@noughmad.eu) 0008 0009 ***************************************************************************/ 0010 0011 /*************************************************************************** 0012 * * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program; if not, write to the Free Software * 0022 * Foundation, Inc., 51 Franklin Street, Fifth Floor, * 0023 * Boston, MA 02110-1301 USA * 0024 * * 0025 ***************************************************************************/ 0026 #include "move.h" 0027 #include "knightsdebug.h" 0028 0029 #include <QTime> 0030 #include <QRegExp> 0031 #include <QRegularExpression> 0032 0033 namespace Knights { 0034 class MovePrivate : public QSharedData { 0035 public: 0036 MovePrivate(); 0037 0038 Pos from; 0039 Pos to; 0040 0041 QList<Move> extraMoves; 0042 0043 PieceType promotedType; 0044 Move::Flags flags; 0045 0046 Move::Notation notationType; 0047 QString string; 0048 0049 PieceDataMap addedPieces; 0050 PieceDataMap removedPieces; 0051 0052 QTime time; 0053 PieceData pieceData; 0054 0055 QMap<Move::Notation, QString> notationStrings; 0056 }; 0057 0058 MovePrivate::MovePrivate() 0059 : from(Pos() ), 0060 to(Pos() ), 0061 promotedType(NoType ), 0062 flags(Move::None ), 0063 notationType( Move::NoNotation) { 0064 } 0065 0066 0067 Move Move::castling(Move::CastlingSide side, Color color) { 0068 const int rank = (color == White) ? 1 : 8; 0069 const int rookFile = (side == QueenSide) ? 1 : 8; 0070 const int kingDestinationFile = (side == QueenSide) ? 3 : 7; 0071 const int rookDestinationFile = (side == QueenSide) ? 4 : 6; 0072 0073 Move m; 0074 m.setFrom(5, rank); 0075 m.setTo(kingDestinationFile, rank); 0076 m.setFlag(Castle, true); 0077 0078 Move rookMove; 0079 rookMove.setFrom(rookFile, rank); 0080 rookMove.setTo(rookDestinationFile, rank); 0081 rookMove.setFlag(Forced, true); 0082 m.setAdditionalMoves({rookMove}); 0083 0084 QLatin1String str((side == QueenSide) ? "O-O-O" : "O-O"); 0085 m.setStringForNotation(Coordinate, str); 0086 m.setStringForNotation(Algebraic, str); 0087 m.setStringForNotation(LongAlgebraic, str); 0088 0089 return m; 0090 } 0091 0092 Move::Move() : d(new MovePrivate) { 0093 0094 } 0095 0096 Move::Move(Pos from, Pos to, Move::Flags flags) : d(new MovePrivate) { 0097 setFrom(from); 0098 setTo(to); 0099 setFlags(flags); 0100 } 0101 0102 Move::Move(QString string) : d(new MovePrivate) { 0103 setString(string); 0104 } 0105 0106 Move::Move(const Move& other) = default; 0107 0108 Move::~Move() = default; 0109 0110 void Move::operator=(const Move& other) { 0111 d = other.d; 0112 } 0113 0114 bool Move::flag(Move::MoveFlag flag) const { 0115 return d->flags & flag; 0116 } 0117 0118 Move::Flags Move::flags() const { 0119 return d->flags; 0120 } 0121 0122 void Move::setFlag(Move::MoveFlag flag, bool value) { 0123 if(value ) 0124 d->flags |= flag; 0125 else 0126 d->flags &= ~flag; 0127 0128 if(flag == Forced) { 0129 for(QList<Move>::iterator it = d->extraMoves.begin(); it != d->extraMoves.end(); ++it) 0130 it->setFlag(flag, value); 0131 } 0132 } 0133 0134 void Move::setFlags(Move::Flags flags) { 0135 d->flags = flags; 0136 } 0137 0138 void Move::setString(QString string) { 0139 d->flags = None; 0140 d->extraMoves.clear(); 0141 0142 if( string.contains(QLatin1Char('x')) ) { 0143 setFlag(Take, true); 0144 string.remove(QLatin1Char('x')); 0145 } 0146 0147 if( string.contains(QLatin1Char('+')) ) { 0148 setFlag(Check, true); 0149 string.remove(QLatin1Char('+')); 0150 } 0151 0152 // In case of double check symbol ++, remove the last + 0153 if( string.contains(QLatin1Char('+')) ) 0154 string.remove(QLatin1Char('+')); 0155 0156 if( string.contains(QLatin1Char('#')) ) { 0157 setFlag(CheckMate, true); 0158 string.remove(QLatin1Char('#')); 0159 } 0160 0161 string.remove(QLatin1Char('-')); 0162 string.remove(QLatin1Char(' ')); 0163 string.remove(QLatin1Char('=')); 0164 0165 QRegExp longMoveTest = QRegExp(QLatin1String("^[a-h][1-8][a-h][1-8]")); 0166 if (longMoveTest.indexIn(string) > -1) { 0167 // Long move notation, can be directly converted to From and To 0168 d->notationType = Coordinate; 0169 setFrom( Pos(string.left(2)) ); 0170 setTo( Pos(string.mid(2, 2)) ); 0171 0172 //check whether we need to promote a pawn (string looks like a7a8R for promotion to rook, etc.) 0173 if(string.size() > 4) { 0174 setFlag(Promote, true); 0175 setPromotedType( Piece::typeFromChar(string[4]) ); 0176 } 0177 } else { 0178 // Short notation, just store the string for later 0179 d->string = string; 0180 d->notationType = Algebraic; 0181 } 0182 } 0183 0184 QString Move::string(bool includeX) const { 0185 if(d->notationType == Algebraic) 0186 return d->string; 0187 0188 QString str = from().string(); 0189 if( (flags() & Take) && includeX ) 0190 str += QLatin1Char('x'); 0191 0192 str += to().string(); 0193 if(flags() & Promote) { 0194 str += QLatin1Char('='); 0195 str += Piece::charFromType(promotedType()); 0196 } 0197 0198 return str; 0199 } 0200 0201 0202 Pos Move::from() const { 0203 return d->from; 0204 } 0205 0206 Pos Move::to() const { 0207 return d->to; 0208 } 0209 0210 void Move::setFrom(const Pos& value) { 0211 d->from = value; 0212 d->notationType = Coordinate; 0213 } 0214 0215 void Move::setFrom(int first, int second) { 0216 setFrom(Pos(first, second )); 0217 } 0218 0219 void Move::setTo(const Pos& value) { 0220 d->to = value; 0221 } 0222 0223 void Move::setTo(int first, int second) { 0224 d->to = Pos(first, second); 0225 } 0226 0227 const QList< Move >& Move::additionalMoves() const { 0228 return d->extraMoves; 0229 } 0230 0231 void Move::setAdditionalMoves(const QList< Move >& list) { 0232 d->extraMoves.clear(); 0233 for (const Move& move : list) { 0234 Move m = move; 0235 m.setFlag(Move::Additional, true); 0236 d->extraMoves << m; 0237 } 0238 } 0239 0240 PieceType Move::promotedType() const { 0241 return d->promotedType; 0242 } 0243 0244 void Move::setPromotedType(PieceType type) { 0245 d->promotedType = type; 0246 } 0247 0248 Move::Notation Move::notation() const { 0249 return d->notationType; 0250 } 0251 0252 const PieceDataMap& Move::removedPieces() const { 0253 return d->removedPieces; 0254 } 0255 0256 void Move::addRemovedPiece(const Pos& pos, const PieceData& data) { 0257 d->removedPieces.insert(pos, data); 0258 } 0259 0260 void Move::setRemovedPieces(const PieceDataMap& map) { 0261 d->removedPieces = map; 0262 } 0263 0264 const PieceDataMap& Move::addedPieces() const { 0265 return d->addedPieces; 0266 } 0267 0268 void Move::addAddedPiece(const Pos& pos, const PieceData& data) { 0269 d->addedPieces.insert(pos, data); 0270 } 0271 0272 void Move::setAddedPieces(const PieceDataMap& map) { 0273 d->addedPieces = map; 0274 } 0275 0276 bool Move::operator==(Move other) const { 0277 return(d->from == other.from() && d->to == other.to()); 0278 } 0279 0280 Move Move::reverse() const { 0281 Move rev; 0282 rev.setFlags(d->flags); 0283 0284 if(notation() == Algebraic) { 0285 // We can't reverse the move from the algebraic notation alone 0286 // Please don't call this with a short notation move 0287 qCCritical(LOG_KNIGHTS) << "Can't reverse the move from short algebraic notation"; 0288 return rev; 0289 } 0290 rev.setTo(d->from); 0291 rev.setFrom(d->to); 0292 if(flags() & Promote ) 0293 rev.setPromotedType(Pawn); 0294 0295 QList<Move> additionalMoves; 0296 for (const Move& m : d->extraMoves) 0297 additionalMoves << m.reverse(); 0298 rev.setAdditionalMoves(additionalMoves); 0299 0300 rev.setAddedPieces(d->removedPieces); 0301 rev.setRemovedPieces(d->addedPieces); 0302 0303 return rev; 0304 } 0305 0306 bool Move::isValid() const { 0307 if(d->flags & Illegal ) 0308 return false; 0309 if(d->notationType == Coordinate ) 0310 return d->from.isValid() && d->to.isValid(); 0311 else if(d->string.size() < 6) { 0312 if(d->string.contains(QRegularExpression(QLatin1String("[a-h][1-8]"))) || d->string.contains(QLatin1String("o-o")) ) 0313 return true; 0314 } 0315 return false; 0316 } 0317 0318 /** 0319 * Sets the time at which this move was made. 0320 * It is used to reset the clock after undoing. 0321 */ 0322 void Move::setTime(const QTime& time) { 0323 d->time = time; 0324 } 0325 0326 /** 0327 * Returns the time at which the move was made. 0328 */ 0329 QTime Move::time() { 0330 return d->time; 0331 } 0332 0333 void Move::setPieceData(const PieceData& data) { 0334 d->pieceData = data; 0335 } 0336 0337 PieceData Move::pieceData() const { 0338 return d->pieceData; 0339 } 0340 0341 void Move::setStringForNotation(Move::Notation notation, const QString& string) { 0342 d->notationStrings [ notation ] = string; 0343 } 0344 0345 QString Move::stringForNotation(Move::Notation notation) const { 0346 if(d->notationStrings.contains(notation) ) 0347 return d->notationStrings [ notation ]; 0348 else 0349 return QString(); 0350 } 0351 0352 } 0353 0354 QDebug operator<<(QDebug debug, const Knights::Move& move) { 0355 debug << move.string(true); 0356 return debug; 0357 }