Compare commits
4 Commits
4cf45f75bc
...
c38f134254
Author | SHA1 | Date | |
---|---|---|---|
c38f134254
|
|||
99c644bffb
|
|||
4602cbe187
|
|||
0a1f3d1e26
|
@@ -8,3 +8,7 @@ trim_trailing_whitespace = true
|
|||||||
|
|
||||||
[*.{cc,cpp,hpp,h}]
|
[*.{cc,cpp,hpp,h}]
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[makefile]
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = tab
|
@@ -34,10 +34,36 @@ std::set<ChessPiecePosition> King::GetNextPositions(Chessboard* chessboard, std:
|
|||||||
ChessPieceColor opponentColor = (this->GetColor() == ChessPieceColor::Black) ? ChessPieceColor::White : ChessPieceColor::Black;
|
ChessPieceColor opponentColor = (this->GetColor() == ChessPieceColor::Black) ? ChessPieceColor::White : ChessPieceColor::Black;
|
||||||
|
|
||||||
if (chessboard->IsPositionUnderAttack(*nextPosition, opponentColor) == false) {
|
if (chessboard->IsPositionUnderAttack(*nextPosition, opponentColor) == false) {
|
||||||
|
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);
|
positions.insert(*nextPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return positions;
|
return positions;
|
||||||
}
|
}
|
||||||
|
@@ -21,22 +21,28 @@ std::set<ChessPiecePosition> Pawn::GetNextPositions(Chessboard* chessboard, std:
|
|||||||
std::set<ChessPiecePosition> positions;
|
std::set<ChessPiecePosition> positions;
|
||||||
ChessPiecePosition currentPosition = this->GetPosition();
|
ChessPiecePosition currentPosition = this->GetPosition();
|
||||||
ChessPiecePosition* nextPosition = ¤tPosition;
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string stepVertical = nextDiagonalPositions[0].substr(0, 2);
|
||||||
|
|
||||||
// 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.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.
|
// 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++) {
|
for (int move = 1; move <= 2; move++) {
|
||||||
nextPosition = nextPosition->NextBottom();
|
if (nextPosition = nextPosition->NextFromString(stepVertical)) {
|
||||||
|
|
||||||
if (nextPosition) {
|
|
||||||
if (chessboard->IsEmptyField(nextPosition) == false) {
|
if (chessboard->IsEmptyField(nextPosition) == false) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (move == 1) {
|
if (move == 1 || (move == 2 && this->IsFirstMove())) {
|
||||||
positions.insert(*nextPosition);
|
|
||||||
} else if (move == 2 && this->IsFirstMove()) {
|
|
||||||
positions.insert(*nextPosition);
|
positions.insert(*nextPosition);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -45,9 +51,9 @@ std::set<ChessPiecePosition> Pawn::GetNextPositions(Chessboard* chessboard, std:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
// 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"}) {
|
for (std::string moveDiagonal : nextDiagonalPositions) {
|
||||||
nextPosition = ¤tPosition;
|
nextPosition = ¤tPosition;
|
||||||
nextPosition = nextPosition->NextFromString(move);
|
nextPosition = nextPosition->NextFromString(moveDiagonal);
|
||||||
|
|
||||||
if (nextPosition && chessboard->IsEmptyField(nextPosition) == false) {
|
if (nextPosition && chessboard->IsEmptyField(nextPosition) == false) {
|
||||||
ChessPiece* chessPiece = chessboard->GetChessPiece(nextPosition);
|
ChessPiece* chessPiece = chessboard->GetChessPiece(nextPosition);
|
||||||
@@ -61,108 +67,24 @@ std::set<ChessPiecePosition> Pawn::GetNextPositions(Chessboard* chessboard, std:
|
|||||||
// 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
|
// 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.
|
// 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.
|
// 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) {
|
if (chessboard->HasMoves()) {
|
||||||
std::pair<ChessPiecePosition, ChessPiecePosition> lastMove = chessboard->GetLastMove();
|
std::pair<ChessPiecePosition, ChessPiecePosition> lastMovePositions = chessboard->GetLastMovePositions();
|
||||||
std::string moveDifference = ChessPiecePosition::GetDifference(lastMove.first, lastMove.second);
|
std::string moveDifference = ChessPiecePosition::GetDifference(lastMovePositions.first, lastMovePositions.second);
|
||||||
ChessPiecePosition* leftPosition = currentPosition.NextLeft();
|
|
||||||
ChessPiecePosition* rightPosition = currentPosition.NextRight();
|
|
||||||
|
|
||||||
if (leftPosition && chessboard->IsEmptyField(leftPosition) == false) {
|
for (std::string nextDiagonalPosition : nextDiagonalPositions) {
|
||||||
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 = ¤tPosition;
|
||||||
nextPosition = nextPosition->NextFromString(move);
|
std::string stepDiagonal = nextDiagonalPosition.substr(2, 2);
|
||||||
|
nextPosition = nextPosition->NextFromString(stepDiagonal);
|
||||||
|
|
||||||
if (nextPosition && chessboard->IsEmptyField(nextPosition) == false) {
|
if (nextPosition && chessboard->IsEmptyField(nextPosition) == false) {
|
||||||
ChessPiece* chessPiece = chessboard->GetChessPiece(nextPosition);
|
ChessPiece* chessPiece = chessboard->GetChessPiece(nextPosition);
|
||||||
|
|
||||||
if (chessPiece->GetColor() != this->GetColor()) {
|
if (chessPiece->IsPawn() && chessPiece->GetColor() != this->GetColor()) {
|
||||||
positions.insert(*nextPosition);
|
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
|
if (diagonalPosition && chessboard->IsEmptyField(diagonalPosition)) {
|
||||||
// in one move from its original square may capture this opponent's pawn as though the latter had been moved only one square.
|
positions.insert(*diagonalPosition);
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,10 +6,246 @@
|
|||||||
#include "../ChessPieces/ChessPieceMove.hpp"
|
#include "../ChessPieces/ChessPieceMove.hpp"
|
||||||
#include "../ChessPieces/ChessPiece.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) {
|
void Chessboard::SetChessPiece(ChessPiece* chesspiece) {
|
||||||
this->chessboard[chesspiece->GetPosition()] = 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) {
|
bool Chessboard::IsEmptyField(ChessPiecePosition* position) {
|
||||||
return (this->chessboard.count(*position) == 0);
|
return (this->chessboard.count(*position) == 0);
|
||||||
}
|
}
|
||||||
@@ -18,12 +254,7 @@ ChessPiece* Chessboard::GetChessPiece(ChessPiecePosition* position) {
|
|||||||
return this->chessboard.at(*position);
|
return this->chessboard.at(*position);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chessboard::RemoveChessPiece(ChessPiecePosition position) {
|
|
||||||
if (this->chessboard.count(position) == 1) {
|
|
||||||
delete this->chessboard[position];
|
|
||||||
this->chessboard.erase(position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Chessboard::MoveCastling(ChessPieceColor color, bool shortCastling) {
|
bool Chessboard::MoveCastling(ChessPieceColor color, bool shortCastling) {
|
||||||
ChessPiecePosition fromPositionKing = (color == ChessPieceColor::White) ? ChessPiecePosition("E1") : ChessPiecePosition("E8");
|
ChessPiecePosition fromPositionKing = (color == ChessPieceColor::White) ? ChessPiecePosition("E1") : ChessPiecePosition("E8");
|
||||||
@@ -218,20 +449,19 @@ void Chessboard::MoveChessPiece(std::string move) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->SetToHistory(move);
|
||||||
this->UpdateChessPieces();
|
this->UpdateChessPieces();
|
||||||
this->SwitchCurrentPlayer();
|
this->SwitchCurrentPlayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chessboard::SetToHistory(ChessPiecePosition fromPosition, ChessPiecePosition toPosition) {
|
|
||||||
this->history.push_back({fromPosition, toPosition});
|
|
||||||
}
|
|
||||||
|
|
||||||
int Chessboard::GetCountMoves() {
|
int Chessboard::GetCountMoves() {
|
||||||
return this->history.size();
|
return this->_historyPositions.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<ChessPiecePosition, ChessPiecePosition> Chessboard::GetLastMove() {
|
std::pair<ChessPiecePosition, ChessPiecePosition> Chessboard::GetLastMovePositions() {
|
||||||
return this->history.back();
|
return this->_historyPositions.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ChessPiece*> Chessboard::GetChessPieces() {
|
std::vector<ChessPiece*> Chessboard::GetChessPieces() {
|
||||||
@@ -244,155 +474,9 @@ std::vector<ChessPiece*> Chessboard::GetChessPieces() {
|
|||||||
return chessPieces;
|
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) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
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.
|
* Set the players to the chessboard.
|
||||||
@@ -416,16 +500,7 @@ void Chessboard::SetPlayers(Player* playerA, Player* playerB) {
|
|||||||
this->currentPlayer = this->GetPlayer(ChessPieceColor::White);
|
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.
|
* Update the next positions of all chess pieces.
|
||||||
|
@@ -16,14 +16,17 @@ class ChessPiece;
|
|||||||
|
|
||||||
class Chessboard {
|
class Chessboard {
|
||||||
private:
|
private:
|
||||||
|
std::vector<std::string> _historyMoves;
|
||||||
|
std::vector<std::pair<ChessPiecePosition, ChessPiecePosition>> _historyPositions;
|
||||||
std::map<ChessPiecePosition, ChessPiece*> chessboard;
|
std::map<ChessPiecePosition, ChessPiece*> chessboard;
|
||||||
std::vector<std::pair<ChessPiecePosition, ChessPiecePosition>> history;
|
|
||||||
std::pair<Player*, Player*> players;
|
std::pair<Player*, Player*> players;
|
||||||
Player* currentPlayer;
|
Player* currentPlayer;
|
||||||
void RemoveChessPiece(ChessPiecePosition position);
|
void RemoveChessPiece(ChessPiecePosition position);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~Chessboard();
|
~Chessboard();
|
||||||
|
std::vector<std::string> GetHistoryMoves();
|
||||||
|
std::vector<std::pair<ChessPiecePosition, ChessPiecePosition>> GetHistoryPositions();
|
||||||
void SetChessPiece(ChessPiece* chesspiece);
|
void SetChessPiece(ChessPiece* chesspiece);
|
||||||
bool IsEmptyField(ChessPiecePosition* position);
|
bool IsEmptyField(ChessPiecePosition* position);
|
||||||
ChessPiece* GetChessPiece(ChessPiecePosition* position);
|
ChessPiece* GetChessPiece(ChessPiecePosition* position);
|
||||||
@@ -31,11 +34,13 @@ class Chessboard {
|
|||||||
void UpdateChessPieces();
|
void UpdateChessPieces();
|
||||||
void MoveChessPiece(std::string move);
|
void MoveChessPiece(std::string move);
|
||||||
bool MoveCastling(ChessPieceColor color, bool shortCastling);
|
bool MoveCastling(ChessPieceColor color, bool shortCastling);
|
||||||
|
void SetToHistory(std::string move);
|
||||||
void SetToHistory(ChessPiecePosition fromPosition, ChessPiecePosition toPosition);
|
void SetToHistory(ChessPiecePosition fromPosition, ChessPiecePosition toPosition);
|
||||||
int GetCountMoves();
|
int GetCountMoves();
|
||||||
std::pair<ChessPiecePosition, ChessPiecePosition> GetLastMove();
|
bool HasMoves();
|
||||||
|
std::pair<ChessPiecePosition, ChessPiecePosition> GetLastMovePositions();
|
||||||
bool IsCheckmate();
|
bool IsCheckmate();
|
||||||
bool IsCheck();
|
bool IsInCheck();
|
||||||
bool IsStalemate();
|
bool IsStalemate();
|
||||||
bool IsPositionUnderAttack(ChessPiecePosition position, ChessPieceColor color);
|
bool IsPositionUnderAttack(ChessPiecePosition position, ChessPieceColor color);
|
||||||
Player* GetCurrentPlayer();
|
Player* GetCurrentPlayer();
|
||||||
|
19
README.md
19
README.md
@@ -98,21 +98,24 @@
|
|||||||
- Plattformunabhängiges CMD clearing vor dem Zeichnen
|
- Plattformunabhängiges CMD clearing vor dem Zeichnen
|
||||||
|
|
||||||
## Kompilieren und Ausführen
|
## Kompilieren und Ausführen
|
||||||
|
Um TurboSchach kompilieren zu können wird mindestens C++ 17 benötigt.
|
||||||
|
|
||||||
### Windows
|
### 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
|
./chess.exe
|
||||||
```
|
```
|
||||||
|
|
||||||
### Linux
|
### Linux und MacOS
|
||||||
|
|
||||||
```
|
```
|
||||||
g++ -o chess main.cpp ChessPieces/*.cpp Chessboard/*.cpp Player/*.cpp
|
# compile the project
|
||||||
./chess
|
make
|
||||||
|
|
||||||
|
# run the project
|
||||||
|
make run
|
||||||
|
|
||||||
|
# remove all output files
|
||||||
|
make clean
|
||||||
```
|
```
|
||||||
|
|
||||||
## Voraussetzungen
|
|
||||||
|
|
||||||
- C++ 17
|
|
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
|
2
run.ps1
2
run.ps1
@@ -1,2 +1,2 @@
|
|||||||
& "g++" -o chess main.cpp ChessPieces/*.cpp Chessboard/*.cpp Player/*.cpp
|
& "g++" -std=c++17 -o chess main.cpp ChessPieces/*.cpp Chessboard/*.cpp Player/*.cpp
|
||||||
./chess.exe
|
./chess.exe
|
Reference in New Issue
Block a user