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 }