Compare commits
11 Commits
Menu_Featu
...
c38f134254
Author | SHA1 | Date | |
---|---|---|---|
c38f134254
|
|||
99c644bffb
|
|||
4602cbe187
|
|||
0a1f3d1e26
|
|||
4cf45f75bc
|
|||
4bf1447673
|
|||
27b684c550
|
|||
47b6a82be8
|
|||
af5b4ceb66
|
|||
36558c0c7c
|
|||
2d9d1d1eb3
|
@@ -8,3 +8,7 @@ trim_trailing_whitespace = true
|
||||
|
||||
[*.{cc,cpp,hpp,h}]
|
||||
insert_final_newline = true
|
||||
|
||||
[makefile]
|
||||
indent_size = 4
|
||||
indent_style = tab
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
.vscode/
|
||||
*.exe
|
||||
*.txt
|
||||
chess
|
@@ -127,6 +127,54 @@ ChessPiecePosition ChessPiece::GetPosition() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all chess piece positions of the chess piece which are still protective for the king.
|
||||
* Any position between the attacker chess piece and the current chess piece is protective for the king.
|
||||
* The position is also protective for the king if the chess piece can beat the attacker chess piece.
|
||||
* @param chessboard A pointer to the chessboard.
|
||||
* @param positions A set of positions the chess piece can move next (also positions are not protective).
|
||||
* @return A set of chess piece positions which are still protective for the king.
|
||||
*/
|
||||
std::set<ChessPiecePosition> ChessPiece::GetProtectivePositions(Chessboard* chessboard, std::set<ChessPiecePosition> positions) {
|
||||
ChessPiece* chessPieceKing = chessboard->GetChessPieceKing(this->GetColor());
|
||||
std::set<ChessPiecePosition> protectivePositions;
|
||||
|
||||
for (ChessPiece* chessPiece : chessboard->GetChessPieces()) {
|
||||
if (chessPiece->GetColor() != this->GetColor()) {
|
||||
std::set<ChessPiecePosition> positionsStay = chessPiece->GetNextPositions(chessboard, {});
|
||||
std::set<ChessPiecePosition> positionsMove = chessPiece->GetNextPositions(chessboard, {this->GetPosition()});
|
||||
|
||||
if (positionsStay.count(chessPieceKing->GetPosition()) == 0 && positionsMove.count(chessPieceKing->GetPosition()) == 1) {
|
||||
|
||||
// the position is protective if the current chess piece can beat the attacker chess piece.
|
||||
if (positions.count(chessPiece->GetPosition()) == 1) {
|
||||
protectivePositions.insert(chessPiece->GetPosition());
|
||||
}
|
||||
|
||||
// every position between the current chess piece and the attacker chess piece is also protective.
|
||||
std::string step = ChessPiecePosition::GetStep(chessPiece->GetPosition(), chessPieceKing->GetPosition());
|
||||
|
||||
if (step.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ChessPiecePosition currentPosition = chessPiece->GetPosition();
|
||||
ChessPiecePosition* nextPosition = ¤tPosition;
|
||||
|
||||
while (nextPosition = nextPosition->NextFromString(step)) {
|
||||
if (nextPosition->ToString() == this->GetPosition().ToString()) {
|
||||
break;
|
||||
} else if (positions.count(*nextPosition) == 1) {
|
||||
protectivePositions.insert(*nextPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return protectivePositions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a set of all positions along the rank.
|
||||
* @param chessboard A pointer to the chessboard.
|
||||
@@ -300,7 +348,7 @@ void ChessPiece::SetPosition(ChessPiecePosition position) {
|
||||
*/
|
||||
void ChessPiece::UpdateNextPositions(Chessboard* chessboard) {
|
||||
if (this->IsProtective(chessboard)) {
|
||||
this->SetNextPositions({});
|
||||
this->SetNextPositions(this->GetProtectivePositions(chessboard, this->GetNextPositions(chessboard)));
|
||||
} else {
|
||||
this->SetNextPositions(this->GetNextPositions(chessboard));
|
||||
}
|
||||
|
@@ -14,6 +14,8 @@ class ChessPiece {
|
||||
ChessPieceColor _color;
|
||||
std::vector<ChessPiecePosition> _positions;
|
||||
std::set<ChessPiecePosition> _positions_next;
|
||||
std::set<ChessPiecePosition> GetProtectivePositions(Chessboard* chessboard, std::set<ChessPiecePosition> positions);
|
||||
bool IsProtective(Chessboard* chessboard);
|
||||
|
||||
protected:
|
||||
char _char = '\0';
|
||||
@@ -46,7 +48,6 @@ class ChessPiece {
|
||||
bool IsKing();
|
||||
bool IsKnight();
|
||||
bool IsPawn();
|
||||
bool IsProtective(Chessboard* chessboard);
|
||||
bool IsQueen();
|
||||
bool IsRook();
|
||||
void SetPosition(ChessPiecePosition position);
|
||||
|
163
ChessPieces/ChessPieceMove.cpp
Normal file
163
ChessPieces/ChessPieceMove.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "../ChessPieces/ChessPieceMove.hpp"
|
||||
|
||||
/**
|
||||
* Creates a new move of a chess piece that uses the short algebraic notation.
|
||||
* @param move The move that uses the short algebraic notation.
|
||||
*/
|
||||
ChessPieceMove::ChessPieceMove(std::string move) {
|
||||
move = this->Normalize(move);
|
||||
|
||||
if (this->IsValidShortNotation(move)) {
|
||||
this->_move = move;
|
||||
this->ParseShortNotation();
|
||||
} else {
|
||||
throw std::invalid_argument("invalid move notation");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the move is a valid move that uses the short algebraic notation.
|
||||
* @return Status whether the move is a valid move that uses the short algebraic notation.
|
||||
*/
|
||||
bool ChessPieceMove::IsValidShortNotation(std::string move) {
|
||||
if (move.empty()) {
|
||||
return std::regex_match(this->_move, this->_notation);
|
||||
} else {
|
||||
return std::regex_match(move, this->_notation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the move which, if valid, can then be parsed.
|
||||
* @param move The move to be normalized.
|
||||
* @return The normalized move which, if valid, can then be parsed.
|
||||
*/
|
||||
std::string ChessPieceMove::Normalize(std::string move) {
|
||||
std::replace(move.begin(), move.end(), 'O', '0'); // castling: portable game notation to algebraic notation
|
||||
std::replace(move.begin(), move.end(), 'S', 'N'); // german notation to english notation (knight)
|
||||
std::replace(move.begin(), move.end(), 'L', 'B'); // german notation to english notation (bishop)
|
||||
std::replace(move.begin(), move.end(), 'T', 'R'); // german notation to english notation (queen)
|
||||
return move;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a move that uses the short algebraic notation.
|
||||
*/
|
||||
void ChessPieceMove::ParseShortNotation() {
|
||||
std::smatch parts;
|
||||
std::regex_search(this->_move, parts, this->_notation);
|
||||
this->_chessPiece = parts[1].str();
|
||||
this->_fromPositionFile = parts[2].str();
|
||||
this->_fromPositionRank = parts[3].str();
|
||||
this->_capture = parts[4].str();
|
||||
this->_toPositionFile = parts[5].str();
|
||||
this->_toPositionRank = parts[6].str();
|
||||
this->_promotion = parts[7].str();
|
||||
this->_castling = parts[8].str();
|
||||
this->_draw = parts[9].str();
|
||||
this->_additional = parts[10].str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the chess piece char of the move.
|
||||
* @return The chess piece char of the move.
|
||||
*/
|
||||
char ChessPieceMove::GetChessPieceChar() {
|
||||
return this->_chessPiece.empty() ? 'P' : this->_chessPiece[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file part of the source position.
|
||||
* @return The file part of the source position.
|
||||
*/
|
||||
char ChessPieceMove::GetFromPositionFile() {
|
||||
return this->_fromPositionFile.empty() ? '\0' : this->_fromPositionFile[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the rank part of the source position.
|
||||
* @return The rank part of the source position.
|
||||
*/
|
||||
int ChessPieceMove::GetFromPositionRank() {
|
||||
return this->_fromPositionRank.empty() ? 0 : stoi(this->_fromPositionRank);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the chess piece character of the promotion.
|
||||
* @return The chess piece character of the promotion.
|
||||
*/
|
||||
char ChessPieceMove::GetPromotionChessPieceChar() {
|
||||
return this->_promotion.empty() ? '\0' : this->_promotion[this->_promotion.length() - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file part of the target position.
|
||||
* @return The file part of the target position.
|
||||
*/
|
||||
char ChessPieceMove::GetToPositionFile() {
|
||||
return this->_toPositionFile.empty() ? '\0' : this->_toPositionFile[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the rank part of the target position.
|
||||
* @return The rank part of the target position.
|
||||
*/
|
||||
int ChessPieceMove::GetToPositionRank() {
|
||||
return this->_toPositionRank.empty() ? 0 : stoi(this->_toPositionRank);
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the move is a move with capture.
|
||||
* @return Status whether the move is a move with capture.
|
||||
*/
|
||||
bool ChessPieceMove::IsCapture() {
|
||||
return this->_capture == "x" ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the move results in a check.
|
||||
* @return Status whether the move results in a check.
|
||||
*/
|
||||
bool ChessPieceMove::IsCheck() {
|
||||
return this->_additional == "+";
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the move results in a checkmate.
|
||||
* @return Status whether the move results in a checkmate.
|
||||
*/
|
||||
bool ChessPieceMove::IsCheckmate() {
|
||||
return this->_additional == "++" || this->_additional == "#";
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the move is a offer for a draw.
|
||||
* @return Status whether the move is a offer for a draw.
|
||||
*/
|
||||
bool ChessPieceMove::IsDrawOffer() {
|
||||
return this->_draw == "(=)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the move is an "en passant" by pawn.
|
||||
* @return Status whether the move is an "en passant" by pawn.
|
||||
*/
|
||||
bool ChessPieceMove::IsEnPassant() {
|
||||
return this->_additional == "e.p.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the move is the long castling (castling queenside).
|
||||
* @return Status whether the move is the long castling (castling queenside).
|
||||
*/
|
||||
bool ChessPieceMove::IsLongCastling() {
|
||||
return this->_castling == "0-0-0";
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the move is the short castling (castling kingside).
|
||||
* @return Status whether the move is the short castling (castling kingside).
|
||||
*/
|
||||
bool ChessPieceMove::IsShortCastling() {
|
||||
return this->_castling == "0-0";
|
||||
}
|
41
ChessPieces/ChessPieceMove.hpp
Normal file
41
ChessPieces/ChessPieceMove.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef ChessPieceMove_H
|
||||
#define ChessPieceMove_H
|
||||
|
||||
#include <regex>
|
||||
|
||||
class ChessPieceMove {
|
||||
private:
|
||||
std::string _chessPiece;
|
||||
std::string _fromPositionFile;
|
||||
std::string _fromPositionRank;
|
||||
std::string _capture;
|
||||
std::string _toPositionFile;
|
||||
std::string _toPositionRank;
|
||||
std::string _promotion;
|
||||
std::string _castling;
|
||||
std::string _draw;
|
||||
std::string _additional;
|
||||
std::string _move;
|
||||
const std::regex _notation = std::regex("^(?:([RNBQKP]?)([a-h]?)([1-8]?)(x?)([a-h])([1-8])((?:[=])?[QRBN])?|(0-0(?:-0)?)|(\\(=\\)))([+#!?]| e.p.)*$");
|
||||
std::string Normalize(std::string move);
|
||||
void ParseShortNotation();
|
||||
|
||||
public:
|
||||
ChessPieceMove(std::string move);
|
||||
bool IsValidShortNotation(std::string move = "");
|
||||
char GetChessPieceChar();
|
||||
char GetFromPositionFile();
|
||||
int GetFromPositionRank();
|
||||
bool IsCapture();
|
||||
char GetToPositionFile();
|
||||
int GetToPositionRank();
|
||||
char GetPromotionChessPieceChar();
|
||||
bool IsShortCastling();
|
||||
bool IsLongCastling();
|
||||
bool IsDrawOffer();
|
||||
bool IsCheck();
|
||||
bool IsCheckmate();
|
||||
bool IsEnPassant();
|
||||
};
|
||||
|
||||
#endif
|
@@ -56,18 +56,18 @@ std::string ChessPiecePosition::GetDifference(ChessPiecePosition fromPosition, C
|
||||
} else {
|
||||
std::string difference;
|
||||
|
||||
if (toPosition.GetFile() < fromPosition.GetFile()) {
|
||||
difference.append(std::string(1, ChessPiecePosition::_MOVE_LEFT) + std::to_string(fromPosition.GetFile() - toPosition.GetFile()));
|
||||
} else if (toPosition.GetFile() > fromPosition.GetFile()) {
|
||||
difference.append(std::string(1, ChessPiecePosition::_MOVE_RIGHT) + std::to_string(toPosition.GetFile() - fromPosition.GetFile()));
|
||||
}
|
||||
|
||||
if (toPosition.GetRank() < fromPosition.GetRank()) {
|
||||
difference.append(std::string(1, ChessPiecePosition::_MOVE_BOTTOM) + std::to_string(fromPosition.GetRank() - toPosition.GetRank()));
|
||||
} else if (toPosition.GetRank() > fromPosition.GetRank()) {
|
||||
difference.append(std::string(1, ChessPiecePosition::_MOVE_TOP) + std::to_string(toPosition.GetRank() - fromPosition.GetRank()));
|
||||
}
|
||||
|
||||
if (toPosition.GetFile() < fromPosition.GetFile()) {
|
||||
difference.append(std::string(1, ChessPiecePosition::_MOVE_LEFT) + std::to_string(fromPosition.GetFile() - toPosition.GetFile()));
|
||||
} else if (toPosition.GetFile() > fromPosition.GetFile()) {
|
||||
difference.append(std::string(1, ChessPiecePosition::_MOVE_RIGHT) + std::to_string(toPosition.GetFile() - fromPosition.GetFile()));
|
||||
}
|
||||
|
||||
return difference;
|
||||
}
|
||||
}
|
||||
|
@@ -34,7 +34,33 @@ std::set<ChessPiecePosition> King::GetNextPositions(Chessboard* chessboard, std:
|
||||
ChessPieceColor opponentColor = (this->GetColor() == ChessPieceColor::Black) ? ChessPieceColor::White : ChessPieceColor::Black;
|
||||
|
||||
if (chessboard->IsPositionUnderAttack(*nextPosition, opponentColor) == false) {
|
||||
positions.insert(*nextPosition);
|
||||
bool isUnderAttackAfterMove = false;
|
||||
|
||||
// Positions are ignored which are still under attack after the king has moved.
|
||||
// There are fields under attack which are in the shadow of the king.
|
||||
for (ChessPiece* chessPiece : chessboard->GetChessPieces()) {
|
||||
if (chessPiece && chessPiece->GetColor() != this->GetColor()) {
|
||||
if (chessPiece->GetNextPositions().count(this->GetPosition()) == 1) {
|
||||
if (chessPiece->GetNextPositions(chessboard, {this->GetPosition(), *nextPosition}).count(*nextPosition) == 1) {
|
||||
isUnderAttackAfterMove = true;
|
||||
break;
|
||||
}
|
||||
} else if (chessPiece->IsKing()) {
|
||||
for (std::string nextMove : {"T1", "T1L1", "T1R1", "R1", "L1", "B1", "B1L1", "B1R1"}) {
|
||||
ChessPiecePosition* nextPositionKing = chessPiece->GetPosition().NextFromString(nextMove);
|
||||
|
||||
if (nextPositionKing && *nextPosition == *nextPositionKing) {
|
||||
isUnderAttackAfterMove = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isUnderAttackAfterMove == false) {
|
||||
positions.insert(*nextPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -21,148 +21,70 @@ std::set<ChessPiecePosition> Pawn::GetNextPositions(Chessboard* chessboard, std:
|
||||
std::set<ChessPiecePosition> positions;
|
||||
ChessPiecePosition currentPosition = this->GetPosition();
|
||||
ChessPiecePosition* nextPosition = ¤tPosition;
|
||||
std::vector<std::string> nextDiagonalPositions;
|
||||
std::string moveDifferenceOpponent;
|
||||
|
||||
if (this->GetColor() == ChessPieceColor::Black) {
|
||||
if (this->GetColor() == ChessPieceColor::White) {
|
||||
nextDiagonalPositions = {"T1L1", "T1R1"};
|
||||
moveDifferenceOpponent = "B2";
|
||||
} else {
|
||||
nextDiagonalPositions = {"B1L1", "B1R1"};
|
||||
moveDifferenceOpponent = "T2";
|
||||
}
|
||||
|
||||
// 3.7.1. The pawn may move forward to the square immediately in front of it on the same file, provided that this square is unoccupied.
|
||||
// 3.7.2. On its first move the pawn may move as in 3.7.1 or alternatively it may advance two squares along the same file, provided that both squares are unoccupied.
|
||||
for (int move = 1; move <= 2; move++) {
|
||||
nextPosition = nextPosition->NextBottom();
|
||||
std::string stepVertical = nextDiagonalPositions[0].substr(0, 2);
|
||||
|
||||
if (nextPosition) {
|
||||
if (chessboard->IsEmptyField(nextPosition) == false) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (move == 1) {
|
||||
positions.insert(*nextPosition);
|
||||
} else if (move == 2 && this->IsFirstMove()) {
|
||||
positions.insert(*nextPosition);
|
||||
}
|
||||
} else {
|
||||
// 3.7.1. The pawn may move forward to the square immediately in front of it on the same file, provided that this square is unoccupied.
|
||||
// 3.7.2. On its first move the pawn may move as in 3.7.1 or alternatively it may advance two squares along the same file, provided that both squares are unoccupied.
|
||||
for (int move = 1; move <= 2; move++) {
|
||||
if (nextPosition = nextPosition->NextFromString(stepVertical)) {
|
||||
if (chessboard->IsEmptyField(nextPosition) == false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 3.7.3. the pawn may move to a square occupied by an opponent's piece diagonally in front of it on an adjacent file, capturing that piece.
|
||||
for (std::string move : {"B1L1", "B1R1"}) {
|
||||
if (move == 1 || (move == 2 && this->IsFirstMove())) {
|
||||
positions.insert(*nextPosition);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 3.7.3. the pawn may move to a square occupied by an opponent's piece diagonally in front of it on an adjacent file, capturing that piece.
|
||||
for (std::string moveDiagonal : nextDiagonalPositions) {
|
||||
nextPosition = ¤tPosition;
|
||||
nextPosition = nextPosition->NextFromString(moveDiagonal);
|
||||
|
||||
if (nextPosition && chessboard->IsEmptyField(nextPosition) == false) {
|
||||
ChessPiece* chessPiece = chessboard->GetChessPiece(nextPosition);
|
||||
|
||||
if (chessPiece->GetColor() != this->GetColor()) {
|
||||
positions.insert(*nextPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3.7.3.1. A pawn occupying a square on the same rank as and on an adjacent file to an opponent's pawn which has just advanced two squares
|
||||
// in one move from its original square may capture this opponent's pawn as though the latter had been moved only one square.
|
||||
// 3.7.3.2. This capture is only legal on the move following this advance and is called an "en passant" capture.
|
||||
if (chessboard->HasMoves()) {
|
||||
std::pair<ChessPiecePosition, ChessPiecePosition> lastMovePositions = chessboard->GetLastMovePositions();
|
||||
std::string moveDifference = ChessPiecePosition::GetDifference(lastMovePositions.first, lastMovePositions.second);
|
||||
|
||||
for (std::string nextDiagonalPosition : nextDiagonalPositions) {
|
||||
nextPosition = ¤tPosition;
|
||||
nextPosition = nextPosition->NextFromString(move);
|
||||
std::string stepDiagonal = nextDiagonalPosition.substr(2, 2);
|
||||
nextPosition = nextPosition->NextFromString(stepDiagonal);
|
||||
|
||||
if (nextPosition && chessboard->IsEmptyField(nextPosition) == false) {
|
||||
ChessPiece* chessPiece = chessboard->GetChessPiece(nextPosition);
|
||||
|
||||
if (chessPiece->GetColor() != this->GetColor()) {
|
||||
positions.insert(*nextPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chessPiece->IsPawn() && chessPiece->GetColor() != this->GetColor()) {
|
||||
if (chessPiece->GetNumberOfMoves() == 1 && moveDifference == moveDifferenceOpponent && chessPiece->GetPosition() == lastMovePositions.second) {
|
||||
ChessPiecePosition* diagonalPosition = currentPosition.NextFromString(nextDiagonalPosition);
|
||||
|
||||
// 3.7.3.1. A pawn occupying a square on the same rank as and on an adjacent file to an opponent's pawn which has just advanced two squares
|
||||
// in one move from its original square may capture this opponent's pawn as though the latter had been moved only one square.
|
||||
// 3.7.3.2. This capture is only legal on the move following this advance and is called an "en passant" capture.
|
||||
if (chessboard->GetCountMoves() > 0) {
|
||||
std::pair<ChessPiecePosition, ChessPiecePosition> lastMove = chessboard->GetLastMove();
|
||||
std::string moveDifference = ChessPiecePosition::GetDifference(lastMove.first, lastMove.second);
|
||||
ChessPiecePosition* leftPosition = currentPosition.NextLeft();
|
||||
ChessPiecePosition* rightPosition = currentPosition.NextRight();
|
||||
|
||||
if (leftPosition && chessboard->IsEmptyField(leftPosition) == false) {
|
||||
ChessPiece* chessPiece = chessboard->GetChessPiece(leftPosition);
|
||||
|
||||
if (chessPiece->GetColor() != this->GetColor() && chessPiece->IsPawn()) {
|
||||
if (chessPiece->GetNumberOfMoves() == 1 && moveDifference == "T2" && chessPiece->GetPosition() == lastMove.second) {
|
||||
ChessPiecePosition* bottomLeftPosition = currentPosition.NextFromString("B1L1");
|
||||
|
||||
if (bottomLeftPosition && chessboard->IsEmptyField(bottomLeftPosition)) {
|
||||
positions.insert(*bottomLeftPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rightPosition && chessboard->IsEmptyField(rightPosition) == false) {
|
||||
ChessPiece* chessPiece = chessboard->GetChessPiece(rightPosition);
|
||||
|
||||
if (chessPiece->GetColor() != this->GetColor() && chessPiece->IsPawn()) {
|
||||
if (chessPiece->GetNumberOfMoves() == 1 && moveDifference == "T2" && chessPiece->GetPosition() == lastMove.second) {
|
||||
ChessPiecePosition* bottomRightPosition = currentPosition.NextFromString("B1R1");
|
||||
|
||||
if (bottomRightPosition && chessboard->IsEmptyField(bottomRightPosition)) {
|
||||
positions.insert(*bottomRightPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (this->GetColor() == ChessPieceColor::White) {
|
||||
|
||||
// 3.7.1. The pawn may move forward to the square immediately in front of it on the same file, provided that this square is unoccupied.
|
||||
// 3.7.2. On its first move the pawn may move as in 3.7.1 or alternatively it may advance two squares along the same file, provided that both squares are unoccupied.
|
||||
for (int move = 1; move <= 2; move++) {
|
||||
nextPosition = nextPosition->NextTop();
|
||||
|
||||
if (nextPosition) {
|
||||
if (chessboard->IsEmptyField(nextPosition) == false) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (move == 1) {
|
||||
positions.insert(*nextPosition);
|
||||
} else if (move == 2 && this->IsFirstMove()) {
|
||||
positions.insert(*nextPosition);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 3.7.3. the pawn may move to a square occupied by an opponent's piece diagonally in front of it on an adjacent file, capturing that piece.
|
||||
for (std::string move : {"T1L1", "T1R1"}) {
|
||||
nextPosition = ¤tPosition;
|
||||
nextPosition = nextPosition->NextFromString(move);
|
||||
|
||||
if (nextPosition && chessboard->IsEmptyField(nextPosition) == false) {
|
||||
ChessPiece* chessPiece = chessboard->GetChessPiece(nextPosition);
|
||||
|
||||
if (chessPiece->GetColor() != this->GetColor()) {
|
||||
positions.insert(*nextPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3.7.3.1. A pawn occupying a square on the same rank as and on an adjacent file to an opponent's pawn which has just advanced two squares
|
||||
// in one move from its original square may capture this opponent's pawn as though the latter had been moved only one square.
|
||||
// 3.7.3.2. This capture is only legal on the move following this advance and is called an "en passant" capture.
|
||||
if (chessboard->GetCountMoves() > 0) {
|
||||
std::pair<ChessPiecePosition, ChessPiecePosition> lastMove = chessboard->GetLastMove();
|
||||
std::string moveDifference = ChessPiecePosition::GetDifference(lastMove.first, lastMove.second);
|
||||
ChessPiecePosition* leftPosition = currentPosition.NextLeft();
|
||||
ChessPiecePosition* rightPosition = currentPosition.NextRight();
|
||||
|
||||
if (leftPosition && chessboard->IsEmptyField(leftPosition) == false) {
|
||||
ChessPiece* chessPiece = chessboard->GetChessPiece(leftPosition);
|
||||
|
||||
if (chessPiece->GetColor() != this->GetColor() && chessPiece->IsPawn()) {
|
||||
if (chessPiece->GetNumberOfMoves() == 1 && moveDifference == "B2" && chessPiece->GetPosition() == lastMove.second) {
|
||||
ChessPiecePosition* topLeftPosition = currentPosition.NextFromString("T1L1");
|
||||
|
||||
if (topLeftPosition && chessboard->IsEmptyField(topLeftPosition)) {
|
||||
positions.insert(*topLeftPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rightPosition && chessboard->IsEmptyField(rightPosition) == false) {
|
||||
ChessPiece* chessPiece = chessboard->GetChessPiece(rightPosition);
|
||||
|
||||
if (chessPiece->GetColor() != this->GetColor() && chessPiece->IsPawn()) {
|
||||
if (chessPiece->GetNumberOfMoves() == 1 && moveDifference == "B2" && chessPiece->GetPosition() == lastMove.second) {
|
||||
ChessPiecePosition* topRightPosition = currentPosition.NextFromString("T1R1");
|
||||
|
||||
if (topRightPosition && chessboard->IsEmptyField(topRightPosition)) {
|
||||
positions.insert(*topRightPosition);
|
||||
if (diagonalPosition && chessboard->IsEmptyField(diagonalPosition)) {
|
||||
positions.insert(*diagonalPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,10 +1,251 @@
|
||||
#include "../Chessboard/Chessboard.hpp"
|
||||
#include "../ChessPieces/Rook.hpp"
|
||||
#include "../ChessPieces/Queen.hpp"
|
||||
#include "../ChessPieces/Bishop.hpp"
|
||||
#include "../ChessPieces/Knight.hpp"
|
||||
#include "../ChessPieces/ChessPieceMove.hpp"
|
||||
#include "../ChessPieces/ChessPiece.hpp"
|
||||
|
||||
/**
|
||||
* Gets the current player.
|
||||
* @return A pointer to the current player.
|
||||
*/
|
||||
Player* Chessboard::GetCurrentPlayer() {
|
||||
return this->currentPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the history of all moves.
|
||||
* @return The history of all moves.
|
||||
*/
|
||||
std::vector<std::string> Chessboard::GetHistoryMoves() {
|
||||
return this->_historyMoves;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the history of all positions.
|
||||
* @return The history of all positions.
|
||||
*/
|
||||
std::vector<std::pair<ChessPiecePosition, ChessPiecePosition>> Chessboard::GetHistoryPositions() {
|
||||
return this->_historyPositions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the opponent player.
|
||||
* @return A pointer to the opponent player.
|
||||
*/
|
||||
Player* Chessboard::GetOpponentPlayer() {
|
||||
if (this->GetCurrentPlayer()->GetColor() == ChessPieceColor::Black) {
|
||||
return this->GetPlayer(ChessPieceColor::White);
|
||||
} else {
|
||||
return this->GetPlayer(ChessPieceColor::Black);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a player by the specified color.
|
||||
* @return Player* A pointer to the player with the specified color.
|
||||
*/
|
||||
Player* Chessboard::GetPlayer(ChessPieceColor color) {
|
||||
if (color == ChessPieceColor::Black) {
|
||||
return std::get<ChessPieceColor::Black>(this->players);
|
||||
} else {
|
||||
return std::get<ChessPieceColor::White>(this->players);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether moves have already been made.
|
||||
* @return Status whether moves have already been made.
|
||||
*/
|
||||
bool Chessboard::HasMoves() {
|
||||
return this->_historyMoves.size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the king of the current player is in checkmate.
|
||||
* @return Status whether the king of the current player is in checkmate.
|
||||
*/
|
||||
bool Chessboard::IsCheckmate() {
|
||||
int numberOfAttackers = 0;
|
||||
|
||||
if (this->IsInCheck() == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ChessPiece* chessPieceKing = this->GetChessPieceKing(this->GetCurrentPlayer()->GetColor());
|
||||
|
||||
if (chessPieceKing == nullptr) {
|
||||
throw std::domain_error("king is missing");
|
||||
}
|
||||
|
||||
// it is not checkmate if the king can move to another position.
|
||||
if (chessPieceKing->GetNextPositions().size() > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ChessPiece* chessPieceOpponent : this->GetChessPieces()) {
|
||||
if (chessPieceOpponent->GetColor() == this->GetOpponentPlayer()->GetColor()) {
|
||||
if (chessPieceOpponent->GetNextPositions().count(chessPieceKing->GetPosition()) == 1) {
|
||||
bool isDefensibleAttack = false;
|
||||
numberOfAttackers++;
|
||||
|
||||
// the attacker can be removed directly by another chess piece.
|
||||
for (ChessPiece* chessPieceCurrent : this->GetChessPieces()) {
|
||||
if (chessPieceCurrent->GetColor() == this->GetCurrentPlayer()->GetColor()) {
|
||||
if (chessPieceCurrent->GetNextPositions().count(chessPieceOpponent->GetPosition()) == 1) {
|
||||
isDefensibleAttack = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the attacker is defensible.
|
||||
if (isDefensibleAttack == true) {
|
||||
std::cout << isDefensibleAttack << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if a chess piece can move between the attacker and king.
|
||||
std::string step = ChessPiecePosition::GetStep(chessPieceOpponent->GetPosition(), chessPieceKing->GetPosition());
|
||||
|
||||
// there is no direct way between attacker and king. so the attacker is the knight.
|
||||
// in this case we can not defend the attack by moving a chess piece between attacker and king.
|
||||
if (step.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ChessPiecePosition currentPosition = chessPieceOpponent->GetPosition();
|
||||
ChessPiecePosition* nextPosition = ¤tPosition;
|
||||
|
||||
while (nextPosition = nextPosition->NextFromString(step)) {
|
||||
if (*nextPosition == chessPieceKing->GetPosition()) break;
|
||||
|
||||
for (ChessPiece* chessPieceCurrent : this->GetChessPieces()) {
|
||||
if (chessPieceCurrent->GetColor() == this->GetCurrentPlayer()->GetColor()) {
|
||||
if (chessPieceCurrent->GetNextPositions().count(*nextPosition) == 1) {
|
||||
isDefensibleAttack = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isDefensibleAttack == false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return numberOfAttackers > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the king of the current player is in check.
|
||||
* @return Status whether the king of the current player is in check.
|
||||
*/
|
||||
bool Chessboard::IsInCheck() {
|
||||
ChessPieceColor currentPlayerColor = this->GetCurrentPlayer()->GetColor();
|
||||
ChessPiece* chessPieceKing = this->GetChessPieceKing(currentPlayerColor);
|
||||
|
||||
if (chessPieceKing == nullptr) {
|
||||
throw std::domain_error("king is missing");
|
||||
}
|
||||
|
||||
// 3.9 The king in check:
|
||||
// 3.9.1 The king is said to be "in check" if it is attacked by one or more of the opponent's pieces, even if such pieces are
|
||||
// constrained from moving to the square occupied by the king because they would then leave or place their own king in check.
|
||||
// 3.9.2 No piece can be moved that will either expose the king of the same colour to check or leave that king in check.
|
||||
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
|
||||
if (field.second->GetColor() == this->GetOpponentPlayer()->GetColor()) {
|
||||
if (field.second->GetNextPositions().count(chessPieceKing->GetPosition()) == 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the match ends with a draw because of stalemate.
|
||||
* @return Status whether the match ends with a draw because of stalemate.
|
||||
*/
|
||||
bool Chessboard::IsStalemate() {
|
||||
ChessPieceColor currentPlayerColor = this->GetCurrentPlayer()->GetColor();
|
||||
ChessPieceColor opponentPlayerColor = this->GetOpponentPlayer()->GetColor();
|
||||
ChessPiece* chessPieceKing = this->GetChessPieceKing(currentPlayerColor);
|
||||
|
||||
if (chessPieceKing == nullptr) {
|
||||
throw std::domain_error("king is missing");
|
||||
}
|
||||
|
||||
// 5.2.1 The game is drawn when the player to move has no legal move and his/her king is not in check.
|
||||
// The game is said to end in "stalemate". This immediately ends the game.
|
||||
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
|
||||
if (field.second->GetColor() == currentPlayerColor) {
|
||||
if (field.second->GetNextPositions().size() > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this->IsInCheck()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a chess piece on the chessboard.
|
||||
* If there is already a chess piece on the field, the chess piece will be deleted.
|
||||
* @param chesspiece A pointer to the chess piece which is to be set.
|
||||
*/
|
||||
void Chessboard::SetChessPiece(ChessPiece* chesspiece) {
|
||||
this->chessboard[chesspiece->GetPosition()] = chesspiece;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the move to the history.
|
||||
* @param move The move to be added to the history.
|
||||
*/
|
||||
void Chessboard::SetToHistory(std::string move) {
|
||||
this->_historyMoves.push_back(move);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the source and target position to the history.
|
||||
* @param fromPosition The source position to be added to the history.
|
||||
* @param toPosition The target position to be added to the history.
|
||||
*/
|
||||
void Chessboard::SetToHistory(ChessPiecePosition fromPosition, ChessPiecePosition toPosition) {
|
||||
this->_historyPositions.push_back({fromPosition, toPosition});
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the current player.
|
||||
*/
|
||||
void Chessboard::SwitchCurrentPlayer() {
|
||||
this->currentPlayer = this->GetOpponentPlayer();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Chessboard::RemoveChessPiece(ChessPiecePosition position) {
|
||||
if (this->chessboard.count(position) == 1) {
|
||||
delete this->chessboard[position];
|
||||
this->chessboard.erase(position);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Chessboard::IsEmptyField(ChessPiecePosition* position) {
|
||||
return (this->chessboard.count(*position) == 0);
|
||||
}
|
||||
@@ -15,41 +256,212 @@ ChessPiece* Chessboard::GetChessPiece(ChessPiecePosition* position) {
|
||||
|
||||
|
||||
|
||||
void Chessboard::MoveChessPiece(std::string move) {
|
||||
if (move.length() == 5) {
|
||||
ChessPiecePosition* fromPosition = new ChessPiecePosition(move.substr(0, 2));
|
||||
ChessPiecePosition* toPosition = new ChessPiecePosition(move.substr(3, 2));
|
||||
bool Chessboard::MoveCastling(ChessPieceColor color, bool shortCastling) {
|
||||
ChessPiecePosition fromPositionKing = (color == ChessPieceColor::White) ? ChessPiecePosition("E1") : ChessPiecePosition("E8");
|
||||
ChessPiecePosition fromPositionRook = (color == ChessPieceColor::White) ? ChessPiecePosition(shortCastling ? "H1" : "A1") : ChessPiecePosition(shortCastling ? "H8" : "A8");
|
||||
ChessPiecePosition toPositionKing = (color == ChessPieceColor::White) ? ChessPiecePosition(shortCastling ? "G1" : "C1") : ChessPiecePosition(shortCastling ? "G8" : "C8");
|
||||
ChessPiecePosition toPositionRook = (color == ChessPieceColor::White) ? ChessPiecePosition(shortCastling ? "F1" : "D1") : ChessPiecePosition(shortCastling ? "F8" : "D8");
|
||||
|
||||
ChessPiece* piece = this->GetChessPiece(fromPosition);
|
||||
ChessPiece* chessPieceRook = this->GetChessPiece(&fromPositionRook);
|
||||
ChessPiece* chessPieceKing = this->GetChessPiece(&fromPositionKing);
|
||||
|
||||
if (piece->GetColor() != this->GetCurrentPlayer()->GetColor()) {
|
||||
return; // wrong player
|
||||
}
|
||||
if (chessPieceKing == nullptr || chessPieceRook == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (piece->GetNextPositions().count(*toPosition) == 1) {
|
||||
this->chessboard[*toPosition] = this->chessboard[*fromPosition];
|
||||
this->chessboard.erase(*fromPosition);
|
||||
piece->SetPosition(*toPosition);
|
||||
this->SetToHistory(*fromPosition, *toPosition);
|
||||
if (chessPieceKing->IsKing() == false || chessPieceRook->IsRook() == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (chessPieceKing->IsFirstMove() == false || chessPieceRook->IsFirstMove() == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string step = ChessPiecePosition::GetStep(chessPieceKing->GetPosition(), toPositionKing);
|
||||
ChessPiecePosition currentPosition = chessPieceKing->GetPosition();
|
||||
ChessPiecePosition* nextPosition = ¤tPosition;
|
||||
while (nextPosition = nextPosition->NextFromString(step)) {
|
||||
if (this->IsEmptyField(nextPosition)) {
|
||||
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
|
||||
if (field.second->GetColor() != color) {
|
||||
if (field.second->GetNextPositions().count(*nextPosition) == 0) {
|
||||
continue;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nextPosition->ToString() == toPositionKing.ToString()) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return; // wrong or invalid move
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this->chessboard[toPositionKing] = chessPieceKing;
|
||||
this->chessboard.erase(chessPieceKing->GetPosition());
|
||||
this->SetToHistory(chessPieceKing->GetPosition(), toPositionKing);
|
||||
chessPieceKing->SetPosition(toPositionKing);
|
||||
this->chessboard[toPositionRook] = chessPieceRook;
|
||||
this->chessboard.erase(chessPieceRook->GetPosition());
|
||||
this->SetToHistory(chessPieceRook->GetPosition(), toPositionRook);
|
||||
chessPieceRook->SetPosition(toPositionRook);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Chessboard::MoveChessPiece(std::string move) {
|
||||
ChessPieceMove chessPieceMove = ChessPieceMove(move);
|
||||
|
||||
if (chessPieceMove.IsValidShortNotation() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (chessPieceMove.IsShortCastling()) {
|
||||
this->MoveCastling(this->GetCurrentPlayer()->GetColor(), true);
|
||||
} else if (chessPieceMove.IsLongCastling()) {
|
||||
this->MoveCastling(this->GetCurrentPlayer()->GetColor(), false);
|
||||
} else if (chessPieceMove.IsDrawOffer()) {
|
||||
// Remis
|
||||
} else {
|
||||
ChessPiece* chessPiece = nullptr;
|
||||
ChessPiecePosition toChessPiecePosition = ChessPiecePosition(chessPieceMove.GetToPositionFile(), chessPieceMove.GetToPositionRank());
|
||||
char fromPositionFile = chessPieceMove.GetFromPositionFile();
|
||||
int fromPositionRank = chessPieceMove.GetFromPositionRank();
|
||||
|
||||
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
|
||||
if (field.second->GetColor() == this->GetCurrentPlayer()->GetColor()) {
|
||||
if (field.second->GetChar() == chessPieceMove.GetChessPieceChar() && field.second->GetNextPositions().count(toChessPiecePosition) == 1) {
|
||||
|
||||
if (fromPositionFile >= 'a' && fromPositionFile <= 'h') {
|
||||
if (field.second->GetPosition().GetFile() == std::toupper(fromPositionFile)) {
|
||||
chessPiece = field.second;
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (fromPositionRank >= 1 && fromPositionRank <= 8) {
|
||||
if (field.second->GetPosition().GetRank() == fromPositionRank) {
|
||||
chessPiece = field.second;
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
chessPiece = field.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (chessPiece) {
|
||||
if (this->IsEmptyField(&toChessPiecePosition) == false) {
|
||||
this->RemoveChessPiece(toChessPiecePosition);
|
||||
this->chessboard[toChessPiecePosition] = this->chessboard[chessPiece->GetPosition()];
|
||||
this->chessboard.erase(chessPiece->GetPosition());
|
||||
this->SetToHistory(chessPiece->GetPosition(), toChessPiecePosition);
|
||||
chessPiece->SetPosition(toChessPiecePosition);
|
||||
} else {
|
||||
if (chessPiece->IsPawn()) {
|
||||
if (chessPiece->GetColor() == ChessPieceColor::White) {
|
||||
std::string difference = ChessPiecePosition::GetDifference(chessPiece->GetPosition(), toChessPiecePosition);
|
||||
|
||||
if (difference == "T1L1" || difference == "T1R1") {
|
||||
ChessPiecePosition currentPosition = toChessPiecePosition;
|
||||
ChessPiecePosition* nextPosition = ¤tPosition;
|
||||
|
||||
if (nextPosition = nextPosition->NextBottom()) {
|
||||
if (this->IsEmptyField(nextPosition) == false) {
|
||||
ChessPiece* chessPieceRemove = this->GetChessPiece(nextPosition);
|
||||
|
||||
if (chessPieceRemove->GetColor() != chessPiece->GetColor() && chessPieceRemove->IsPawn()) {
|
||||
this->chessboard[toChessPiecePosition] = this->chessboard[chessPiece->GetPosition()];
|
||||
this->chessboard.erase(chessPiece->GetPosition());
|
||||
this->SetToHistory(chessPiece->GetPosition(), toChessPiecePosition);
|
||||
chessPiece->SetPosition(toChessPiecePosition);
|
||||
this->RemoveChessPiece(chessPieceRemove->GetPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this->chessboard[toChessPiecePosition] = this->chessboard[chessPiece->GetPosition()];
|
||||
this->chessboard.erase(chessPiece->GetPosition());
|
||||
this->SetToHistory(chessPiece->GetPosition(), toChessPiecePosition);
|
||||
chessPiece->SetPosition(toChessPiecePosition);
|
||||
}
|
||||
} else if (chessPiece->GetColor() == ChessPieceColor::Black) {
|
||||
std::string difference = ChessPiecePosition::GetDifference(chessPiece->GetPosition(), toChessPiecePosition);
|
||||
|
||||
if (difference == "B1L1" || difference == "B1R1") {
|
||||
ChessPiecePosition currentPosition = toChessPiecePosition;
|
||||
ChessPiecePosition* nextPosition = ¤tPosition;
|
||||
|
||||
if (nextPosition = nextPosition->NextTop()) {
|
||||
if (this->IsEmptyField(nextPosition) == false) {
|
||||
ChessPiece* chessPieceRemove = this->GetChessPiece(nextPosition);
|
||||
|
||||
if (chessPieceRemove->GetColor() != chessPiece->GetColor() && chessPieceRemove->IsPawn()) {
|
||||
this->chessboard[toChessPiecePosition] = this->chessboard[chessPiece->GetPosition()];
|
||||
this->chessboard.erase(chessPiece->GetPosition());
|
||||
this->SetToHistory(chessPiece->GetPosition(), toChessPiecePosition);
|
||||
chessPiece->SetPosition(toChessPiecePosition);
|
||||
this->RemoveChessPiece(chessPieceRemove->GetPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this->chessboard[toChessPiecePosition] = this->chessboard[chessPiece->GetPosition()];
|
||||
this->chessboard.erase(chessPiece->GetPosition());
|
||||
this->SetToHistory(chessPiece->GetPosition(), toChessPiecePosition);
|
||||
chessPiece->SetPosition(toChessPiecePosition);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this->chessboard[toChessPiecePosition] = this->chessboard[chessPiece->GetPosition()];
|
||||
this->chessboard.erase(chessPiece->GetPosition());
|
||||
this->SetToHistory(chessPiece->GetPosition(), toChessPiecePosition);
|
||||
chessPiece->SetPosition(toChessPiecePosition);
|
||||
}
|
||||
}
|
||||
|
||||
if (chessPieceMove.GetPromotionChessPieceChar() != '\0') {
|
||||
ChessPieceColor color = chessPiece->GetColor();
|
||||
this->RemoveChessPiece(toChessPiecePosition);
|
||||
|
||||
switch (chessPieceMove.GetPromotionChessPieceChar()) {
|
||||
case 'R':
|
||||
this->chessboard[toChessPiecePosition] = new Rook(color, toChessPiecePosition);
|
||||
break;
|
||||
case 'B':
|
||||
this->chessboard[toChessPiecePosition] = new Bishop(color, toChessPiecePosition);
|
||||
break;
|
||||
case 'N':
|
||||
this->chessboard[toChessPiecePosition] = new Knight(color, toChessPiecePosition);
|
||||
break;
|
||||
case 'Q':
|
||||
this->chessboard[toChessPiecePosition] = new Queen(color, toChessPiecePosition);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->SetToHistory(move);
|
||||
this->UpdateChessPieces();
|
||||
this->SwitchCurrentPlayer();
|
||||
}
|
||||
|
||||
void Chessboard::SetToHistory(ChessPiecePosition fromPosition, ChessPiecePosition toPosition) {
|
||||
this->history.push_back({fromPosition, toPosition});
|
||||
}
|
||||
|
||||
|
||||
int Chessboard::GetCountMoves() {
|
||||
return this->history.size();
|
||||
return this->_historyPositions.size();
|
||||
}
|
||||
|
||||
std::pair<ChessPiecePosition, ChessPiecePosition> Chessboard::GetLastMove() {
|
||||
return this->history.back();
|
||||
std::pair<ChessPiecePosition, ChessPiecePosition> Chessboard::GetLastMovePositions() {
|
||||
return this->_historyPositions.back();
|
||||
}
|
||||
|
||||
std::vector<ChessPiece*> Chessboard::GetChessPieces() {
|
||||
@@ -62,157 +474,9 @@ std::vector<ChessPiece*> Chessboard::GetChessPieces() {
|
||||
return chessPieces;
|
||||
}
|
||||
|
||||
bool Chessboard::IsCheck() {
|
||||
ChessPiece* chessPieceKing = this->GetChessPieceKing(this->GetCurrentPlayer()->GetColor());
|
||||
|
||||
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
|
||||
if (field.second->GetColor() == this->GetOpponentPlayer()->GetColor()) {
|
||||
if (field.second->GetNextPositions().count(chessPieceKing->GetPosition()) == 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Chessboard::IsCheckmate() {
|
||||
|
||||
if (this->IsCheck() == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ChessPiece* chessPieceKing = this->GetChessPieceKing(this->GetCurrentPlayer()->GetColor());
|
||||
|
||||
if (chessPieceKing->GetNextPositions().size() > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int cntAttacker = 0;
|
||||
|
||||
for (ChessPiece* chessPieceOpponent : this->GetChessPieces()) {
|
||||
if (chessPieceOpponent->GetColor() == this->GetOpponentPlayer()->GetColor()) {
|
||||
if (chessPieceOpponent->GetNextPositions().count(chessPieceKing->GetPosition()) == 1) { // Is an attacker
|
||||
cntAttacker++;
|
||||
|
||||
bool deniable = false;
|
||||
|
||||
// remove attacker
|
||||
for (ChessPiece* chessPieceCurrent : this->GetChessPieces()) {
|
||||
if (chessPieceCurrent->GetColor() == this->GetCurrentPlayer()->GetColor()) {
|
||||
if (chessPieceCurrent->GetNextPositions().count(chessPieceOpponent->GetPosition()) == 1) {
|
||||
deniable = true; // blocked attack because current player can remove attacker
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deniable == false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// move between attacker and king
|
||||
// this is not possible for pawn, knight and king because they beat on next field.
|
||||
|
||||
std::string step = ChessPiecePosition::GetStep(chessPieceOpponent->GetPosition(), chessPieceKing->GetPosition());
|
||||
|
||||
ChessPiecePosition currentPosition = chessPieceOpponent->GetPosition();
|
||||
ChessPiecePosition* nextPosition = ¤tPosition;
|
||||
deniable = false;
|
||||
|
||||
while (nextPosition = nextPosition->NextFromString(step)) {
|
||||
for (ChessPiece* chessPieceCurrent : this->GetChessPieces()) {
|
||||
if (chessPieceCurrent->GetColor() == this->GetCurrentPlayer()->GetColor()) {
|
||||
if (chessPieceCurrent->GetNextPositions().count(*nextPosition) == 1) {
|
||||
deniable = true; // blocked attack because current player can block between attacker and king
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deniable == false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cntAttacker > 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Chessboard::IsStalemate() {
|
||||
ChessPieceColor currentColor = this->GetCurrentPlayer()->GetColor();
|
||||
ChessPieceColor opponentColor = this->GetOpponentPlayer()->GetColor();
|
||||
ChessPiece* chessPieceKing;
|
||||
|
||||
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
|
||||
if (field.second->IsKing() && field.second->GetColor() == currentColor) {
|
||||
chessPieceKing = field.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Der König hat aber auch keine validen Züge mehr.
|
||||
if (chessPieceKing->GetNextPositions().size() > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Man hat selbst keine Figuren mehr welche noch ziehen könnten.
|
||||
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
|
||||
if (field.second->GetColor() == currentColor) {
|
||||
if (field.second->GetNextPositions().size() > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// König wird nicht direkt attackiert.
|
||||
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
|
||||
if (field.second->GetColor() == opponentColor) {
|
||||
if (field.second->GetNextPositions().count(chessPieceKing->GetPosition()) == 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current player.
|
||||
*/
|
||||
Player* Chessboard::GetCurrentPlayer() {
|
||||
return this->currentPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the opponent player.
|
||||
*/
|
||||
Player* Chessboard::GetOpponentPlayer() {
|
||||
if (this->GetCurrentPlayer()->GetColor() == ChessPieceColor::Black) {
|
||||
return this->GetPlayer(ChessPieceColor::White);
|
||||
} else {
|
||||
return this->GetPlayer(ChessPieceColor::Black);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a player by color.
|
||||
* @return Player* The player who is associated with the given color.
|
||||
*/
|
||||
Player* Chessboard::GetPlayer(ChessPieceColor color) {
|
||||
if (color == ChessPieceColor::Black) {
|
||||
return std::get<ChessPieceColor::Black>(this->players);
|
||||
} else {
|
||||
return std::get<ChessPieceColor::White>(this->players);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the players to the chessboard.
|
||||
@@ -236,16 +500,7 @@ void Chessboard::SetPlayers(Player* playerA, Player* playerB) {
|
||||
this->currentPlayer = this->GetPlayer(ChessPieceColor::White);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch the current player.
|
||||
*/
|
||||
void Chessboard::SwitchCurrentPlayer() {
|
||||
if (this->GetCurrentPlayer()->GetColor() == ChessPieceColor::White) {
|
||||
this->currentPlayer = this->GetPlayer(ChessPieceColor::Black);
|
||||
} else {
|
||||
this->currentPlayer = this->GetPlayer(ChessPieceColor::White);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the next positions of all chess pieces.
|
||||
@@ -268,15 +523,31 @@ void Chessboard::UpdateChessPieces() {
|
||||
*/
|
||||
bool Chessboard::IsPositionUnderAttack(ChessPiecePosition position, ChessPieceColor color) {
|
||||
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
|
||||
if (field.second->GetColor() == color && field.second->GetNextPositions().count(position) == 1) {
|
||||
if (field.second->IsPawn() == false && field.second->GetColor() == color && field.second->GetNextPositions().count(position) == 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
|
||||
if (field.second->IsPawn() && field.second->GetColor() == color) {
|
||||
std::string difference = ChessPiecePosition::GetDifference(field.second->GetPosition(), position);
|
||||
if (color == ChessPieceColor::White && (difference == "T1L1" || difference == "T1R1")) {
|
||||
return true;
|
||||
} else if (color == ChessPieceColor::Black && (difference == "B1L1" || difference == "B1R1")) {
|
||||
return true;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Chessboard::~Chessboard() {
|
||||
delete this->players.first;
|
||||
delete this->players.second;
|
||||
|
||||
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
|
||||
delete field.second;
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <random>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -15,24 +16,31 @@ class ChessPiece;
|
||||
|
||||
class Chessboard {
|
||||
private:
|
||||
std::vector<std::string> _historyMoves;
|
||||
std::vector<std::pair<ChessPiecePosition, ChessPiecePosition>> _historyPositions;
|
||||
std::map<ChessPiecePosition, ChessPiece*> chessboard;
|
||||
std::vector<std::pair<ChessPiecePosition, ChessPiecePosition>> history;
|
||||
std::pair<Player*, Player*> players;
|
||||
Player* currentPlayer;
|
||||
void RemoveChessPiece(ChessPiecePosition position);
|
||||
|
||||
public:
|
||||
~Chessboard();
|
||||
std::vector<std::string> GetHistoryMoves();
|
||||
std::vector<std::pair<ChessPiecePosition, ChessPiecePosition>> GetHistoryPositions();
|
||||
void SetChessPiece(ChessPiece* chesspiece);
|
||||
bool IsEmptyField(ChessPiecePosition* position);
|
||||
ChessPiece* GetChessPiece(ChessPiecePosition* position);
|
||||
std::vector<ChessPiece*> GetChessPieces();
|
||||
void UpdateChessPieces();
|
||||
void MoveChessPiece(std::string move);
|
||||
bool MoveCastling(ChessPieceColor color, bool shortCastling);
|
||||
void SetToHistory(std::string move);
|
||||
void SetToHistory(ChessPiecePosition fromPosition, ChessPiecePosition toPosition);
|
||||
int GetCountMoves();
|
||||
std::pair<ChessPiecePosition, ChessPiecePosition> GetLastMove();
|
||||
bool HasMoves();
|
||||
std::pair<ChessPiecePosition, ChessPiecePosition> GetLastMovePositions();
|
||||
bool IsCheckmate();
|
||||
bool IsCheck();
|
||||
bool IsInCheck();
|
||||
bool IsStalemate();
|
||||
bool IsPositionUnderAttack(ChessPiecePosition position, ChessPieceColor color);
|
||||
Player* GetCurrentPlayer();
|
||||
|
24
README.md
24
README.md
@@ -92,18 +92,30 @@
|
||||
|
||||
## ToDo
|
||||
- Speicherfunktion
|
||||
- Schachnotation (Lange)
|
||||
- Menü (ASCII Art of TurboSchach)
|
||||
- Startmenü
|
||||
- Spielmenü mit Historie, Kommand
|
||||
- Plattformunabhängiges CMD clearing vor dem Zeichnen
|
||||
- Spezialbewegung (Rochade, Umwandlung, En passant)
|
||||
|
||||
## Build
|
||||
## Kompilieren und Ausführen
|
||||
Um TurboSchach kompilieren zu können wird mindestens C++ 17 benötigt.
|
||||
|
||||
### Windows
|
||||
|
||||
```
|
||||
g++ -o chess.exe main.cpp ChessPieces/*.cpp Chessboard/*.cpp Player/*.cpp
|
||||
g++ -std=c++17 -o chess.exe main.cpp ChessPieces/*.cpp Chessboard/*.cpp Player/*.cpp
|
||||
./chess.exe
|
||||
```
|
||||
|
||||
## Voraussetzungen
|
||||
### Linux und MacOS
|
||||
|
||||
- C++ 17
|
||||
```
|
||||
# compile the project
|
||||
make
|
||||
|
||||
# run the project
|
||||
make run
|
||||
|
||||
# remove all output files
|
||||
make clean
|
||||
```
|
62
main.cpp
62
main.cpp
@@ -1,4 +1,7 @@
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <fstream>
|
||||
#include "Player/Player.hpp"
|
||||
#include "ChessPieces/Knight.hpp"
|
||||
#include "ChessPieces/Bishop.hpp"
|
||||
@@ -13,7 +16,9 @@
|
||||
#include "Chessboard/ChessboardVisualizerGraphic.hpp"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
#endif
|
||||
|
||||
std::cout << "Spieler A: ";
|
||||
std::string namePlayerA;
|
||||
@@ -69,13 +74,68 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
//ChessboardVisualizerText* visualizer = new ChessboardVisualizerText();
|
||||
ChessboardVisualizerGraphic* visualizer = new ChessboardVisualizerGraphic();
|
||||
visualizer->Draw(&chessboard);
|
||||
|
||||
// chessgames.com/1649097
|
||||
// https://www.chessgames.com/perl/chessgame?gid=1649097
|
||||
//
|
||||
//1258478 - Stalemate
|
||||
//std::ifstream input("C:\\Users\\Sebastian Brosch\\Desktop\\2767717.pgn");
|
||||
// std::ifstream input("C:\\Users\\Sebastian Brosch\\Desktop\\1258478.pgn");
|
||||
// //std::ifstream input("2767713.pgn");
|
||||
// //std::ifstream input("1482706.pgn");
|
||||
// //std::ifstream input("1693032.pgn");
|
||||
// //std::ifstream input("1272702.pgn");
|
||||
// //std::ifstream input("2446807.pgn");
|
||||
// //std::ifstream input("1884783.pgn");
|
||||
// std::regex moves_regex("([0-9]+.)(?:\n| )?([A-Za-z][A-Za-z0-9+#-=]+)(?:\n| )?([A-Za-z][A-Za-z0-9+#-]+)?");
|
||||
// std::smatch moves_matches;
|
||||
// std::string moves_content;
|
||||
// for (std::string line; getline(input, line);) {
|
||||
// if (line[0] == '[') {
|
||||
// // Info
|
||||
// } else {
|
||||
// moves_content.append((moves_content == "") ? "" : " ").append(line);
|
||||
// }
|
||||
// }
|
||||
// std::regex_search(moves_content, moves_matches, moves_regex);
|
||||
// std::vector<std::string> moves;
|
||||
|
||||
// for(std::sregex_iterator i = std::sregex_iterator(moves_content.begin(), moves_content.end(), moves_regex); i != std::sregex_iterator(); ++i) {
|
||||
// std::smatch m = *i;
|
||||
// if (m[2].str() != "") {
|
||||
// moves.push_back(m[2].str());
|
||||
// }
|
||||
// if (m[3].str() != "") {
|
||||
// moves.push_back(m[3].str());
|
||||
// }
|
||||
// }
|
||||
// int move_count = 0;
|
||||
// while (chessboard.IsCheckmate() == false) {
|
||||
// move_count++;
|
||||
// std::string move = moves.front();
|
||||
// moves.erase(moves.begin());
|
||||
// chessboard.MoveChessPiece(move);
|
||||
// visualizer->Draw(&chessboard);
|
||||
// std::cout << "SPIELZUG: " << move << " | " << move_count << std::endl;
|
||||
// if (moves.size() == 0) {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// std::cout << std::endl;
|
||||
// std::cout << "Game is over! - " << ((chessboard.GetOpponentPlayer()->GetColor() == ChessPieceColor::White) ? "White" : "Black") << " has won the game!" << std::endl;
|
||||
|
||||
// return 0;
|
||||
|
||||
|
||||
|
||||
while (chessboard.IsCheckmate() == false) {
|
||||
visualizer->Draw(&chessboard);
|
||||
std::string move;
|
||||
std::cout << "Move [" << ((chessboard.GetCurrentPlayer()->GetColor() == ChessPieceColor::White) ? "White" : "Black") << "] : ";
|
||||
std::cin >> move;
|
||||
chessboard.MoveChessPiece(move);
|
||||
visualizer->Draw(&chessboard);
|
||||
}
|
||||
|
||||
std::cout << "Game is over! - " << chessboard.GetOpponentPlayer()->GetName() << " has won the game!" << std::endl;
|
||||
|
10
makefile
Normal file
10
makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
all:
|
||||
g++ -std=c++17 -o chess main.cpp ChessPieces/*.cpp Chessboard/*.cpp Player/*.cpp
|
||||
|
||||
run:
|
||||
./chess
|
||||
|
||||
clean:
|
||||
rm -f main.exe
|
||||
rm -f chess.exe
|
||||
rm -f chess
|
Reference in New Issue
Block a user