Compare commits
79 Commits
1003456908
...
main
Author | SHA1 | Date | |
---|---|---|---|
3891d90f43
|
|||
a7ef306800
|
|||
a1e1205247
|
|||
1007b6744d
|
|||
f0701fe9e7
|
|||
b462ae3c7e
|
|||
8ad2a1342a
|
|||
99007047eb
|
|||
|
d8b9430120 | ||
|
4f4d598eb6 | ||
|
2f065f4917 | ||
3e8d6d9557
|
|||
|
6c8d033629 | ||
|
3dfe314836 | ||
|
ca546e5ba6 | ||
|
6b01273580 | ||
625d607ef0
|
|||
|
14869f4a83 | ||
|
47b880e737 | ||
efd60b89e4
|
|||
bbd92bfa8b | |||
65227d12ab | |||
d2fbaa4af6
|
|||
|
2ae5de715d | ||
8d75c055c0 | |||
83eb22e627 | |||
30cd5c4af8 | |||
faa0e236b6 | |||
|
12a009b7db | ||
|
373c186075 | ||
|
9821f78c87 | ||
|
9fb66b6755 | ||
50b3312f49
|
|||
6127e4cf37 | |||
|
8636bcfb0c | ||
aa06318c07
|
|||
7695efa5d3
|
|||
c38f134254
|
|||
99c644bffb
|
|||
4602cbe187
|
|||
0a1f3d1e26
|
|||
|
b4c2f84ea0 | ||
|
c9f83c396f | ||
|
5a77578699 | ||
|
0fbe762391 | ||
|
85d22b4736 | ||
4cf45f75bc
|
|||
4bf1447673
|
|||
|
d1286469c3 | ||
27b684c550
|
|||
|
de0cbf5a8b | ||
47b6a82be8
|
|||
|
e24ebcfc0b | ||
af5b4ceb66
|
|||
36558c0c7c
|
|||
2d9d1d1eb3
|
|||
c335ec2b7e | |||
1a6e52d52a
|
|||
2c8e62fef0
|
|||
6042e71c2d
|
|||
|
ccc955d0cb
|
||
|
8f64897bf6
|
||
|
69052504cc
|
||
|
a791970935
|
||
|
1c6baca895
|
||
|
06706ff640
|
||
|
f4bc9d371b
|
||
|
c1aff353b3
|
||
|
074cbef775
|
||
|
c7cb4b3fcb
|
||
|
3be8e2170b
|
||
|
ee712b65e1
|
||
|
7ac5c940be
|
||
|
ba29fc51d8
|
||
|
b168809158
|
||
|
2eb3b3ddc1
|
||
|
0b5ce02c40
|
||
|
e9fde172be
|
||
|
efaa471ce2
|
@@ -7,4 +7,8 @@ indent_style = space
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{cc,cpp,hpp,h}]
|
||||
insert_final_newline = true
|
||||
insert_final_newline = true
|
||||
|
||||
[makefile]
|
||||
indent_size = 4
|
||||
indent_style = tab
|
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1 +1,7 @@
|
||||
.vscode
|
||||
.vscode/
|
||||
.idea/
|
||||
build/
|
||||
docs/
|
||||
*.exe
|
||||
*.txt
|
||||
chess
|
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// Created by hamac on 18.12.2024.
|
||||
//
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include "ChessUtils.h"
|
||||
|
||||
std::vector<std::string> ChessUtils::split(const std::string &s, char delim) {
|
||||
std::vector<std::string> elems;
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
while (std::getline(ss, item, delim)) {
|
||||
elems.push_back(item);
|
||||
}
|
||||
return elems;
|
||||
}
|
||||
//a4
|
||||
std::pair<int, int> ChessUtils::convertPosition(const std::string &pos) {
|
||||
int y = pos[pos.size()-2] - 'a';
|
||||
int x = ChessUtils::fileConvertion.find((pos[pos.size()-1] - '0')-1)->second;
|
||||
return std::make_pair(x, y);
|
||||
}
|
34
ChessUtils.h
34
ChessUtils.h
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// Created by hamac on 20.12.2024.
|
||||
//
|
||||
|
||||
#ifndef CHESSUTILS_H
|
||||
#define CHESSUTILS_H
|
||||
|
||||
//
|
||||
// Created by hamac on 18.12.2024.
|
||||
//
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
class ChessUtils {
|
||||
public:
|
||||
inline static const std::unordered_map<int, int> fileConvertion = {
|
||||
{0, 7},
|
||||
{1, 6},
|
||||
{2, 5},
|
||||
{3, 4},
|
||||
{4, 3},
|
||||
{5, 2},
|
||||
{6, 1},
|
||||
{7, 0}
|
||||
};
|
||||
static std::vector<std::string> split(const std::string &s, char delim);
|
||||
|
||||
static std::pair<int, int> convertPosition(const std::string &pos);
|
||||
};
|
||||
|
||||
#endif //CHESSUTILS_H
|
268
Chessboard.cpp
268
Chessboard.cpp
@@ -1,268 +0,0 @@
|
||||
//
|
||||
// Created by hamac on 18.12.2024.
|
||||
//
|
||||
|
||||
#include "Chessboard.h"
|
||||
#include "Chesspiece.cpp"
|
||||
#include "ChessUtils.cpp"
|
||||
#include "Pawn.h"
|
||||
#include <iostream>
|
||||
|
||||
std::string Chessboard::getChessIcon(char identifier) {
|
||||
switch (identifier) {
|
||||
case 'x': return blackSquare;
|
||||
case 'r': return blackRook;
|
||||
case 'n': return blackKnight;
|
||||
case 'b': return blackBischop;
|
||||
case 'q': return blackQueen;
|
||||
case 'k': return blackKing;
|
||||
case 'p': return blackPawn;
|
||||
case 'w': return whiteSquare;
|
||||
case 'R': return whiteRook;
|
||||
case 'N': return whiteKnight;
|
||||
case 'B': return whiteBischop;
|
||||
case 'Q': return whiteQueen;
|
||||
case 'K': return whiteKing;
|
||||
case 'P': return whitePawn;
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
void Chessboard::generateTopLine() {
|
||||
this->display += " " + horizontal + " " + topLeft;
|
||||
for (int col = 0; col < boardSize; ++col) {
|
||||
this->display += horizontal + horizontal + horizontal;
|
||||
if (col < boardSize - 1) this->display += topIntersection;
|
||||
}
|
||||
this->display += topRight + "\n";
|
||||
}
|
||||
|
||||
void Chessboard::generatePlayingField(const std::vector<std::vector<char>>& chessboard) {
|
||||
char desc = 56;
|
||||
for (int row = 0; row < boardSize; ++row) {
|
||||
this->display += " ";
|
||||
this->display += desc--;
|
||||
this->display += " " + vertical;
|
||||
for (int col = 0; col < boardSize; ++col) {
|
||||
this->display += " " + Chessboard::getChessIcon(chessboard[row][col]) + " " + vertical;
|
||||
}
|
||||
this->display += "\n";
|
||||
|
||||
// Horizontale Trennlinie (außer nach der letzten Zeile)
|
||||
if (row < boardSize - 1) {
|
||||
this->display += " " + horizontal + " " + vertical;
|
||||
for (int col = 0; col < boardSize; ++col) {
|
||||
this->display += horizontal + horizontal + horizontal;
|
||||
if (col < boardSize - 1) this->display += middleIntersection;
|
||||
}
|
||||
this->display += vertical + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Chessboard::generateBottomLine() {
|
||||
char desc = 65;
|
||||
this->display += " " + horizontal + " " + bottomLeft;
|
||||
for (int col = 0; col < boardSize; ++col) {
|
||||
this->display += horizontal + horizontal + horizontal;
|
||||
if (col < boardSize - 1) this->display += bottomIntersection;
|
||||
}
|
||||
this->display += bottomRight + "\n";
|
||||
this->display += " " + vertical;
|
||||
for (int col = 0; col < boardSize; ++col) {
|
||||
this->display += " ";
|
||||
this->display += (desc++);
|
||||
this->display += " " + vertical;
|
||||
}
|
||||
}
|
||||
|
||||
/* methods */
|
||||
bool Chessboard::getTurnOrder() { return this->turnOrder; }
|
||||
void Chessboard::setTurnOrder(bool turnOrder) { this->turnOrder = turnOrder; }
|
||||
|
||||
std::vector<std::vector<char>> Chessboard::getBoard() { return this->currentBoard; }
|
||||
void Chessboard::setBoard(std::vector<std::vector<char>> board) { this->currentBoard = board; }
|
||||
|
||||
std::vector<std::vector<char>> Chessboard::getStartBoard() { return this->startBoard; }
|
||||
|
||||
std::vector<std::vector<char>> Chessboard::getEmptyBoard() { return this->emptyBoard; }
|
||||
|
||||
void Chessboard::draw () {
|
||||
Chessboard::draw(getStartBoard());
|
||||
}
|
||||
|
||||
void Chessboard::draw (const std::vector<std::vector<char>>& chessboard) {
|
||||
// Obere Rahmenlinie
|
||||
Chessboard::generateTopLine();
|
||||
|
||||
// Schachbrett mit vertikalen Linien
|
||||
Chessboard::generatePlayingField(chessboard);
|
||||
|
||||
// Untere Rahmenlinie
|
||||
Chessboard::generateBottomLine();
|
||||
|
||||
Chessboard::setBoard(chessboard);
|
||||
|
||||
std::cout << this->display << std::endl;
|
||||
}
|
||||
|
||||
// ToDo:
|
||||
// Methode um mögliche Züge anzuzeigen
|
||||
// Rochade
|
||||
// Mate
|
||||
// Schachmate
|
||||
// En Passond
|
||||
// Restlichen moves
|
||||
// Angabe mit Menü Optionen als Zahlen
|
||||
// Figuren wählen mit Schachnotation
|
||||
|
||||
void Chessboard::init() {
|
||||
Chessboard::initPieces();
|
||||
}
|
||||
|
||||
void Chessboard::initPieces() {
|
||||
/*playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(6,0)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(6,1)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(6,2)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(6,3)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(6,4)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(6,5)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(6,6)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(6,7)));
|
||||
playingPieces['w'].push_back(std::make_unique<Rook>('w', std::make_pair(7,0)));
|
||||
playingPieces['w'].push_back(std::make_unique<Knight>('w', std::make_pair(7,1)));
|
||||
playingPieces['w'].push_back(std::make_unique<Bischop>('w', std::make_pair(7,2)));
|
||||
playingPieces['w'].push_back(std::make_unique<Queen>('w', std::make_pair(7,3)));
|
||||
playingPieces['w'].push_back(std::make_unique<King>('w', std::make_pair(7,4)));
|
||||
playingPieces['w'].push_back(std::make_unique<Bischop>('w', std::make_pair(7,5)));
|
||||
playingPieces['w'].push_back(std::make_unique<Knight>('w', std::make_pair(7,6)));
|
||||
playingPieces['w'].push_back(std::make_unique<Rooke>('w', std::make_pair(7,7)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(1,0)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(1,1)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(1,2)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(1,3)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(1,4)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(1,5)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(1,6)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(1,7)));
|
||||
playingPieces['b'].push_back(std::make_unique<Rook>('b', std::make_pair(0,0)));
|
||||
playingPieces['b'].push_back(std::make_unique<Knight>('b', std::make_pair(0,1)));
|
||||
playingPieces['b'].push_back(std::make_unique<Bischop>('b', std::make_pair(0,2)));
|
||||
playingPieces['b'].push_back(std::make_unique<Queen>('b', std::make_pair(0,3)));
|
||||
playingPieces['b'].push_back(std::make_unique<King>('b', std::make_pair(0,4)));
|
||||
playingPieces['b'].push_back(std::make_unique<Bischop>('b', std::make_pair(0,5)));
|
||||
playingPieces['b'].push_back(std::make_unique<Knight>('b', std::make_pair(0,6)));
|
||||
playingPieces['b'].push_back(std::make_unique<Rook>('b', std::make_pair(0,7)));*/
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(6,0)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(6,1)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(6,2)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(6,3)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(6,4)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(6,5)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(6,6)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(6,7)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(7,0)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(7,1)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(7,2)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(7,3)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(7,4)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(7,5)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(7,6)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(7,7)));
|
||||
playingPieces['w'].push_back(std::make_unique<Pawn>('w', std::make_pair(1,0)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(1,1)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(1,2)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(1,3)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(1,4)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(1,5)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(1,6)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(1,7)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(0,0)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(0,1)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(0,2)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(0,3)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(0,4)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(0,5)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(0,6)));
|
||||
playingPieces['b'].push_back(std::make_unique<Pawn>('b', std::make_pair(0,7)));
|
||||
}
|
||||
|
||||
void Chessboard::move() {
|
||||
|
||||
}
|
||||
|
||||
char Chessboard::getCorrectPiece(char pieceIdentifier) {
|
||||
bool isWhite = Chessboard::getTurnOrder();
|
||||
if (!isWhite) return pieceIdentifier + 32;
|
||||
return pieceIdentifier;
|
||||
}
|
||||
|
||||
//
|
||||
void Chessboard::move(std::string move) {
|
||||
// ToDo:
|
||||
// add move to history queue
|
||||
|
||||
std::vector<std::string> splitMove;
|
||||
|
||||
if (move.find('#') != std::string::npos) {
|
||||
// Schachmate
|
||||
// Finish game
|
||||
} else if (move.rfind("0-0", 0) == 0) { // Kleine Rochade
|
||||
// Check for Rochade
|
||||
} else if (move.rfind("0-0-0", 0) == 0) {
|
||||
// Große Rochade
|
||||
}
|
||||
|
||||
// Standard move
|
||||
if (move.find('-')) {
|
||||
splitMove = ChessUtils::split(move, '-');
|
||||
} else if (move.find('x')) {
|
||||
splitMove = ChessUtils::split(move, 'x');
|
||||
}
|
||||
|
||||
std::pair oldCoords = ChessUtils::convertPosition(splitMove[0]);
|
||||
std::pair newCoords = ChessUtils::convertPosition(splitMove[1]);
|
||||
|
||||
std::vector<std::vector<char>> board = getBoard();
|
||||
|
||||
board[oldCoords.first][oldCoords.second] = getEmptyBoard()[oldCoords.first][oldCoords.second];
|
||||
board[newCoords.first][newCoords.second] = getCorrectPiece(splitMove[0][0]);
|
||||
|
||||
//e4-e5
|
||||
|
||||
Chessboard::draw(board);
|
||||
|
||||
// Notation
|
||||
// Start with current position - dash - new position
|
||||
// eg.: b1-c3
|
||||
// Letter first than number
|
||||
// eg.: a5
|
||||
// Pawn: e4 or p e4 (pawn)
|
||||
// R for rook
|
||||
// N for Knight
|
||||
// K for King
|
||||
// B for Bischop
|
||||
// Q for Queen
|
||||
// Special:
|
||||
// 0-0 short castle
|
||||
// 0-0-0 long castle
|
||||
// en passant: write square where pawn lands
|
||||
// Umwandlung: g8=R,Q,N,B (Wo gehst du hin = Was wirst du)
|
||||
|
||||
// capture: x
|
||||
// check: +
|
||||
// checkmate: #
|
||||
// draw/stalemate: 1/2-1/2
|
||||
|
||||
}
|
||||
|
||||
// This method saves the current board state
|
||||
void Chessboard::saveBoard(Chessboard& chessboard) {
|
||||
|
||||
}
|
||||
|
||||
// This method loads a save state
|
||||
void Chessboard::loadBoard(int saveState) {
|
||||
// readJSONFile
|
||||
//
|
||||
}
|
143
Chessboard.h
143
Chessboard.h
@@ -1,143 +0,0 @@
|
||||
//
|
||||
// Created by hamac on 20.12.2024.
|
||||
//
|
||||
|
||||
#ifndef CHESSBOARD_H
|
||||
#define CHESSBOARD_H
|
||||
|
||||
#include "Chesspiece.h"
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
class Chessboard {
|
||||
private:
|
||||
/* Constants */
|
||||
// Chars to display board
|
||||
// Corners
|
||||
const std::string topLeft = "┌";
|
||||
const std::string topRight = "┐";
|
||||
const std::string bottomLeft = "└";
|
||||
const std::string bottomRight = "┘";
|
||||
|
||||
// Line chars
|
||||
const std::string horizontal = "─";
|
||||
const std::string vertical = "│";
|
||||
const std::string topIntersection = "┬";
|
||||
const std::string bottomIntersection = "┴";
|
||||
const std::string middleIntersection = "┼";
|
||||
|
||||
// White pieces
|
||||
const std::string whiteSquare = "□";
|
||||
const std::string whiteKing = "♔";
|
||||
const std::string whiteQueen = "♕";
|
||||
const std::string whiteRook = "♖";
|
||||
const std::string whiteBischop = "♗";
|
||||
const std::string whiteKnight = "♘";
|
||||
const std::string whitePawn = "♙";
|
||||
|
||||
// Black pieces
|
||||
const std::string blackSquare = "■";
|
||||
const std::string blackKing = "♚";
|
||||
const std::string blackQueen = "♛";
|
||||
const std::string blackRook = "♜";
|
||||
const std::string blackBischop = "♝";
|
||||
const std::string blackKnight = "♞";
|
||||
const std::string blackPawn = "♟";
|
||||
|
||||
/* class fields */
|
||||
std::vector<std::vector<char>> currentBoard;
|
||||
|
||||
std::unordered_map<char, std::vector<std::unique_ptr<Chesspiece>>> playingPieces;
|
||||
|
||||
// Starting formatting
|
||||
std::vector<std::vector<char>> startBoard = {
|
||||
{'r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'},
|
||||
{'p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'},
|
||||
{'w', 'x', 'w', 'x' ,'w', 'x', 'w', 'x'},
|
||||
{'x', 'w', 'x', 'w', 'x', 'w', 'x', 'w'},
|
||||
{'w', 'x', 'w', 'x' ,'w', 'x', 'w', 'x'},
|
||||
{'x', 'w', 'x', 'w', 'x', 'w', 'x', 'w'},
|
||||
{'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'},
|
||||
{'R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R'},
|
||||
};
|
||||
|
||||
// ToDo: Mathematisch sicherlich weg rationalisierbar
|
||||
std::vector<std::vector<char>> emptyBoard = {
|
||||
{'w', 'x', 'w', 'x' ,'w', 'x', 'w', 'x'},
|
||||
{'x', 'w', 'x', 'w', 'x', 'w', 'x', 'w'},
|
||||
{'w', 'x', 'w', 'x' ,'w', 'x', 'w', 'x'},
|
||||
{'x', 'w', 'x', 'w', 'x', 'w', 'x', 'w'},
|
||||
{'w', 'x', 'w', 'x' ,'w', 'x', 'w', 'x'},
|
||||
{'x', 'w', 'x', 'w', 'x', 'w', 'x', 'w'},
|
||||
{'w', 'x', 'w', 'x' ,'w', 'x', 'w', 'x'},
|
||||
{'x', 'w', 'x', 'w', 'x', 'w', 'x', 'w'}
|
||||
};
|
||||
|
||||
// Saves turn order; true = white and false = black
|
||||
bool turnOrder = true;
|
||||
|
||||
// ToDo: Max Size definieren?
|
||||
std::queue<std::string> queue;
|
||||
|
||||
/* methods */
|
||||
// Method returns unicode for chess icon depending on the identifier
|
||||
std::string getChessIcon(char identifier);
|
||||
|
||||
void generateTopLine();
|
||||
|
||||
void generatePlayingField(const std::vector<std::vector<char>>& chessboard);
|
||||
|
||||
void generateBottomLine();
|
||||
|
||||
public:
|
||||
/* fields */
|
||||
std::string display;
|
||||
int boardSize = 8;
|
||||
|
||||
/* methods */
|
||||
bool getTurnOrder();
|
||||
void setTurnOrder(bool turnOrder);
|
||||
|
||||
std::vector<std::vector<char>> getBoard();
|
||||
void setBoard(std::vector<std::vector<char>> board);
|
||||
|
||||
std::vector<std::vector<char>> getStartBoard();
|
||||
|
||||
std::vector<std::vector<char>> getEmptyBoard();
|
||||
|
||||
void draw ();
|
||||
|
||||
void draw (const std::vector<std::vector<char>>& chessboard);
|
||||
|
||||
// ToDo:
|
||||
// Methode um mögliche Züge anzuzeigen
|
||||
// Rochade
|
||||
// Mate
|
||||
// Schachmate
|
||||
// En Passond
|
||||
// Restlichen moves
|
||||
// Angabe mit Menü Optionen als Zahlen
|
||||
// Figuren wählen mit Schachnotation
|
||||
|
||||
void init();
|
||||
|
||||
void initPieces();
|
||||
|
||||
void move();
|
||||
|
||||
char getCorrectPiece(char pieceIdentifier);
|
||||
|
||||
//
|
||||
void move(std::string move);
|
||||
|
||||
// This method saves the current board state
|
||||
void saveBoard(Chessboard& chessboard);
|
||||
|
||||
// This method loads a save state
|
||||
void loadBoard(int saveState);
|
||||
};
|
||||
|
||||
|
||||
#endif //CHESSBOARD_H
|
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// Created by hamac on 18.12.2024.
|
||||
//
|
||||
|
||||
#include "Chesspiece.h"
|
||||
|
||||
Chesspiece::Chesspiece(char color, std::pair<int, int> position)
|
||||
: color(color), position(position) {}
|
||||
|
||||
std::pair<int, int> Chesspiece::getPosition() { return this->position; };
|
||||
void Chesspiece::setPosition(std::pair<int, int> position) {
|
||||
this->position = position;
|
||||
Chesspiece::calcAvaibleMoves(); // Brett -> Auswahl Figuren -> Züge gewählte
|
||||
}
|
||||
|
||||
char Chesspiece::getColor() { return color; }
|
||||
void Chesspiece::setColor(char color) { color = color; }
|
||||
|
||||
std::vector<std::string> Chesspiece::getAvaibleMoves() { return avaibleMoves; }
|
||||
|
||||
bool Chesspiece::checkNotation(std::string notation) {
|
||||
std::regex pattern("R^([KQRNBP]?)([a-h][1-8])[-x]([a-h][1-8])([+#]?)$");
|
||||
std::smatch match;
|
||||
return (std::regex_search(notation, match, pattern) ? true : false);
|
||||
}
|
46
Chesspiece.h
46
Chesspiece.h
@@ -1,46 +0,0 @@
|
||||
//
|
||||
// Created by hamac on 19.12.2024.
|
||||
//
|
||||
|
||||
#ifndef CHESSPIECE_H
|
||||
#define CHESSPIECE_H
|
||||
|
||||
//
|
||||
// Created by hamac on 18.12.2024.
|
||||
//
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <regex>
|
||||
|
||||
class Chesspiece {
|
||||
private:
|
||||
/* fields */
|
||||
// ToDo: von char auf enum Color {W, B} umstellen
|
||||
char color;
|
||||
char identifier; // P,K,...
|
||||
std::pair<int, int> position;
|
||||
// std::vector<std::pair<int, int>>
|
||||
std::vector<std::string> avaibleMoves;
|
||||
|
||||
/* methods */
|
||||
virtual std::vector<std::string> calcAvaibleMoves() = 0;
|
||||
|
||||
public:
|
||||
Chesspiece(char color, std::pair<int, int> position);
|
||||
|
||||
virtual ~Chesspiece() = default;
|
||||
|
||||
std::pair<int, int> getPosition();
|
||||
void setPosition(std::pair<int, int> position);
|
||||
|
||||
char getColor();
|
||||
void setColor(char color);
|
||||
|
||||
std::vector<std::string> getAvaibleMoves();
|
||||
|
||||
bool checkNotation(std::string notation);
|
||||
};
|
||||
|
||||
|
||||
#endif //CHESSPIECE_H
|
96
MoveList.cpp
96
MoveList.cpp
@@ -1,96 +0,0 @@
|
||||
#include "MoveList.h"
|
||||
|
||||
MoveList::MoveList() {
|
||||
this->head->next = nullptr;
|
||||
}
|
||||
|
||||
MoveList::~MoveList() {
|
||||
Node *current = head;
|
||||
std::cout << "Destructor: " << std::endl;
|
||||
|
||||
while (current != nullptr) {
|
||||
Node *nextNode = current->next;
|
||||
std::cout << " delete on " << current->data.first << " " << current->data.second << std::endl;
|
||||
delete current;
|
||||
current = nextNode;
|
||||
}
|
||||
}
|
||||
|
||||
MoveList &display() {
|
||||
Node *currentNode = this->head;
|
||||
|
||||
while (currentNode->next != nullptr) {
|
||||
currentNode = currentNode->next;
|
||||
std::cout << "[" << currentNode->data.first << " " << currentNode->data.second << " | " << currentNode->next << ((currentNode->next != nullptr) ? "] ---> " : "] ");
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
MoveList &append(const std::pair<int, int> &data) {
|
||||
if(this->head->next == nullptr) {
|
||||
Node *node = new Node;
|
||||
node->data = data;
|
||||
this->head->next = node;
|
||||
} else {
|
||||
Node *newNode = new Node;
|
||||
|
||||
newNode->data = data;
|
||||
newNode->next = nullptr;
|
||||
|
||||
Node *currentNode = this->head;
|
||||
|
||||
while (currentNode->next != nullptr) {
|
||||
currentNode = currentNode->next;
|
||||
}
|
||||
|
||||
currentNode->next = newNode;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
MoveList &insert(const std::pair<int, int> &data) {
|
||||
Node *newNode = new Node;
|
||||
newNode->data = data;
|
||||
newNode->next = this->head->next;
|
||||
this->head->next = newNode;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Node* MoveList::search(const std::pair<int, int> &data) const {
|
||||
Node *currentNode = this->head;
|
||||
|
||||
do {
|
||||
if (currentNode->data == data) {
|
||||
return currentNode;
|
||||
}
|
||||
} while(currentNode = currentNode->next);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MoveList &remove(const std::pair<int, int> &data) {
|
||||
Node *currentNode = this->head;
|
||||
|
||||
do {
|
||||
if (currentNode->next->data == data) {
|
||||
Node *temp = currentNode->next;
|
||||
currentNode->next = currentNode->next->next;
|
||||
delete temp;
|
||||
break;
|
||||
}
|
||||
} while((currentNode = currentNode->next) && (currentNode->next != nullptr));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void MoveList::removeTail(const std::pair<int, int> &data) {
|
||||
Node *found = search(data);
|
||||
|
||||
if(found != nullptr) {
|
||||
found->next = nullptr;
|
||||
}
|
||||
}
|
38
MoveList.h
38
MoveList.h
@@ -1,38 +0,0 @@
|
||||
//
|
||||
// Created by hamac on 20.12.2024.
|
||||
//
|
||||
|
||||
#ifndef MOVELIST_H
|
||||
#define MOVELIST_H
|
||||
|
||||
#include <iostream>
|
||||
|
||||
struct Node {
|
||||
std::pair<int, int> data;
|
||||
Node *next;
|
||||
};
|
||||
|
||||
|
||||
class MoveList {
|
||||
private:
|
||||
Node *head;
|
||||
|
||||
public:
|
||||
MoveList();
|
||||
~MoveList();
|
||||
|
||||
MoveList &display();
|
||||
|
||||
MoveList &append(const std::pair<int, int> &data);
|
||||
|
||||
MoveList &insert(const std::pair<int, int> &data);
|
||||
|
||||
Node* search(const std::pair<int, int> &data) const;
|
||||
|
||||
MoveList &remove(const std::pair<int, int> &data);
|
||||
|
||||
void removeTail(const std::pair<int, int> &data);
|
||||
};
|
||||
|
||||
|
||||
#endif //MOVELIST_H
|
60
OldCode.txt
60
OldCode.txt
@@ -1,60 +0,0 @@
|
||||
void firstVersion() {
|
||||
const std::string white = "\u2B1C";
|
||||
const std::string black = "\u2B1B";
|
||||
|
||||
std::string brett[8][8];
|
||||
|
||||
std::string field;
|
||||
for (int y = 0; y < 8; y++) {
|
||||
for (int x = 0; x < 8; x++) {
|
||||
if (y % 2 == 0) {
|
||||
field = (x % 2 == 0) ? "\u2B1C" : "\u2B1B";
|
||||
}
|
||||
else {
|
||||
field = (x % 2 == 0) ? "⬛" : "⬜";
|
||||
}
|
||||
std::cout << field;
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void secondVersion() {
|
||||
//wchar_t t = 0x25FF;
|
||||
|
||||
// Horizontale Linie u2500
|
||||
// Vertikale Linie u2502
|
||||
// Top Right Corner u250C
|
||||
// Top Left Corner u2510
|
||||
// Bottom Right Corner u2514
|
||||
// Bottom Left Corner u2518
|
||||
|
||||
std::string topRightCorner = "\u2554";
|
||||
std::string topLeftCorner = "\u2557";
|
||||
std::string bottomLeftCorner = "\u255A";
|
||||
std::string bottomRightCorner = "\u255D";
|
||||
std::string horizontalLine = "\u2550\u2550";
|
||||
std::string verticalLine = "\u2551";
|
||||
std::string crossSuc = "\u256C";
|
||||
std::string leftSide = "\u2560";
|
||||
std::string rightSide = "\u2563";
|
||||
std::string topSide = "\u2566";
|
||||
std::string bottomSide = "\u2569";
|
||||
|
||||
std::string firstLine = "\u2554\u2550\u2566";
|
||||
std::string line;
|
||||
|
||||
for (int row = 0; row < 9; ++row) {
|
||||
for (int col = 0; col < 8; ++col) {
|
||||
if (row == 0 && col > 0) line += topSide + horizontalLine;
|
||||
if (row == 8 && col > 0) line += bottomSide + horizontalLine;
|
||||
if (col == 0 && row < 8 && row > 0) line += leftSide + horizontalLine;
|
||||
if (row > 0 && row < 8 && col > 0) line += crossSuc + horizontalLine;
|
||||
if (col == 7 && row < 8 && row > 0) line += rightSide;
|
||||
if (row == 0 && col == 0) line += topRightCorner + horizontalLine;
|
||||
if (row == 8 && col == 0) line += bottomLeftCorner + horizontalLine;
|
||||
if (row == 0 && col == 7) line += topLeftCorner + "\n" + verticalLine;
|
||||
if (row == 8 && col == 7) line += bottomRightCorner;
|
||||
}
|
||||
line += "\n";
|
||||
}
|
26
Pawn.cpp
26
Pawn.cpp
@@ -1,26 +0,0 @@
|
||||
//
|
||||
// Created by hamac on 19.12.2024.
|
||||
//
|
||||
|
||||
#include "Chesspiece.h"
|
||||
#include "Pawn.h"
|
||||
#include <iostream>
|
||||
|
||||
/* methods */
|
||||
std::vector<std::string> Pawn::calcAvaibleMoves() {
|
||||
std::pair<int, int> pos = Chesspiece::getPosition();
|
||||
std::vector<std::string> moves;
|
||||
|
||||
//current
|
||||
|
||||
if (this->firstMove) {
|
||||
//vector[8] => MoveList(1-1;2-2+)
|
||||
//moves.
|
||||
}
|
||||
|
||||
|
||||
std::cout << "Hello World!\n" << std::endl;
|
||||
return moves;
|
||||
}
|
||||
|
||||
Pawn::Pawn(char color, std::pair<int, int> position): Chesspiece(color, position) {}
|
28
Pawn.h
28
Pawn.h
@@ -1,28 +0,0 @@
|
||||
//
|
||||
// Created by hamac on 20.12.2024.
|
||||
//
|
||||
|
||||
#ifndef PAWN_H
|
||||
#define PAWN_H
|
||||
|
||||
//
|
||||
// Created by hamac on 19.12.2024.
|
||||
//
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "Chesspiece.h"
|
||||
|
||||
class Pawn: public Chesspiece {
|
||||
private:
|
||||
/* fields */
|
||||
bool firstMove = true;
|
||||
bool movedTwoFields = false;
|
||||
|
||||
/* methods */
|
||||
std::vector<std::string> calcAvaibleMoves() override ;
|
||||
public:
|
||||
Pawn(char color, std::pair<int, int> position);
|
||||
};
|
||||
|
||||
#endif //PAWN_H
|
134
README.md
134
README.md
@@ -1,83 +1,69 @@
|
||||
# c/c++ Abschlussprojekt - Schach
|
||||
# TurboSchach
|
||||
|
||||
## Notwendige Umsetzungen
|
||||
## Kompilieren / Ausführen
|
||||
Für die Ausführung von TurboSchach sollte die Konsole / das Terminal im Vollbildmodus geöffnet sein. Durch ein zu kleines Terminal kann es zu Verschiebungen in der Benutzeroberfläche kommen. Um TurboSchach kompilieren zu können wird mindestens C++ 17 benötigt.
|
||||
|
||||
1. Klasse für Spielbrett
|
||||
2. Oberklasse für Spielfiguren
|
||||
1. Bauer
|
||||
2. Dame
|
||||
3. König
|
||||
4. Läufer
|
||||
5. Turm
|
||||
6. Springer
|
||||
3. Fancy User Interface
|
||||
4. Speicherung des Spielbretts
|
||||
5. Unentschieden anbieten
|
||||
6. Aufgegeben
|
||||
### Linux und MacOS
|
||||
|
||||
### Spielbrett
|
||||
```
|
||||
# build
|
||||
make
|
||||
|
||||
- 8x8 Matrix
|
||||
- ANSI Linien checken
|
||||
- Unterscheidung von schwarzen und weißen Feldern
|
||||
- UTF-8 Spielfiguren
|
||||
- draw() Funktion
|
||||
- movement
|
||||
- Schachnotation
|
||||
- oder Klickbar?
|
||||
- Beschriftung des Spielbretts
|
||||
# run
|
||||
make run
|
||||
```
|
||||
|
||||
### Spielfiguren
|
||||
### Microsoft Windows
|
||||
|
||||
- Interface für Implementierung?
|
||||
- Default Felder:
|
||||
- Art der Figure
|
||||
- UTF-8 Code
|
||||
- Schlagen: Ich sehe dich nach movement Regeln -> Du stirbst
|
||||
- Bauer:
|
||||
- Movement: 1 Felder, außer bei Start 1-2 Felder
|
||||
- Schlagen: 1 Feld vor Diagonal
|
||||
- Spezialmove:
|
||||
- Beförderung
|
||||
- En Passent - Wie?
|
||||
- Dame:
|
||||
- Movement: Fuck it, i do what i want
|
||||
- König:
|
||||
- Movement: Körperklaus. Ich kann nur ein Feld nach überall
|
||||
- Spezialmove: Castlen
|
||||
- Läufer:
|
||||
- Movement: Die Diagnal ist mir
|
||||
- Turm:
|
||||
- Movement: Vertikal und Horizontal
|
||||
- Springer:
|
||||
- Movement: L-Bewegung (2 nach vorn + 1 nach links oder rechts); Krake
|
||||
- Spezial Moves:
|
||||
- Rochade -> Kurz/Lang
|
||||
- En Passant
|
||||
- Stalemate
|
||||
- Bauer: 2 Felder
|
||||
- Umwandeln
|
||||
```
|
||||
# build
|
||||
g++ -std=c++17 -o chess.exe main.cpp ChessPieces/*.cpp Chessboard/*.cpp Controller/*.cpp Player/*.cpp Visualizer/*.cpp
|
||||
|
||||
## Optional wenn Lust und Zeit?
|
||||
# run
|
||||
./chess.exe
|
||||
|
||||
1. Bedienung per Maus
|
||||
2. Multiplayer
|
||||
3. Historie der Spielzüge
|
||||
4. Gravejard der geschlagenen Figuren
|
||||
5. König platzieren -> Anzeigen wer gewinnt
|
||||
# build and run (PowerShell)
|
||||
.\run
|
||||
```
|
||||
|
||||
# Gedankensammlung
|
||||
- Initialisierung:
|
||||
- Brett zeichnen
|
||||
- Figuren mit Zugmöglichkeiten ermitteln
|
||||
- Züge kalkulieren
|
||||
- Objekt mit allen Figuren erstellen -> alle Figuren initialisieren
|
||||
- Kalkulation der Züge
|
||||
- Anhand der aktuellen Position
|
||||
- Mit einbeziehen, dass Zug König nicht in Mate versetzt (verboten)
|
||||
- Sonder Züge, wie En passant, Rochade (Lang, Kurz), Bauern zwei Feld, beachten
|
||||
- Prüfen, ob andere Figuren im Weg stehen
|
||||
- Array von Linked Lists
|
||||
- Array Index steht für Richtung von Bewegungen (diagonalen, L, Gerade)
|
||||
- alles droppen, was nach pos mit Figur kommt
|
||||
-
|
||||
## Algebraische Notation
|
||||
Um die Figuren auf dem Schachbrett zu bewegen, wird die kurze algebraische Notation nach [FIDE](https://handbook.fide.com/chapter/E012023) (siehe Appendix C) unterstützt. Es werden die Schachfiguren sowohl in deutscher als auch in englischer Sprache unterstützt.
|
||||
|
||||
| Schachfigur | Deutsch | Englisch |
|
||||
| ----------- | ------- | -------- |
|
||||
| Bauer (Pawn) | B | P |
|
||||
| Springer (Knight) | S | N |
|
||||
| Läufer (Bishop) | L | B |
|
||||
| Turm (Rook) | T | R |
|
||||
| Dame (Queen) | D | Q |
|
||||
| König (King) | K | K |
|
||||
|
||||
Bei der kurzen Notation wird immer nur das Zielfeld eines Spielzuges genannt. Sollte der Ursprung nicht eindeutig sein kann auch die Spalte oder Zeile der zu bewegenden Figur genannt werden. So gibt der Spielzug `dxe5` an dass der Bauer in der Spalte D auf das Feld E5 zieht. Das `x` gibt an dass dabei eine Spielfigur des Gegeners geschlagen wird. Dies muss aber nicht zwingend angegeben werden.
|
||||
Der Bauer wird in den Spielzügen nicht benannt. Der Spielzug `b4` ist daher der Zug eines Bauern auf das Feld B4. Alle anderen Schachfiguren werden im Zug mit ihrem entsprechenden Buchstaben genannt. So gibt der Spielzug `Bc4` an dass der Läufer auf das Feld C4 gezogen wird.
|
||||
|
||||
### Beispiele
|
||||
|
||||
- `(=)` Draw: Das Draw wird dann aktiv wenn dieses im nächsten Spielzug vom Gegner bestätigt wird.
|
||||
- `b4` Der Bauer zieht auf das Feld B4.
|
||||
- `Bc4` Der Läufer zieht auf das Feld C4.
|
||||
- `0-0` Kleine Rochade (Rochade am Königsflügel)
|
||||
- `0-0-0` Große Rochade (Rochade am Damenflügel)
|
||||
- `d8Q` Umwandlung: Der Bauer zieht auf D8 und wandelt sich zur Dame.
|
||||
|
||||
## PGN (Portable Game Notation)
|
||||
TurboSchach unterstützt das Datenformat [PGN (Portable Game Notation)](https://www.saremba.de/chessgml/standards/pgn/pgn-complete.htm), welches zur Speicherung von Schachpartien verwendet wird. Auf der Website https://www.chessgames.com/ werden beispielsweise viele Schachpartien von offiziellen Turnieren im PGN-Format zur Verfügung gestellt. Diese Dateien können in TurboSchach importiert werden. Über den Menüpunkt 2 direkt nach dem Start der Anwendung ist dieser Import möglich.
|
||||
|
||||
Diese PGN-Dateien wurden auch zum Testen der Anwendung verwendet. Nachfolgend einige Schachspiele mit bestimmten Eigenschaften:
|
||||
|
||||
- [Jack Rudd vs Martin Simons](https://www.chessgames.com/perl/chessgame?gid=1588906) vom 09.08.2000 - Schachmatt
|
||||
- [Tim Jaksland vs Line Jin Jorgensen](https://www.chessgames.com/perl/chessgame?gid=1482551) vom 22.01.2008 - Schachmatt
|
||||
- [Gabriel Sargissian vs Nigel Short](https://www.chessgames.com/perl/chessgame?gid=1775611) vom 04.10.2014 - En Passant (`axb6`)
|
||||
- [Ulf Andersson vs Daniel Stellwagen](https://www.chessgames.com/perl/chessgame?gid=1427255) vom 27.08.2006 - En Passant (`bxc6+` und `dxe6`)
|
||||
- [Garry Kasparov vs Kiril Georgiev](https://www.chessgames.com/perl/chessgame?gid=1258478) vom 20.02.1988 - Stalemate
|
||||
- [Magnus Carlsen vs Hans Niemann](https://www.chessgames.com/perl/chessgame?gid=2821644) vom 31.12.2024
|
||||
|
||||
## Ausblick
|
||||
|
||||
- Multiplayer-Modus
|
||||
- Abspielen der Schachpartien (Schritt für Schritt) aus einer PGN-Datei
|
||||
- Anzeige der nächstmöglichen Züge bei Auswahl einer Figur
|
27
chessgames/Tests/1229276.pgn
Normal file
27
chessgames/Tests/1229276.pgn
Normal file
@@ -0,0 +1,27 @@
|
||||
[Event "Bosna 21st"]
|
||||
[Site "Sarajevo YUG"]
|
||||
[Date "1982.??.??"]
|
||||
[EventDate "1983.03.00"]
|
||||
[Round "?"]
|
||||
[Result "0-1"]
|
||||
[White "Vitomir Arapovic"]
|
||||
[Black "Alexander Beliavsky"]
|
||||
[ECO "C26"]
|
||||
[WhiteElo "?"]
|
||||
[BlackElo "?"]
|
||||
[PlyCount "150"]
|
||||
|
||||
1.e4 e5 2.Nc3 Nf6 3.Bc4 Bc5 4.d3 O-O 5.Nf3 Re8 6.Ng5 Re7 7.O-O
|
||||
h6 8.Nf3 c6 9.d4 exd4 10.e5 Ne8 11.Nxd4 d5 12.exd6 Nxd6 13.Bb3
|
||||
Na6 14.Be3 Qe8 15.Qf3 Bxd4 16.Bxd4 Nf5 17.Rad1 Bd7 18.Qh5 b6
|
||||
19.Qg6 Nxd4 20.Rxd4 Nc5 21.Bc4 Be6 22.Qg3 Bxc4 23.Rxc4 Rd8
|
||||
24.Rg4 f5 25.Rg6 Rd4 26.Rd6 f4 27.Qg6 Qxg6 28.Rxg6 Ne6 29.h4
|
||||
h5 30.g4 fxg3 31.Rxg3 Rf7 32.Re1 Nc5 33.Re8+ Kh7 34.Rg5 Rxh4
|
||||
35.b4 Nd7 36.Ne4 Nf6 37.Nxf6+ Rxf6 38.Re7 Rg6 39.Rxg6 Kxg6
|
||||
40.Re6+ Kh7 41.a3 Rc4 42.Re7 Rxc2 43.Rxa7 Rc4 44.Kg2 b5 45.Ra6
|
||||
g6 46.Ra8 Kh6 47.Rh8+ Kg5 48.Rg8 Kf5 49.Rf8+ Kg4 50.Rf6 g5
|
||||
51.Rf8 Rc3 52.Ra8 h4 53.a4 h3+ 54.Kh2 Rc2 55.Kg1 Rb2 56.axb5
|
||||
cxb5 57.Ra3 Kh4 58.Rd3 Rxb4 59.Kh2 g4 60.Rd5 Rb2 61.Kg1 Rb1+
|
||||
62.Kh2 Rf1 63.Rxb5 Rxf2+ 64.Kg1 Rc2 65.Rb4 Re2 66.Ra4 Kg3
|
||||
67.Ra1 Re3 68.Kh1 Kf3 69.Rf1+ Ke2 70.Rf8 g3 71.Kg1 Re4 72.Rh8
|
||||
Kf3 73.Rf8+ Rf4 74.Rg8 Rf7 75.Rh8 h2+ 0-1
|
29
chessgames/Tests/1258478.pgn
Normal file
29
chessgames/Tests/1258478.pgn
Normal file
@@ -0,0 +1,29 @@
|
||||
[Event "Wch-blitz"]
|
||||
[Site "Saint John CAN"]
|
||||
[Date "1988.02.20"]
|
||||
[EventDate "1988.02.19"]
|
||||
[Round "3"]
|
||||
[Result "1/2-1/2"]
|
||||
[White "Garry Kasparov"]
|
||||
[Black "Kiril Dimitrov Georgiev"]
|
||||
[ECO "A29"]
|
||||
[WhiteElo "2750"]
|
||||
[BlackElo "2595"]
|
||||
[PlyCount "155"]
|
||||
|
||||
1. c4 e5 2. Nc3 Nf6 3. Nf3 Nc6 4. g3 d5 5. cxd5 Nxd5 6. Bg2
|
||||
Nb6 7. O-O Be7 8. a3 a5 9. d3 O-O 10. Be3 Be6 11. Na4 Nd5
|
||||
12. Bc5 Bd6 13. Rc1 h6 14. Nd2 Rc8 15. Ne4 b6 16. Nxd6 cxd6
|
||||
17. Bxb6 Nxb6 18. Rxc6 Rb8 19. Nxb6 Rxb6 20. Qc2 Qb8 21. Rxb6
|
||||
Qxb6 22. Rb1 Bb3 23. Qd2 Rb8 24. Rc1 Be6 25. Rc2 d5 26. h4 a4
|
||||
27. Bf3 Qd4 28. e3 Qb6 29. d4 e4 30. Be2 Qb3 31. Kg2 g6
|
||||
32. Bd1 Kg7 33. Qc1 Qb6 34. Rd2 Bd7 35. Qc5 Qxc5 36. dxc5 Bc6
|
||||
37. Kf1 Kf6 38. Ke1 Ke5 39. Rc2 Rb5 40. Kd2 d4 41. exd4+ Kxd4
|
||||
42. Be2 Rxc5 43. Rxc5 Kxc5 44. Ke3 f5 45. g4 Kd5 46. gxf5 gxf5
|
||||
47.Kf4 Ke6 48. Bc4+ Kf6 49. b4 axb3 50. Bxb3 Bb5 51.a4 Ba6
|
||||
52.Bd5 Bc8 53.Bc6 Ke6 54.Bb5 Kd6 55. Bf1 Kc5 56.Bh3 Kb4
|
||||
57.Bxf5 Ba6 58.Bxe4 Kxa4 59.Ke5 Kb4 60. f4 Kc5 61.f5 Bc4 62.f6
|
||||
Bf7 63.Bf5 Kc6 64.Be6 Bg6 65.f7 Bxf7 66. Bxf7 Kd7 67.Kf6 Kd6
|
||||
68.h5 Kd7 69.Kg6 Ke7 70.Ba2 Ke8 71. Kxh6 Kf8 72.Kg6 Ke7 73.h6
|
||||
Kd6 74.h7 Kc5 75.h8=Q Kb4 76. Qd4+ Kb5 77.Bd5 Ka6 78.Qc5
|
||||
1/2-1/2
|
22
chessgames/Tests/1284086.pgn
Normal file
22
chessgames/Tests/1284086.pgn
Normal file
@@ -0,0 +1,22 @@
|
||||
[Event "Alicante"]
|
||||
[Site "Spain"]
|
||||
[Date "1992.??.??"]
|
||||
[EventDate "?"]
|
||||
[Round "7"]
|
||||
[Result "1/2-1/2"]
|
||||
[White "Stephen Boyd"]
|
||||
[Black "Torbjorn Glimbrant"]
|
||||
[ECO "C49"]
|
||||
[WhiteElo "2265"]
|
||||
[BlackElo "2235"]
|
||||
[PlyCount "91"]
|
||||
|
||||
1.e4 e5 2.Nf3 Nc6 3.Nc3 Nf6 4.Bb5 Bb4 5.O-O O-O 6.d3 d6 7.Bg5
|
||||
Bxc3 8.bxc3 Qe7 9.Re1 Nd8 10.d4 h6 11.Bc1 a6 12.Bd3 Bg4 13.h3
|
||||
Bd7 14.a4 a5 15.Nh4 Nc6 16.Nf5 Bxf5 17.exf5 Qd7 18.g4 exd4
|
||||
19.cxd4 Nxd4 20.Bb2 c5 21.Bxd4 cxd4 22.Bb5 Qc7 23.Qxd4 d5
|
||||
24.Re2 Rad8 25.Rae1 Rd6 26.Re7 Qxc2 27.Rxb7 Ne4 28.Re2 Qc1+
|
||||
29.Kg2 Qc8 30.Re7 Qd8 31.Qa7 Nf6 32.Rc7 d4 33.Ree7 d3 34.Rxf7
|
||||
Rxf7 35.Rxf7 d2 36.Rxg7+ Kh8 37.Be2 d1=Q 38.Bxd1 Rxd1 39.Rg6
|
||||
Qd5+ 40.Kg3 Rd3+ 41.Kh4 Rxh3+ 42.Kxh3 Qh1+ 43.Kg3 Nh5+ 44.gxh5
|
||||
Qf3+ 45.Kh2 Qg2+ 46.Kxg2 1/2-1/2
|
21
chessgames/Tests/1348060.pgn
Normal file
21
chessgames/Tests/1348060.pgn
Normal file
@@ -0,0 +1,21 @@
|
||||
[Event "Norwegian Championship"]
|
||||
[Site "Sandnes NOR"]
|
||||
[Date "2005.07.05"]
|
||||
[EventDate "2005.07.??"]
|
||||
[Round "5"]
|
||||
[Result "1-0"]
|
||||
[White "Magnus Carlsen"]
|
||||
[Black "Geir Sune Tallaksen Ostmoe"]
|
||||
[ECO "E15"]
|
||||
[WhiteElo "2528"]
|
||||
[BlackElo "2349"]
|
||||
[PlyCount "73"]
|
||||
|
||||
1. Nf3 Nf6 2. c4 e6 3. d4 b6 4. g3 Ba6 5. b3 b5 6. cxb5 Bxb5
|
||||
7. Bg2 d5 8. O-O Nbd7 9. Nc3 Ba6 10. Re1 Bd6 11. Bb2 O-O
|
||||
12. e4 Nxe4 13. Nxe4 dxe4 14. Rxe4 Bb7 15. Rh4 Be7 16. Rh3 Nf6
|
||||
17. Qe2 Bd5 18. Re1 Qb8 19. Ne5 Qb7 20. Bxd5 Qxd5 21. Qc2 c5
|
||||
22. Ng4 h6 23. Re5 Qf3 24. Nxh6+ gxh6 25. Rxh6 Kg7 26. Rg5+
|
||||
Kxh6 27. Bc1 cxd4 28. Rg4+ Qe3 29. Rh4+ Nh5 30. Rxh5+ Kxh5
|
||||
31. Qh7+ Kg4 32. fxe3 Rac8 33. Kg2 Rxc1 34. h3+ Kg5 35. Qg7+
|
||||
Kf5 36. g4+ Ke4 37. Qxd4# 1-0
|
31
chessgames/Tests/1427255.pgn
Normal file
31
chessgames/Tests/1427255.pgn
Normal file
@@ -0,0 +1,31 @@
|
||||
[Event "Youth - Experience"]
|
||||
[Site "Amsterdam NED"]
|
||||
[Date "2006.08.27"]
|
||||
[EventDate "2006.08.19"]
|
||||
[Round "8"]
|
||||
[Result "1/2-1/2"]
|
||||
[White "Ulf Andersson"]
|
||||
[Black "Daniel Stellwagen"]
|
||||
[ECO "A04"]
|
||||
[WhiteElo "2542"]
|
||||
[BlackElo "2575"]
|
||||
[PlyCount "175"]
|
||||
|
||||
1. Nf3 f5 2. d4 g6 3. g3 Bg7 4. Bg2 Nf6 5. O-O O-O 6. c4 d6
|
||||
7. Nc3 Qe8 8. d5 a5 9. Nd4 Na6 10. e4 fxe4 11. Nxe4 Nxe4
|
||||
12. Bxe4 Bh3 13. Re1 Nc5 14. Bh1 Qf7 15. Be3 Rae8 16. Qd2 b6
|
||||
17. b3 Kh8 18. Rad1 e5 19. dxe6 Nxe6 20. Bd5 Nxd4 21. Bxd4 Qd7
|
||||
22. a3 Bxd4 23. Qxd4+ Qg7 24. Qxg7+ Kxg7 25. Bc6 Rxe1+
|
||||
26. Rxe1 g5 27. f3 Bf5 28. Kf2 Kf6 29. b4 axb4 30. axb4 Be6
|
||||
31. Re4 Bf5 32. Rd4 Rf7 33. b5 Re7 34. Bd5 Be6 35. f4 Bxd5
|
||||
36. fxg5+ Kxg5 37. Rxd5+ Kg6 38. Kf3 Re1 39. h4 h5 40. Rg5+
|
||||
Kh6 41. g4 hxg4+ 42. Rxg4 Re5 43. Rd4 Rc5 44. Ke4 Kg6 45. Kd3
|
||||
Rh5 46. Rg4+ Kf6 47. Kd4 Ke6 48. Re4+ Kf6 49. Rg4 Ke6 50. Rf4
|
||||
Ke7 51. Ke4 Re5+ 52. Kf3 d5 53. Rd4 Re1 54. Rxd5 Rc1 55. Rd4
|
||||
Kf6 56. Ke4 Ke6 57. Kd3 Rd1+ 58. Kc3 Rc1+ 59. Kd2 Rh1 60. Re4+
|
||||
Kf6 61. Ke3 Rc1 62. Rg4 Kf5 63. Rg5+ Kf6 64. Kd4 Rh1 65. Rg4
|
||||
Ke6 66. Ke4 Re1+ 67. Kf3 Rc1 68. Ke3 Kf5 69. Rd4 Ke5 70. Rd5+
|
||||
Ke6 71. Kd3 Rd1+ 72. Ke4 Rh1 73. h5 Rh4+ 74. Kd3 Rh3+ 75. Kd4
|
||||
Rh4+ 76. Kc3 Rh3+ 77. Kb4 Rg3 78. Rd8 Rh3 79. Rh8 Kd7 80. h6
|
||||
c5+ 81. bxc6+ Kxc6 82. Rh7 Rh4 83. Kb3 Kc5 84. Rc7+ Kd6
|
||||
85. Rh7 Kc5 86. Rc7+ Kd6 87. Rb7 Kc6 88. Rh7 1/2-1/2
|
29
chessgames/Tests/1482551.pgn
Normal file
29
chessgames/Tests/1482551.pgn
Normal file
@@ -0,0 +1,29 @@
|
||||
[Event "Gibraltar Chess Festival"]
|
||||
[Site "Gibraltar ENG"]
|
||||
[Date "2008.01.23"]
|
||||
[EventDate "2008.01.22"]
|
||||
[Round "2"]
|
||||
[Result "1-0"]
|
||||
[White "Tim Jaksland"]
|
||||
[Black "Line Jin Jorgensen"]
|
||||
[ECO "C01"]
|
||||
[WhiteElo "2294"]
|
||||
[BlackElo "1867"]
|
||||
[PlyCount "149"]
|
||||
|
||||
1. e4 e6 2. d4 d5 3. exd5 exd5 4. Nf3 Nf6 5. Bd3 Bd6 6. O-O
|
||||
O-O 7. c4 c6 8. Nc3 Bg4 9. h3 Bh5 10. Re1 dxc4 11. Bxc4 Qc7
|
||||
12. g4 Bg6 13. Ne5 Nfd7 14. f4 Nxe5 15. dxe5 Bc5+ 16. Kg2 h6
|
||||
17. f5 Bh7 18. Qf3 Kh8 19. Bf4 b5 20. Bb3 Qb6 21. Rad1 a5
|
||||
22. a4 Ra7 23. Re2 b4 24. Ne4 Bd4 25. Rxd4 Qxd4 26. Rd2 Qb6
|
||||
27. Be3 Qc7 28. Bc5 Nd7 29. Rxd7 Qxd7 30. Bxf8 Qd4 31. Bc5
|
||||
Qxb2+ 32. Qf2 Qxf2+ 33. Kxf2 Rd7 34. Nd6 Bg8 35. Ke3 Re7
|
||||
36. Kf4 Rxe5 37. Kxe5 f6+ 38. Kd4 Bxb3 39. Bb6 Bxa4 40. Bxa5
|
||||
h5 41. gxh5 Bd1 42. h6 b3 43. hxg7+ Kxg7 44. Bc3 Kh6 45. Ke4
|
||||
Kh5 46. Bxf6 b2 47. Bxb2 Kh4 48. f6 Bb3 49. Ke5 Kxh3 50. f7
|
||||
Bxf7 51. Nxf7 c5 52. Nd6 c4 53. Kf4 c3 54. Bxc3 Kg2 55. Ne4
|
||||
Kh2 56. Kf3 Kg1 57. Nf2 Kh2 58. Be5+ Kg1 59. Bd6 Kf1 60. Bh2
|
||||
Ke1 61. Ne4 Kf1 62. Nd2+ Ke1 63. Ke3 Kd1 64. Kd3 Ke1 65. Bg3+
|
||||
Kd1 66. Bf2 Kc1 67. Nc4 Kd1 68. Nb2+ Kc1 69. Kc3 Kb1 70. Kb3
|
||||
Ka1 71. Be3 Kb1 72. Bf4 Ka1 73. Nc4 Kb1 74. Na3+ Ka1 75. Be5+
|
||||
1-0
|
18
chessgames/Tests/1588906.pgn
Normal file
18
chessgames/Tests/1588906.pgn
Normal file
@@ -0,0 +1,18 @@
|
||||
[Event "BCF-ch 87th"]
|
||||
[Site "Street ENG"]
|
||||
[Date "2000.08.09"]
|
||||
[EventDate "2000.07.31"]
|
||||
[Round "9"]
|
||||
[Result "0-1"]
|
||||
[White "Jack Rudd"]
|
||||
[Black "Martin Simons"]
|
||||
[ECO "B01"]
|
||||
[WhiteElo "2176"]
|
||||
[BlackElo "2226"]
|
||||
[PlyCount "40"]
|
||||
|
||||
1. e4 d5 2. exd5 Nf6 3. c4 e6 4. dxe6 Bxe6 5. d4 Bb4+ 6. Bd2
|
||||
Bxd2+ 7. Qxd2 Qe7 8. Qe2 Nc6 9. Nf3 O-O-O 10. d5 Rhe8 11. Kd1
|
||||
Nxd5 12. cxd5 Bxd5 13. Nfd2 Qf6 14. Qd3 Bxg2 15. Qg3 Bxh1
|
||||
16. Nc3 Bf3+ 17. Kc1 Re1+ 18. Kc2 Nd4+ 19. Kd3 Nb3+ 20. Kc2
|
||||
Nxa1# 0-1
|
21
chessgames/Tests/1775611.pgn
Normal file
21
chessgames/Tests/1775611.pgn
Normal file
@@ -0,0 +1,21 @@
|
||||
[Event "Isle of Man Masters"]
|
||||
[Site "Douglas IMN"]
|
||||
[Date "2014.10.11"]
|
||||
[EventDate "2014.10.04"]
|
||||
[Round "8.2"]
|
||||
[Result "0-1"]
|
||||
[White "Gabriel Sargissian"]
|
||||
[Black "Nigel Short"]
|
||||
[ECO "A70"]
|
||||
[WhiteElo "2690"]
|
||||
[BlackElo "2646"]
|
||||
[PlyCount "78"]
|
||||
|
||||
1. d4 Nf6 2. c4 e6 3. Nf3 c5 4. d5 exd5 5. cxd5 d6 6. Nc3 g6
|
||||
7. Bf4 a6 8. a4 Bg7 9. h3 O-O 10. e3 Ne8 11. Be2 Nd7 12. O-O
|
||||
Rb8 13. Nd2 Ne5 14. a5 f5 15. Bg3 Bd7 16. Qb3 b5 17. axb6 Rxb6
|
||||
18. Qc2 Bb5 19. Nxb5 axb5 20. Nb3 g5 21. Na5 f4 22. Bh2 Nc7
|
||||
23. Rfd1 Kh8 24. Qe4 Qf6 25. exf4 gxf4 26. Nb3 Na6 27. Nd2 c4
|
||||
28. Qb1 Nc5 29. Ne4 Nxe4 30. Qxe4 Rb7 31. Ra5 Nd7 32. b4 cxb3
|
||||
33. Bd3 Qh6 34. Qb4 Nc5 35. Bb1 Be5 36. Rxb5 Rg7 37. Be4 Nxe4
|
||||
38. Qxe4 Qxh3 39. Kh1 Qh5 0-1
|
19
chessgames/Tests/1799484.pgn
Normal file
19
chessgames/Tests/1799484.pgn
Normal file
@@ -0,0 +1,19 @@
|
||||
[Event "Russian Championship Superfinal"]
|
||||
[Site "Chita RUS"]
|
||||
[Date "2015.08.12"]
|
||||
[EventDate "2015.08.09"]
|
||||
[Round "4"]
|
||||
[Result "0-1"]
|
||||
[White "Peter Svidler"]
|
||||
[Black "Denis Khismatullin"]
|
||||
[ECO "B91"]
|
||||
[WhiteElo "2739"]
|
||||
[BlackElo "2642"]
|
||||
[PlyCount "58"]
|
||||
|
||||
1. e4 c5 2. Nc3 a6 3. Nge2 d6 4. g3 g6 5. d4 cxd4 6. Nxd4 Bg7
|
||||
7. Bg2 Nf6 8. b3 O-O 9. Bb2 Bd7 10. Qd2 Nc6 11. Nde2 b5
|
||||
12. O-O-O Ng4 13. Rdf1 Qa5 14. h3 Nf6 15. Kb1 b4 16. Nd1 Qc7
|
||||
17. Ne3 a5 18. f4 a4 19. e5 dxe5 20. fxe5 Nxe5 21. Bxa8 Rxa8
|
||||
22. Rf4 axb3 23. axb3 Qa7 24. Nd4 Nh5 25. Nd5 Bc6 26. Nxc6
|
||||
Qa2+ 27. Kc1 Qa1+ 28. Bxa1 Rxa1+ 29. Kb2 Nc4# 0-1
|
12
chessgames/save_games/20250116 - E gegen F - Runde 2.PGN
Normal file
12
chessgames/save_games/20250116 - E gegen F - Runde 2.PGN
Normal file
@@ -0,0 +1,12 @@
|
||||
[Event "Private Game"]
|
||||
[Site "Horb am Neckar, Germany"]
|
||||
[Date "2025.01.16"]
|
||||
[Round "2"]
|
||||
[White "E"]
|
||||
[Black "F"]
|
||||
[PlyCount "7"]
|
||||
[Result "*"]
|
||||
|
||||
|
||||
1. e3 e5 2. e4 f5 3. f3 g5
|
||||
4. g4 *
|
11
chessgames/save_games/20250116 - F gegen E - Runde 1
Normal file
11
chessgames/save_games/20250116 - F gegen E - Runde 1
Normal file
@@ -0,0 +1,11 @@
|
||||
[Event "Private Game"]
|
||||
[Site "Horb am Neckar, Germany"]
|
||||
[Date "2025.01.16"]
|
||||
[Round "1"]
|
||||
[White "F"]
|
||||
[Black "E"]
|
||||
[PlyCount "4"]
|
||||
[Result "*"]
|
||||
|
||||
|
||||
1. e3 e5 2. e4 f5 *
|
10
chessgames/save_games/20250116 - F gegen E - Runde 1.PGN
Normal file
10
chessgames/save_games/20250116 - F gegen E - Runde 1.PGN
Normal file
@@ -0,0 +1,10 @@
|
||||
[Event "Private Game"]
|
||||
[Site "Horb am Neckar, Germany"]
|
||||
[Date "2025.01.16"]
|
||||
[Round "1"]
|
||||
[White "F"]
|
||||
[Black "E"]
|
||||
[PlyCount "0"]
|
||||
[Result "*"]
|
||||
|
||||
*
|
@@ -0,0 +1,9 @@
|
||||
[Event "Private Game"]
|
||||
[Site "Horb am Neckar, Germany"]
|
||||
[Date "2025.01.16"]
|
||||
[Round "1"]
|
||||
[White "Player 1"]
|
||||
[Black "Player 2"]
|
||||
[PlyCount "0"]
|
||||
[Result "*"]
|
||||
*
|
@@ -0,0 +1,11 @@
|
||||
[Event "Private Game"]
|
||||
[Site "Horb am Neckar, Germany"]
|
||||
[Date "2025.01.16"]
|
||||
[Round "1"]
|
||||
[White "Player 2"]
|
||||
[Black "Player 1"]
|
||||
[PlyCount "3"]
|
||||
[Result "*"]
|
||||
|
||||
|
||||
1. e3 e6 2. f4 *
|
@@ -0,0 +1,11 @@
|
||||
[Event "Private Game"]
|
||||
[Site "Horb am Neckar, Germany"]
|
||||
[Date "2025.01.16"]
|
||||
[Round "1"]
|
||||
[White "Sebro"]
|
||||
[Black "Fabian"]
|
||||
[PlyCount "1"]
|
||||
[Result "*"]
|
||||
|
||||
|
||||
1. e3 *
|
2970
doc/Doxyfile
Normal file
2970
doc/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
17
main.cpp
17
main.cpp
@@ -1,17 +0,0 @@
|
||||
// Grundsätzlich erstmal nur zum Testen
|
||||
|
||||
#include <windows.h>
|
||||
#include "Chessboard.cpp"
|
||||
#include "Pawn.cpp"
|
||||
|
||||
int main() {
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
|
||||
//Pawn pawn('w', {1,6});
|
||||
Chessboard chessboard;
|
||||
//chessboard.init();
|
||||
chessboard.draw();
|
||||
/*chessboard.move("Pb2-b3");*/
|
||||
|
||||
return 0;
|
||||
}
|
12
makefile
Normal file
12
makefile
Normal file
@@ -0,0 +1,12 @@
|
||||
all:
|
||||
g++ -std=c++17 -o build/chess src/main.cpp src/ChessPieces/*.cpp src/Chessboard/*.cpp src/Controller/*.cpp src/Player/*.cpp src/Visualizer/*.cpp
|
||||
|
||||
run:
|
||||
./build/chess
|
||||
|
||||
clean:
|
||||
rm -f build/*
|
||||
rm -f -r doc/html
|
||||
|
||||
doc:
|
||||
doxygen doc/Doxyfile
|
2
run.ps1
Normal file
2
run.ps1
Normal file
@@ -0,0 +1,2 @@
|
||||
& "g++" -std=c++17 -o build/chess.exe src/main.cpp src/ChessPieces/*.cpp src/Chessboard/*.cpp src/Controller/*.cpp src/Player/*.cpp src/Visualizer/*.cpp
|
||||
./build/chess.exe
|
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"saves": [
|
||||
[
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[]
|
||||
],
|
||||
[
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[]
|
||||
],
|
||||
[
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[]
|
||||
]
|
||||
]
|
||||
}
|
24
src/ChessPieces/Bishop.cpp
Normal file
24
src/ChessPieces/Bishop.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "Bishop.hpp"
|
||||
|
||||
/**
|
||||
* Creates a new bishop with a specified color and position.
|
||||
* @param color The color of the bishop.
|
||||
* @param position The position of the bishop.
|
||||
*/
|
||||
Bishop::Bishop(ChessPieceColor color, ChessPiecePosition position) : ChessPiece(color, position) {
|
||||
this->_char = this->_CHAR_BISHOP;
|
||||
this->_unicode = {"\u265D", "\u2657"};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all chess piece positions the bishop can move next.
|
||||
* It is also possible to ignore several chess pieces while the next positions are calculated.
|
||||
* @param chessboard A pointer to the chessboard.
|
||||
* @param ignore A set of chess piece positions to be ignored.
|
||||
* @return A set of chess piece positions the bishop can move next.
|
||||
*/
|
||||
std::set<ChessPiecePosition> Bishop::GetNextPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore) {
|
||||
|
||||
// 3.2. The bishop may move to any square along a diagonal on which it stands.
|
||||
return this->GetDiagonalPositions(chessboard, ignore);
|
||||
}
|
12
src/ChessPieces/Bishop.hpp
Normal file
12
src/ChessPieces/Bishop.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "ChessPiece.hpp"
|
||||
|
||||
#ifndef ChessPiece_Bishop_H
|
||||
#define ChessPiece_Bishop_H
|
||||
|
||||
class Bishop : public ChessPiece {
|
||||
public:
|
||||
Bishop(ChessPieceColor color, ChessPiecePosition position);
|
||||
std::set<ChessPiecePosition> GetNextPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore = {});
|
||||
};
|
||||
|
||||
#endif
|
355
src/ChessPieces/ChessPiece.cpp
Normal file
355
src/ChessPieces/ChessPiece.cpp
Normal file
@@ -0,0 +1,355 @@
|
||||
#include "../ChessPieces/ChessPiece.hpp"
|
||||
#include "../Chessboard/Chessboard.hpp"
|
||||
|
||||
/**
|
||||
* Creates a new chess piece with a specific color and position.
|
||||
* @param color The color of the chess piece.
|
||||
* @param position The position of the chess piece.
|
||||
*/
|
||||
ChessPiece::ChessPiece(ChessPieceColor color, ChessPiecePosition position) {
|
||||
this->SetColor(color);
|
||||
this->SetPosition(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the char symbol of the chess piece.
|
||||
* @return The char symbol of the chess piece.
|
||||
*/
|
||||
char ChessPiece::GetChar() {
|
||||
return this->_char;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color of the chess piece.
|
||||
* @return The color of the chess piece.
|
||||
*/
|
||||
ChessPieceColor ChessPiece::GetColor() {
|
||||
return this->_color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a set of all positions along the diagonal.
|
||||
* @param chessboard A pointer to the chessboard.
|
||||
* @param ignore A set if chess piece positions to be ignored.
|
||||
* @return A set of all positions along the diagonal.
|
||||
*/
|
||||
std::set<ChessPiecePosition> ChessPiece::GetDiagonalPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore) {
|
||||
std::set<ChessPiecePosition> positions;
|
||||
ChessPiecePosition currentPosition = this->GetPosition();
|
||||
|
||||
for (std::string direction : {"T1L1", "T1R1", "B1L1", "B1R1"}) {
|
||||
ChessPiecePosition* nextPosition = ¤tPosition;
|
||||
|
||||
while (nextPosition = nextPosition->NextFromString(direction)) {
|
||||
if (chessboard->IsEmptyField(nextPosition) || ignore.count(*nextPosition) == 1) {
|
||||
|
||||
// 3.5. When making these moves, the bishop, rook or queen may not move over any intervening pieces.
|
||||
positions.insert(*nextPosition);
|
||||
} else {
|
||||
ChessPiece* chessPiece = chessboard->GetChessPiece(nextPosition);
|
||||
|
||||
// 3.1. It is not permitted to move a piece to a square occupied by a piece of the same colour.
|
||||
// 3.1.1. If a piece moves to a square occupied by an opponent's piece the latter is captured and removed from the chessboard as part of the same move.
|
||||
if (chessPiece && chessPiece->GetColor() != this->GetColor()) {
|
||||
positions.insert(*nextPosition);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a set of all positions along the file.
|
||||
* @param chessboard A pointer to the chessboard.
|
||||
* @param ignore A set if chess piece positions to be ignored.
|
||||
* @return A set of all positions along the file.
|
||||
*/
|
||||
std::set<ChessPiecePosition> ChessPiece::GetFilePositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore) {
|
||||
std::set<ChessPiecePosition> positions;
|
||||
ChessPiecePosition currentPosition = this->GetPosition();
|
||||
|
||||
for (std::string direction : {"T1", "B1"}) {
|
||||
ChessPiecePosition* nextPosition = ¤tPosition;
|
||||
|
||||
while (nextPosition = nextPosition->NextFromString(direction)) {
|
||||
if (chessboard->IsEmptyField(nextPosition) || ignore.count(*nextPosition) == 1) {
|
||||
|
||||
// 3.5. When making these moves, the bishop, rook or queen may not move over any intervening pieces.
|
||||
positions.insert(*nextPosition);
|
||||
} else {
|
||||
ChessPiece* chessPiece = chessboard->GetChessPiece(nextPosition);
|
||||
|
||||
// 3.1. It is not permitted to move a piece to a square occupied by a piece of the same colour.
|
||||
// 3.1.1. If a piece moves to a square occupied by an opponent's piece the latter is captured and removed from the chessboard as part of the same move.
|
||||
if (chessPiece && chessPiece->GetColor() != this->GetColor()) {
|
||||
positions.insert(*nextPosition);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a set of all positions the chess piece can move next.
|
||||
* @return A set of all positions the chess piece can move next.
|
||||
*/
|
||||
std::set<ChessPiecePosition> ChessPiece::GetNextPositions() {
|
||||
return this->_positions_next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of moves of the chess piece.
|
||||
* @return The number of moves of the chess piece.
|
||||
*/
|
||||
int ChessPiece::GetNumberOfMoves() {
|
||||
int positions = this->_positions.size();
|
||||
return (positions < 2) ? 0 : positions - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of the chess piece.
|
||||
* @return The position of the chess piece.
|
||||
* @throws std::out_of_range Thrown if the history of positions was not initialized properly.
|
||||
*/
|
||||
ChessPiecePosition ChessPiece::GetPosition() {
|
||||
if (this->_positions.empty() == false) {
|
||||
return this->_positions.back();
|
||||
} else {
|
||||
throw std::out_of_range("history was not initialized properly.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param ignore A set if chess piece positions to be ignored.
|
||||
* @return A set of all positions along the rank.
|
||||
*/
|
||||
std::set<ChessPiecePosition> ChessPiece::GetRankPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore) {
|
||||
std::set<ChessPiecePosition> positions;
|
||||
ChessPiecePosition currentPosition = this->GetPosition();
|
||||
|
||||
for (std::string direction : {"L1", "R1"}) {
|
||||
ChessPiecePosition* nextPosition = ¤tPosition;
|
||||
|
||||
while (nextPosition = nextPosition->NextFromString(direction)) {
|
||||
if (chessboard->IsEmptyField(nextPosition) || ignore.count(*nextPosition) == 1) {
|
||||
|
||||
// 3.5. When making these moves, the bishop, rook or queen may not move over any intervening pieces.
|
||||
positions.insert(*nextPosition);
|
||||
} else {
|
||||
ChessPiece* chessPiece = chessboard->GetChessPiece(nextPosition);
|
||||
|
||||
// 3.1. It is not permitted to move a piece to a square occupied by a piece of the same colour.
|
||||
// 3.1.1. If a piece moves to a square occupied by an opponent's piece the latter is captured and removed from the chessboard as part of the same move.
|
||||
if (chessPiece && chessPiece->GetColor() != this->GetColor()) {
|
||||
positions.insert(*nextPosition);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unicode symbol of the chess piece depending on the internal color.
|
||||
* @return The unicode symbol of the chess piece.
|
||||
*/
|
||||
std::string ChessPiece::GetUnicode() {
|
||||
return this->GetUnicode(this->GetColor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unicode symbol of the chess piece depending on the given color.
|
||||
* @param color The color of the chess piece.
|
||||
* @return The unicode symbol of the chess piece.
|
||||
*/
|
||||
std::string ChessPiece::GetUnicode(ChessPieceColor color) {
|
||||
if (color == ChessPieceColor::Black) {
|
||||
return std::get<ChessPieceColor::Black>(this->_unicode);
|
||||
} else {
|
||||
return std::get<ChessPieceColor::White>(this->_unicode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the chess piece is a bishop.
|
||||
* @return Status whether the chess piece is a bishop.
|
||||
*/
|
||||
bool ChessPiece::IsBishop() {
|
||||
return this->GetChar() == this->_CHAR_BISHOP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the chess piece can still be moved the first time.
|
||||
* @return Status whether the chess piece can still be moved the first time.
|
||||
*/
|
||||
bool ChessPiece::IsFirstMove() {
|
||||
return this->_positions.size() == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the chess piece is a king.
|
||||
* @return Status whether the chess piece is a king.
|
||||
*/
|
||||
bool ChessPiece::IsKing() {
|
||||
return this->GetChar() == this->_CHAR_KING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the chess piece is a knight.
|
||||
* @return Status whether the chess piece is a knight.
|
||||
*/
|
||||
bool ChessPiece::IsKnight() {
|
||||
return this->GetChar() == this->_CHAR_KNIGHT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the chess piece is a pawn.
|
||||
* @return Status whether the chess piece is a pawn.
|
||||
*/
|
||||
bool ChessPiece::IsPawn() {
|
||||
return this->GetChar() == this->_CHAR_PAWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the chess piece is a protective chess piece for the king.
|
||||
* Any movement of a protective chess piece would lead to a checkmate.
|
||||
* @param chessboard A pointer to the chessboard.
|
||||
* @return Status whether the chess piece is a protective chess piece for the king.
|
||||
*/
|
||||
bool ChessPiece::IsProtective(Chessboard* chessboard) {
|
||||
|
||||
if (this->IsKing()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ChessPiece* chessPieceKing = chessboard->GetChessPieceKing(this->GetColor());
|
||||
|
||||
// A chess piece is a protective chess piece if the chess piece exposes the king to an enemy chess piece after any movement.
|
||||
// So we get all chess piece positions of the opponents chess pieces with and without the current chess piece.
|
||||
// If any opponents chess piece can reach the king after movement the current chess piece is a protective chess piece.
|
||||
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) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the chess piece is a queen.
|
||||
* @return Status whether the chess piece is a queen.
|
||||
*/
|
||||
bool ChessPiece::IsQueen() {
|
||||
return this->GetChar() == this->_CHAR_QUEEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status whether the chess piece is a rook.
|
||||
* @return Status whether the chess piece is a rook.
|
||||
*/
|
||||
bool ChessPiece::IsRook() {
|
||||
return this->GetChar() == this->_CHAR_ROOK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color of the chess piece.
|
||||
* @param color The color of the chess piece.
|
||||
*/
|
||||
void ChessPiece::SetColor(ChessPieceColor color) {
|
||||
this->_color = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a set of all positions the chess piece can move next.
|
||||
* @param positions A set of all positions the chess piece can move next.
|
||||
*/
|
||||
void ChessPiece::SetNextPositions(std::set<ChessPiecePosition> positions) {
|
||||
this->_positions_next = positions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current position of the chess piece.
|
||||
* @param position The current position to be set in the chess piece.
|
||||
*/
|
||||
void ChessPiece::SetPosition(ChessPiecePosition position) {
|
||||
this->_positions.push_back(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the internal positions to which the chess piece can move next.
|
||||
* @param chessboard A pointer to the chessboard.
|
||||
*/
|
||||
void ChessPiece::UpdateNextPositions(Chessboard* chessboard) {
|
||||
if (this->IsProtective(chessboard)) {
|
||||
this->SetNextPositions(this->GetProtectivePositions(chessboard, this->GetNextPositions(chessboard)));
|
||||
} else {
|
||||
this->SetNextPositions(this->GetNextPositions(chessboard));
|
||||
}
|
||||
}
|
57
src/ChessPieces/ChessPiece.hpp
Normal file
57
src/ChessPieces/ChessPiece.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#ifndef ChessPiece_H
|
||||
#define ChessPiece_H
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include "../ChessPieces/ChessPieceColor.hpp"
|
||||
#include "../ChessPieces/ChessPiecePosition.hpp"
|
||||
#include "../Chessboard/Chessboard.hpp"
|
||||
|
||||
class ChessPiece {
|
||||
private:
|
||||
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';
|
||||
std::pair<std::string, std::string> _unicode;
|
||||
const char _CHAR_BISHOP = 'B';
|
||||
const char _CHAR_KING = 'K';
|
||||
const char _CHAR_KNIGHT = 'N';
|
||||
const char _CHAR_PAWN = 'P';
|
||||
const char _CHAR_QUEEN = 'Q';
|
||||
const char _CHAR_ROOK = 'R';
|
||||
ChessPiece(ChessPieceColor color, ChessPiecePosition position);
|
||||
std::set<ChessPiecePosition> GetDiagonalPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore = {});
|
||||
std::set<ChessPiecePosition> GetFilePositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore = {});
|
||||
std::set<ChessPiecePosition> GetRankPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore = {});
|
||||
void SetColor(ChessPieceColor color);
|
||||
void SetNextPositions(std::set<ChessPiecePosition> positions);
|
||||
|
||||
public:
|
||||
ChessPiece() = default;
|
||||
char GetChar();
|
||||
ChessPieceColor GetColor();
|
||||
std::set<ChessPiecePosition> GetNextPositions();
|
||||
virtual std::set<ChessPiecePosition> GetNextPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore = {}) = 0;
|
||||
int GetNumberOfMoves();
|
||||
ChessPiecePosition GetPosition();
|
||||
std::string GetUnicode();
|
||||
std::string GetUnicode(ChessPieceColor color);
|
||||
bool IsBishop();
|
||||
bool IsFirstMove();
|
||||
bool IsKing();
|
||||
bool IsKnight();
|
||||
bool IsPawn();
|
||||
bool IsQueen();
|
||||
bool IsRook();
|
||||
void SetPosition(ChessPiecePosition position);
|
||||
void UpdateNextPositions(Chessboard* chessboard);
|
||||
};
|
||||
|
||||
#endif
|
9
src/ChessPieces/ChessPieceColor.hpp
Normal file
9
src/ChessPieces/ChessPieceColor.hpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef ChessPieceColor_H
|
||||
#define ChessPieceColor_H
|
||||
|
||||
enum ChessPieceColor {
|
||||
Black = 0,
|
||||
White = 1
|
||||
};
|
||||
|
||||
#endif
|
168
src/ChessPieces/ChessPieceMove.cpp
Normal file
168
src/ChessPieces/ChessPieceMove.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
#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) {
|
||||
this->_move = this->Normalize(move);
|
||||
|
||||
if (this->IsValidShortNotation()) {
|
||||
this->ParseShortNotation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the move of the chess piece as string value.
|
||||
* @return The move of the chess piece as string value.
|
||||
*/
|
||||
std::string ChessPieceMove::ToString() {
|
||||
return (this->IsValidShortNotation()) ? this->_move : "";
|
||||
}
|
42
src/ChessPieces/ChessPieceMove.hpp
Normal file
42
src/ChessPieces/ChessPieceMove.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#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();
|
||||
std::string ToString();
|
||||
};
|
||||
|
||||
#endif
|
329
src/ChessPieces/ChessPiecePosition.cpp
Normal file
329
src/ChessPieces/ChessPiecePosition.cpp
Normal file
@@ -0,0 +1,329 @@
|
||||
#include "../ChessPieces/ChessPiecePosition.hpp"
|
||||
|
||||
/**
|
||||
* Creates a new chess piece position.
|
||||
* @param position The chess piece position as string.
|
||||
*/
|
||||
ChessPiecePosition::ChessPiecePosition(std::string position) {
|
||||
if (position.length() != 2) {
|
||||
throw std::invalid_argument("invalid position");
|
||||
} else {
|
||||
this->SetFile(position[0]);
|
||||
this->SetRank(position[1] - '0');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new chess piece position.
|
||||
* @param file The file part of the chess piece position.
|
||||
* @param rank The rank part of the chess piece position.
|
||||
*/
|
||||
ChessPiecePosition::ChessPiecePosition(char file, int rank) {
|
||||
this->SetFile(file);
|
||||
this->SetRank(rank);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the current chess piece position with another chess piece position.
|
||||
* @param position The other chess piece position to be checked with the current chess piece position.
|
||||
*/
|
||||
bool ChessPiecePosition::operator< (ChessPiecePosition const position) const {
|
||||
if (this->_file == position._file) {
|
||||
return this->_rank < position._rank;
|
||||
} else {
|
||||
return this->_file < position._file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the current chess piece position with another chess piece position.
|
||||
* @param position The other chess piece position to be checked with the current chess piece position.
|
||||
*/
|
||||
bool ChessPiecePosition::operator== (ChessPiecePosition const position) const {
|
||||
return this->_file == position._file && this->_rank == position._rank;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the moves to reach the target chess piece position from the starting chess piece position.
|
||||
* @param fromPosition The starting chess piece position.
|
||||
* @param toPosition The target chess piece position.
|
||||
* @return A string with the moves to reach the target chess piece position from the starting chess piece position.
|
||||
* An empty string if the starting chess piece position is the same as the target chess piece position.
|
||||
*/
|
||||
std::string ChessPiecePosition::GetDifference(ChessPiecePosition fromPosition, ChessPiecePosition toPosition) {
|
||||
if (fromPosition == toPosition) {
|
||||
return "";
|
||||
} else {
|
||||
std::string difference;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file part of the chess piece position.
|
||||
* @return char The file part of the chess piece position.
|
||||
*/
|
||||
char ChessPiecePosition::GetFile() {
|
||||
return this->_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the rank part of the chess piece position.
|
||||
* @return int The rank part of the chess piece position.
|
||||
*/
|
||||
int ChessPiecePosition::GetRank() {
|
||||
return this->_rank;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the step to reach the target chess piece position from the starting chess piece position.
|
||||
* The amount of steps to reach the target chess piece position is not known but the target chess piece position is reachable.
|
||||
* @param fromPosition The starting chess piece position.
|
||||
* @param toPosition The target chess piece position.
|
||||
* @return A string with the step to reach the target chess piece position from the starting chess piece position.
|
||||
* An empty string if the starting chess piece position is the same as the target chess piece position
|
||||
* or the target chess piece position is not reachable directly (vertical, horizontal, diagonal).
|
||||
*/
|
||||
std::string ChessPiecePosition::GetStep(ChessPiecePosition fromPosition, ChessPiecePosition toPosition) {
|
||||
if (fromPosition == toPosition) {
|
||||
return "";
|
||||
} else {
|
||||
if (fromPosition.GetRank() == toPosition.GetRank()) {
|
||||
if (fromPosition.GetFile() > toPosition.GetFile()) {
|
||||
return std::string(1, ChessPiecePosition::_MOVE_LEFT) + std::string(1, '1');
|
||||
} else if (fromPosition.GetFile() < toPosition.GetFile()) {
|
||||
return std::string(1, ChessPiecePosition::_MOVE_RIGHT) + std::string(1, '1');
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
} else if (fromPosition.GetFile() == toPosition.GetFile()) {
|
||||
if (fromPosition.GetRank() > toPosition.GetRank()) {
|
||||
return std::string(1, ChessPiecePosition::_MOVE_BOTTOM) + std::string(1, '1');
|
||||
} else if (fromPosition.GetRank() < toPosition.GetRank()) {
|
||||
return std::string(1, ChessPiecePosition::_MOVE_TOP) + std::string(1, '1');
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
int rankDifference = fromPosition.GetRank() - toPosition.GetRank();
|
||||
int fileDifference = fromPosition.GetFile() - toPosition.GetFile();
|
||||
|
||||
if (std::abs(rankDifference) != std::abs(fileDifference)) {
|
||||
return "";
|
||||
} else {
|
||||
if (rankDifference < 0) {
|
||||
if (fileDifference < 0) {
|
||||
return std::string(1, ChessPiecePosition::_MOVE_TOP) + std::string(1, '1') + std::string(1, ChessPiecePosition::_MOVE_RIGHT) + std::string(1, '1');
|
||||
} else {
|
||||
return std::string(1, ChessPiecePosition::_MOVE_TOP) + std::string(1, '1') + std::string(1, ChessPiecePosition::_MOVE_LEFT) + std::string(1, '1');
|
||||
}
|
||||
} else {
|
||||
if (fileDifference < 0) {
|
||||
return std::string(1, ChessPiecePosition::_MOVE_BOTTOM) + std::string(1, '1') + std::string(1, ChessPiecePosition::_MOVE_RIGHT) + std::string(1, '1');
|
||||
} else {
|
||||
return std::string(1, ChessPiecePosition::_MOVE_BOTTOM) + std::string(1, '1') + std::string(1, ChessPiecePosition::_MOVE_LEFT) + std::string(1, '1');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next chess piece position below the current chess piece position.
|
||||
* @return A pointer to the chess piece position below the current chess piece position.
|
||||
* Returns a null pointer if the chess piece position is not available.
|
||||
*/
|
||||
ChessPiecePosition* ChessPiecePosition::NextBottom() {
|
||||
try {
|
||||
return new ChessPiecePosition(this->GetFile(), this->GetRank() - 1);
|
||||
} catch (const std::out_of_range e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next chess piece position below to the left of the current chess piece position.
|
||||
* @return A pointer to the chess piece position below to the left of the current chess piece position.
|
||||
* Returns a null pointer if the chess piece position is not available.
|
||||
*/
|
||||
ChessPiecePosition* ChessPiecePosition::NextBottomLeft() {
|
||||
try {
|
||||
return new ChessPiecePosition(this->GetFile() - 1, this->GetRank() - 1);
|
||||
} catch (const std::out_of_range e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next chess piece position below to the right of the current chess piece position.
|
||||
* @return A pointer to the chess piece position below to the right of the current chess piece position.
|
||||
* Returns a null pointer if the chess piece position is not available.
|
||||
*/
|
||||
ChessPiecePosition* ChessPiecePosition::NextBottomRight() {
|
||||
try {
|
||||
return new ChessPiecePosition(this->GetFile() + 1, this->GetRank() - 1);
|
||||
} catch (const std::out_of_range e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next chess piece position based on the current chess piece position and specified steps.
|
||||
* @param steps The steps to be added to the current chess piece position.
|
||||
* @return A pointer to the chess piece position based on the current chess piece position and specified steps.
|
||||
* Returns a null pointer if the chess piece position is not available.
|
||||
*/
|
||||
ChessPiecePosition* ChessPiecePosition::NextFromString(std::string steps) {
|
||||
if (steps.length() % 2 != 0) return nullptr;
|
||||
|
||||
char nextFile = this->GetFile();
|
||||
int nextRank = this->GetRank();
|
||||
|
||||
for (int step = 0; step < steps.length() / 2; step++) {
|
||||
char direction = steps[0 + (step * 2)];
|
||||
int moves = steps[1 + (step * 2)] - '0';
|
||||
|
||||
if (!(moves >= 1 && moves <= 8)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch (direction) {
|
||||
case ChessPiecePosition::_MOVE_BOTTOM:
|
||||
nextRank -= moves;
|
||||
break;
|
||||
case ChessPiecePosition::_MOVE_LEFT:
|
||||
nextFile -= moves;
|
||||
break;
|
||||
case ChessPiecePosition::_MOVE_RIGHT:
|
||||
nextFile += moves;
|
||||
break;
|
||||
case ChessPiecePosition::_MOVE_TOP:
|
||||
nextRank += moves;
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return new ChessPiecePosition(nextFile, nextRank);
|
||||
} catch (const std::out_of_range e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next chess piece position left of the current chess piece position.
|
||||
* @return A pointer to the chess piece position left of the current chess piece position.
|
||||
* Returns a null pointer if the chess piece position is not available.
|
||||
*/
|
||||
ChessPiecePosition* ChessPiecePosition::NextLeft() {
|
||||
try {
|
||||
return new ChessPiecePosition(this->GetFile() - 1, this->GetRank());
|
||||
} catch (const std::out_of_range e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next chess piece position right of the current chess piece position.
|
||||
* @return A pointer to the chess piece position right of the current chess piece position.
|
||||
* Returns a null pointer if the chess piece position is not available.
|
||||
*/
|
||||
ChessPiecePosition* ChessPiecePosition::NextRight() {
|
||||
try {
|
||||
return new ChessPiecePosition(this->GetFile() + 1, this->GetRank());
|
||||
} catch (const std::out_of_range e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next chess piece position above the current chess piece position.
|
||||
* @return A pointer to the chess piece position above the current chess piece position.
|
||||
* Returns a null pointer if the chess piece position is not available.
|
||||
*/
|
||||
ChessPiecePosition* ChessPiecePosition::NextTop() {
|
||||
try {
|
||||
return new ChessPiecePosition(this->GetFile(), this->GetRank() + 1);
|
||||
} catch (const std::out_of_range e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next chess piece position above to the left of the current chess piece position.
|
||||
* @return A pointer to the chess piece position above to the left of the current chess piece position.
|
||||
* Returns a null pointer if the chess piece position is not available.
|
||||
*/
|
||||
ChessPiecePosition* ChessPiecePosition::NextTopLeft() {
|
||||
try {
|
||||
return new ChessPiecePosition(this->GetFile() - 1, this->GetRank() + 1);
|
||||
} catch (const std::out_of_range e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next chess piece position above to the right of the current chess piece position.
|
||||
* @return A pointer to the chess piece position above to the right of the current chess piece position.
|
||||
* Returns a null pointer if the chess piece position is not available.
|
||||
*/
|
||||
ChessPiecePosition* ChessPiecePosition::NextTopRight() {
|
||||
try {
|
||||
return new ChessPiecePosition(this->GetFile() + 1, this->GetRank() + 1);
|
||||
} catch (const std::out_of_range e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file part of the chess piece position.
|
||||
* @param file The file part of the chess piece position.
|
||||
*/
|
||||
void ChessPiecePosition::SetFile(char file) {
|
||||
|
||||
if (file >= 'a' && file <= 'h') {
|
||||
file = char(std::toupper(file));
|
||||
}
|
||||
|
||||
if (file >= 'A' && file <= 'H') {
|
||||
this->_file = file;
|
||||
} else {
|
||||
throw std::out_of_range("file is out of range");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the rank part of the chess piece position.
|
||||
* @param rank The rank part of the chess piece position.
|
||||
*/
|
||||
void ChessPiecePosition::SetRank(int rank) {
|
||||
if (rank >= 1 && rank <= 8) {
|
||||
this->_rank = rank;
|
||||
} else {
|
||||
throw std::out_of_range("rank is out of range");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current chess piece position as string.
|
||||
* @return The current chess piece position as string.
|
||||
*/
|
||||
std::string ChessPiecePosition::ToString() {
|
||||
return std::string(1, this->GetFile()) + std::to_string(this->GetRank());
|
||||
}
|
39
src/ChessPieces/ChessPiecePosition.hpp
Normal file
39
src/ChessPieces/ChessPiecePosition.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef ChessPiece_ChessPiecePosition_H
|
||||
#define ChessPiece_ChessPiecePosition_H
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
class ChessPiecePosition {
|
||||
private:
|
||||
char _file;
|
||||
int _rank;
|
||||
static const char _MOVE_LEFT = 'L';
|
||||
static const char _MOVE_RIGHT = 'R';
|
||||
static const char _MOVE_TOP = 'T';
|
||||
static const char _MOVE_BOTTOM = 'B';
|
||||
void SetFile(char file);
|
||||
void SetRank(int rank);
|
||||
|
||||
public:
|
||||
ChessPiecePosition(std::string position);
|
||||
ChessPiecePosition(char file, int rank);
|
||||
bool operator< (ChessPiecePosition const position) const;
|
||||
bool operator== (ChessPiecePosition const position) const;
|
||||
static std::string GetDifference(ChessPiecePosition fromPosition, ChessPiecePosition toPosition);
|
||||
char GetFile();
|
||||
int GetRank();
|
||||
static std::string GetStep(ChessPiecePosition fromPosition, ChessPiecePosition toPosition);
|
||||
ChessPiecePosition* NextBottom();
|
||||
ChessPiecePosition* NextBottomLeft();
|
||||
ChessPiecePosition* NextBottomRight();
|
||||
ChessPiecePosition* NextFromString(std::string steps);
|
||||
ChessPiecePosition* NextLeft();
|
||||
ChessPiecePosition* NextRight();
|
||||
ChessPiecePosition* NextTop();
|
||||
ChessPiecePosition* NextTopLeft();
|
||||
ChessPiecePosition* NextTopRight();
|
||||
std::string ToString();
|
||||
};
|
||||
|
||||
#endif
|
71
src/ChessPieces/King.cpp
Normal file
71
src/ChessPieces/King.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "King.hpp"
|
||||
|
||||
/**
|
||||
* Creates a new king with a specified color and position.
|
||||
* @param color The color of the king.
|
||||
* @param position The position of the king.
|
||||
*/
|
||||
King::King(ChessPieceColor color, ChessPiecePosition position) : ChessPiece(color, position) {
|
||||
this->_char = this->_CHAR_KING;
|
||||
this->_unicode = {"\u265A", "\u2654"};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all chess piece positions the king can move next.
|
||||
* The set of chess piece positions will be ignored, because the king has a fixed movement.
|
||||
* @param chessboard A pointer to the chessboard.
|
||||
* @param ignore A set of chess piece positions to be ignored.
|
||||
* @return A set of chess piece positions the rook can move next.
|
||||
*/
|
||||
std::set<ChessPiecePosition> King::GetNextPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore) {
|
||||
std::set<ChessPiecePosition> positions;
|
||||
|
||||
// There are two different ways of moving the king:
|
||||
// 3.8.1. by moving to an adjoining square.
|
||||
// 3.8.2. by 'castling'. This is a move of the king and either rook of the same colour along the player's first rank,
|
||||
// counting as a single move of the king and executed as follows: the king is transferred from its original square
|
||||
// two squares towards the rook on its original square, then that rook is transferred to the square the king
|
||||
// has just crossed. -> is implemented directly on the move function.
|
||||
for (std::string move : {"T1", "T1L1", "T1R1", "B1", "B1L1", "B1R1", "L1", "R1"}) {
|
||||
ChessPiecePosition* nextPosition = this->GetPosition().NextFromString(move);
|
||||
if (nextPosition == nullptr) continue;
|
||||
|
||||
if (chessboard->IsEmptyField(nextPosition) || chessboard->GetChessPiece(nextPosition)->GetColor() != this->GetColor()) {
|
||||
ChessPieceColor opponentColor = (this->GetColor() == ChessPieceColor::Black) ? ChessPieceColor::White : ChessPieceColor::Black;
|
||||
|
||||
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->IsPawn()) continue; // is checked by under attack
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
12
src/ChessPieces/King.hpp
Normal file
12
src/ChessPieces/King.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "ChessPiece.hpp"
|
||||
|
||||
#ifndef ChessPiece_King_H
|
||||
#define ChessPiece_King_H
|
||||
|
||||
class King : public ChessPiece {
|
||||
public:
|
||||
King(ChessPieceColor color, ChessPiecePosition position);
|
||||
std::set<ChessPiecePosition> GetNextPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore = {});
|
||||
};
|
||||
|
||||
#endif
|
36
src/ChessPieces/Knight.cpp
Normal file
36
src/ChessPieces/Knight.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "Knight.hpp"
|
||||
|
||||
/**
|
||||
* Creates a new knight with a specified color and position.
|
||||
* @param color The color of the knight.
|
||||
* @param position The position of the knight.
|
||||
*/
|
||||
Knight::Knight(ChessPieceColor color, ChessPiecePosition position) : ChessPiece(color, position) {
|
||||
this->_char = this->_CHAR_KNIGHT;
|
||||
this->_unicode = {"\u265E", "\u2658"};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all chess piece positions the knight can move next.
|
||||
* The set of chess piece positions will be ignored, because the knight has a fixed movement.
|
||||
* @param chessboard A pointer to the chessboard.
|
||||
* @param ignore A set of chess piece positions to be ignored.
|
||||
* @return A set of chess piece positions the knight can move next.
|
||||
*/
|
||||
std::set<ChessPiecePosition> Knight::GetNextPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore) {
|
||||
std::set<ChessPiecePosition> positions;
|
||||
|
||||
// 3.6. The knight may move to one of the squares nearest to that on which it stands but not on the same rank, file or diagonal.
|
||||
for (std::string move : {"T2L1", "T2R1", "L2T1", "L2B1", "B2L1", "B2R1", "R2T1", "R2B1"}) {
|
||||
ChessPiecePosition* nextPosition = this->GetPosition().NextFromString(move);
|
||||
if (nextPosition == nullptr) continue;
|
||||
|
||||
// 3.1. It is not permitted to move a piece to a square occupied by a piece of the same colour.
|
||||
// 3.1.1. If a piece moves to a square occupied by an opponent's piece the latter is captured and removed from the chessboard as part of the same move.
|
||||
if (chessboard->IsEmptyField(nextPosition) || chessboard->GetChessPiece(nextPosition)->GetColor() != this->GetColor()) {
|
||||
positions.insert(*nextPosition);
|
||||
}
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
12
src/ChessPieces/Knight.hpp
Normal file
12
src/ChessPieces/Knight.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "ChessPiece.hpp"
|
||||
|
||||
#ifndef ChessPiece_Knight_H
|
||||
#define ChessPiece_Knight_H
|
||||
|
||||
class Knight : public ChessPiece {
|
||||
public:
|
||||
Knight(ChessPieceColor color, ChessPiecePosition position);
|
||||
std::set<ChessPiecePosition> GetNextPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore = {});
|
||||
};
|
||||
|
||||
#endif
|
96
src/ChessPieces/Pawn.cpp
Normal file
96
src/ChessPieces/Pawn.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "Pawn.hpp"
|
||||
|
||||
/**
|
||||
* Creates a new pawn with a specified color and position.
|
||||
* @param color The color of the pawn.
|
||||
* @param position The position of the pawn.
|
||||
*/
|
||||
Pawn::Pawn(ChessPieceColor color, ChessPiecePosition position) : ChessPiece(color, position) {
|
||||
this->_char = this->_CHAR_PAWN;
|
||||
this->_unicode = {"\u265F", "\u2659"};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all chess piece positions the pawn can move next.
|
||||
* The set of chess piece positions will be ignored, because the pawn has a fixed movement.
|
||||
* @param chessboard A pointer to the chessboard.
|
||||
* @param ignore A set of chess piece positions to be ignored.
|
||||
* @return A set of chess piece positions the pawn can move next.
|
||||
*/
|
||||
std::set<ChessPiecePosition> Pawn::GetNextPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore) {
|
||||
std::set<ChessPiecePosition> positions;
|
||||
ChessPiecePosition currentPosition = this->GetPosition();
|
||||
ChessPiecePosition* nextPosition = ¤tPosition;
|
||||
std::vector<std::string> nextDiagonalPositions;
|
||||
std::string moveDifferenceOpponent;
|
||||
|
||||
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.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;
|
||||
}
|
||||
|
||||
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->GetCountMoves() > 0) {
|
||||
std::pair<ChessPiecePosition, ChessPiecePosition> lastMovePositions = chessboard->GetLastMovePositions();
|
||||
std::string moveDifference = ChessPiecePosition::GetDifference(lastMovePositions.first, lastMovePositions.second);
|
||||
|
||||
for (std::string nextDiagonalPosition : nextDiagonalPositions) {
|
||||
nextPosition = ¤tPosition;
|
||||
std::string stepDiagonal = nextDiagonalPosition.substr(2, 2);
|
||||
nextPosition = nextPosition->NextFromString(stepDiagonal);
|
||||
|
||||
if (nextPosition && chessboard->IsEmptyField(nextPosition) == false) {
|
||||
ChessPiece* chessPiece = chessboard->GetChessPiece(nextPosition);
|
||||
|
||||
if (chessPiece->IsPawn() && chessPiece->GetColor() != this->GetColor()) {
|
||||
if (chessPiece->GetNumberOfMoves() == 1 && moveDifference == moveDifferenceOpponent && chessPiece->GetPosition() == lastMovePositions.second) {
|
||||
ChessPiecePosition* diagonalPosition = currentPosition.NextFromString(nextDiagonalPosition);
|
||||
|
||||
if (diagonalPosition && chessboard->IsEmptyField(diagonalPosition)) {
|
||||
positions.insert(*diagonalPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
12
src/ChessPieces/Pawn.hpp
Normal file
12
src/ChessPieces/Pawn.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "ChessPiece.hpp"
|
||||
|
||||
#ifndef ChessPiece_Pawn_H
|
||||
#define ChessPiece_Pawn_H
|
||||
|
||||
class Pawn : public ChessPiece {
|
||||
public:
|
||||
Pawn(ChessPieceColor color, ChessPiecePosition position);
|
||||
std::set<ChessPiecePosition> GetNextPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore = {});
|
||||
};
|
||||
|
||||
#endif
|
28
src/ChessPieces/Queen.cpp
Normal file
28
src/ChessPieces/Queen.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "Queen.hpp"
|
||||
|
||||
/**
|
||||
* Creates a new queen with a specified color and position.
|
||||
* @param color The color of the queen.
|
||||
* @param position The position of the queen.
|
||||
*/
|
||||
Queen::Queen(ChessPieceColor color, ChessPiecePosition position) : ChessPiece(color, position) {
|
||||
this->_char = this->_CHAR_QUEEN;
|
||||
this->_unicode = {"\u265B", "\u2655"};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all chess piece positions the queen can move next.
|
||||
* It is also possible to ignore several chess pieces while the next positions are calculated.
|
||||
* @param chessboard A pointer to the chessboard.
|
||||
* @param ignore A set of chess piece positions to be ignored.
|
||||
* @return A set of chess piece positions the queen can move next.
|
||||
*/
|
||||
std::set<ChessPiecePosition> Queen::GetNextPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore) {
|
||||
|
||||
// 3.4. The queen may move to any square along the file, the rank or a diagonal on which it stands.
|
||||
std::set<ChessPiecePosition> positions;
|
||||
positions.merge(this->GetFilePositions(chessboard, ignore));
|
||||
positions.merge(this->GetRankPositions(chessboard, ignore));
|
||||
positions.merge(this->GetDiagonalPositions(chessboard, ignore));
|
||||
return positions;
|
||||
}
|
12
src/ChessPieces/Queen.hpp
Normal file
12
src/ChessPieces/Queen.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "ChessPiece.hpp"
|
||||
|
||||
#ifndef ChessPiece_Queen_H
|
||||
#define ChessPiece_Queen_H
|
||||
|
||||
class Queen : public ChessPiece {
|
||||
public:
|
||||
Queen(ChessPieceColor color, ChessPiecePosition position);
|
||||
std::set<ChessPiecePosition> GetNextPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore = {});
|
||||
};
|
||||
|
||||
#endif
|
27
src/ChessPieces/Rook.cpp
Normal file
27
src/ChessPieces/Rook.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "Rook.hpp"
|
||||
|
||||
/**
|
||||
* Creates a new rook with a specified color and position.
|
||||
* @param color The color of the rook.
|
||||
* @param position The position of the rook.
|
||||
*/
|
||||
Rook::Rook(ChessPieceColor color, ChessPiecePosition position) : ChessPiece(color, position) {
|
||||
this->_char = this->_CHAR_ROOK;
|
||||
this->_unicode = {"\u265C", "\u2656"};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all chess piece positions the rook can move next.
|
||||
* It is also possible to ignore several chess pieces while the next positions are calculated.
|
||||
* @param chessboard A pointer to the chessboard.
|
||||
* @param ignore A set of chess piece positions to be ignored.
|
||||
* @return A set of chess piece positions the rook can move next.
|
||||
*/
|
||||
std::set<ChessPiecePosition> Rook::GetNextPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore) {
|
||||
|
||||
// 3.3 The rook may move to any square along the file or the rank on which it stands.
|
||||
std::set<ChessPiecePosition> positions;
|
||||
positions.merge(this->GetFilePositions(chessboard, ignore));
|
||||
positions.merge(this->GetRankPositions(chessboard, ignore));
|
||||
return positions;
|
||||
}
|
12
src/ChessPieces/Rook.hpp
Normal file
12
src/ChessPieces/Rook.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "ChessPiece.hpp"
|
||||
|
||||
#ifndef ChessPiece_Rook_H
|
||||
#define ChessPiece_Rook_H
|
||||
|
||||
class Rook : public ChessPiece {
|
||||
public:
|
||||
Rook(ChessPieceColor color, ChessPiecePosition position);
|
||||
std::set<ChessPiecePosition> GetNextPositions(Chessboard* chessboard, std::set<ChessPiecePosition> ignore = {});
|
||||
};
|
||||
|
||||
#endif
|
914
src/Chessboard/Chessboard.cpp
Normal file
914
src/Chessboard/Chessboard.cpp
Normal file
@@ -0,0 +1,914 @@
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <ctime>
|
||||
#include "../Chessboard/Chessboard.hpp"
|
||||
#include "../ChessPieces/Rook.hpp"
|
||||
#include "../ChessPieces/Queen.hpp"
|
||||
#include "../ChessPieces/Bishop.hpp"
|
||||
#include "../ChessPieces/Knight.hpp"
|
||||
#include "../ChessPieces/ChessPiece.hpp"
|
||||
#include "../ChessPieces/Knight.hpp"
|
||||
#include "../ChessPieces/Bishop.hpp"
|
||||
#include "../ChessPieces/King.hpp"
|
||||
#include "../ChessPieces/Pawn.hpp"
|
||||
#include "../ChessPieces/Queen.hpp"
|
||||
#include "../ChessPieces/Rook.hpp"
|
||||
|
||||
void Chessboard::SetStartingPosition() {
|
||||
|
||||
// black chess pieces
|
||||
this->SetChessPiece(new Rook(ChessPieceColor::Black, ChessPiecePosition("A8")));
|
||||
this->SetChessPiece(new Knight(ChessPieceColor::Black, ChessPiecePosition("B8")));
|
||||
this->SetChessPiece(new Bishop(ChessPieceColor::Black, ChessPiecePosition("C8")));
|
||||
this->SetChessPiece(new Queen(ChessPieceColor::Black, ChessPiecePosition("D8")));
|
||||
this->SetChessPiece(new King(ChessPieceColor::Black, ChessPiecePosition("E8")));
|
||||
this->SetChessPiece(new Bishop(ChessPieceColor::Black, ChessPiecePosition("F8")));
|
||||
this->SetChessPiece(new Knight(ChessPieceColor::Black, ChessPiecePosition("G8")));
|
||||
this->SetChessPiece(new Rook(ChessPieceColor::Black, ChessPiecePosition("H8")));
|
||||
this->SetChessPiece(new Pawn(ChessPieceColor::Black, ChessPiecePosition("A7")));
|
||||
this->SetChessPiece(new Pawn(ChessPieceColor::Black, ChessPiecePosition("B7")));
|
||||
this->SetChessPiece(new Pawn(ChessPieceColor::Black, ChessPiecePosition("C7")));
|
||||
this->SetChessPiece(new Pawn(ChessPieceColor::Black, ChessPiecePosition("D7")));
|
||||
this->SetChessPiece(new Pawn(ChessPieceColor::Black, ChessPiecePosition("E7")));
|
||||
this->SetChessPiece(new Pawn(ChessPieceColor::Black, ChessPiecePosition("F7")));
|
||||
this->SetChessPiece(new Pawn(ChessPieceColor::Black, ChessPiecePosition("G7")));
|
||||
this->SetChessPiece(new Pawn(ChessPieceColor::Black, ChessPiecePosition("H7")));
|
||||
|
||||
// white chess pieces
|
||||
this->SetChessPiece(new Rook(ChessPieceColor::White, ChessPiecePosition("A1")));
|
||||
this->SetChessPiece(new Knight(ChessPieceColor::White, ChessPiecePosition("B1")));
|
||||
this->SetChessPiece(new Bishop(ChessPieceColor::White, ChessPiecePosition("C1")));
|
||||
this->SetChessPiece(new Queen(ChessPieceColor::White, ChessPiecePosition("D1")));
|
||||
this->SetChessPiece(new King(ChessPieceColor::White, ChessPiecePosition("E1")));
|
||||
this->SetChessPiece(new Bishop(ChessPieceColor::White, ChessPiecePosition("F1")));
|
||||
this->SetChessPiece(new Knight(ChessPieceColor::White, ChessPiecePosition("G1")));
|
||||
this->SetChessPiece(new Rook(ChessPieceColor::White, ChessPiecePosition("H1")));
|
||||
this->SetChessPiece(new Pawn(ChessPieceColor::White, ChessPiecePosition("A2")));
|
||||
this->SetChessPiece(new Pawn(ChessPieceColor::White, ChessPiecePosition("B2")));
|
||||
this->SetChessPiece(new Pawn(ChessPieceColor::White, ChessPiecePosition("C2")));
|
||||
this->SetChessPiece(new Pawn(ChessPieceColor::White, ChessPiecePosition("D2")));
|
||||
this->SetChessPiece(new Pawn(ChessPieceColor::White, ChessPiecePosition("E2")));
|
||||
this->SetChessPiece(new Pawn(ChessPieceColor::White, ChessPiecePosition("F2")));
|
||||
this->SetChessPiece(new Pawn(ChessPieceColor::White, ChessPiecePosition("G2")));
|
||||
this->SetChessPiece(new Pawn(ChessPieceColor::White, ChessPiecePosition("H2")));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<ChessPieceMove> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the chessboard from a PGN file.
|
||||
* @param filePath The file path of the PGN file to load.
|
||||
* @return The status whether the PGN file was loaded successfully.
|
||||
*/
|
||||
std::string Chessboard::ImportFilePGN(std::string filePath) {
|
||||
std::ifstream pgnFile(filePath);
|
||||
std::regex rgxMetaInfo = std::regex("^[\\[]([A-Za-z]+) [\"](.*)[\"][\\]]$");
|
||||
std::regex rgxMoveInfo = std::regex("(?:([RNBQKP]?)([a-h]?)([1-8]?)(x?)([a-h])([1-8])((?:[=])?[QRBN])?|(O-O(?:-O)?)|(\\(=\\)))([+#!?]| e.p.)*");
|
||||
|
||||
if (pgnFile.is_open()) {
|
||||
Player* playerWhite;
|
||||
Player* playerBlack;
|
||||
int expectedNumberOfMoves = 0;
|
||||
std::vector<std::string> moves;
|
||||
|
||||
for(std::string line; getline(pgnFile, line);) {
|
||||
std::smatch parts;
|
||||
|
||||
if (std::regex_search(line, parts, rgxMetaInfo)) {
|
||||
std::string metaName = parts[1].str();
|
||||
std::string metaValue = parts[2].str();
|
||||
|
||||
if (metaName == "White") {
|
||||
playerWhite = new Player(metaValue);
|
||||
playerWhite->SetColor(ChessPieceColor::White);
|
||||
} else if (metaName == "Black") {
|
||||
playerBlack = new Player(metaValue);
|
||||
playerBlack->SetColor(ChessPieceColor::Black);
|
||||
} else if (metaName == "PlyCount") {
|
||||
expectedNumberOfMoves = std::stoi(metaValue);
|
||||
}
|
||||
} else {
|
||||
for (std::sregex_iterator rgxIterator = std::sregex_iterator(line.begin(), line.end(), rgxMoveInfo); rgxIterator != std::sregex_iterator(); ++rgxIterator) {
|
||||
parts = *rgxIterator;
|
||||
moves.push_back(parts[0].str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pgnFile.close();
|
||||
|
||||
// this was removed because it doesn't match all the time because of wrong data?
|
||||
// https://www.chessgames.com/nodejs/game/viewGamePGN?text=1&gid=2821644 (77 not 78)
|
||||
// if (expectedNumberOfMoves != moves.size()) {
|
||||
// return "moves doesn't match!";
|
||||
// }
|
||||
|
||||
this->players = {playerBlack, playerWhite};
|
||||
this->currentPlayer = playerWhite;
|
||||
|
||||
for (std::string move : moves) {
|
||||
std::string status = this->MoveChessPiece(move);
|
||||
if (status.empty() == false) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
} else {
|
||||
return "file not found!";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(ChessPieceMove 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);
|
||||
}
|
||||
|
||||
ChessPiece* Chessboard::GetChessPiece(ChessPiecePosition* position) {
|
||||
return this->chessboard.at(*position);
|
||||
}
|
||||
|
||||
|
||||
bool Chessboard::IsDraw() {
|
||||
return this->draw.first == true && this->draw.second == true;
|
||||
}
|
||||
bool Chessboard::IsFinished() {
|
||||
return this->game_status.first == true || this->game_status.second == true;
|
||||
}
|
||||
|
||||
std::string 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");
|
||||
|
||||
if (this->IsEmptyField(&fromPositionRook) || this->IsEmptyField(&fromPositionKing)) {
|
||||
return "Castling: Unknown chess piece position!";
|
||||
}
|
||||
|
||||
ChessPiece* chessPieceRook = this->GetChessPiece(&fromPositionRook);
|
||||
ChessPiece* chessPieceKing = this->GetChessPiece(&fromPositionKing);
|
||||
|
||||
if (chessPieceKing == nullptr || chessPieceRook == nullptr) {
|
||||
return "Castling: Chess pieces not found!";
|
||||
}
|
||||
|
||||
if (chessPieceKing->IsKing() == false || chessPieceRook->IsRook() == false) {
|
||||
return "Castling: Wrong chess pieces found!";
|
||||
}
|
||||
|
||||
if (chessPieceKing->IsFirstMove() == false || chessPieceRook->IsFirstMove() == false) {
|
||||
return "Castling: Chess pieces already moved!";
|
||||
}
|
||||
|
||||
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 "Castling: Field " + nextPosition->ToString() + " is under attack!";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nextPosition->ToString() == toPositionKing.ToString()) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return "Castling: Field " + nextPosition->ToString() + " is not empty!";
|
||||
}
|
||||
}
|
||||
|
||||
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 "";
|
||||
}
|
||||
|
||||
std::string Chessboard::MoveChessPiece(std::string move) {
|
||||
ChessPieceMove chessPieceMove = ChessPieceMove(move);
|
||||
|
||||
if (chessPieceMove.IsValidShortNotation() == false) {
|
||||
return move + " not valid!";
|
||||
}
|
||||
|
||||
// reset a started draw offer.
|
||||
if (this->draw.first == true && this->draw.second == false && chessPieceMove.IsDrawOffer() == false) {
|
||||
this->draw = {false, false};
|
||||
} else if (this->draw.second == true && this->draw.first == false && chessPieceMove.IsDrawOffer() == false) {
|
||||
this->draw = {false, false};
|
||||
}
|
||||
|
||||
if (chessPieceMove.IsShortCastling()) {
|
||||
std::string status = this->MoveCastling(this->GetCurrentPlayer()->GetColor(), true);
|
||||
this->draw = {false, false};
|
||||
if (status.empty() == false) {
|
||||
return status;
|
||||
}
|
||||
} else if (chessPieceMove.IsLongCastling()) {
|
||||
std::string status = this->MoveCastling(this->GetCurrentPlayer()->GetColor(), false);
|
||||
this->draw = {false, false};
|
||||
if (status.empty() == false) {
|
||||
return status;
|
||||
}
|
||||
} else if (chessPieceMove.IsDrawOffer()) {
|
||||
if (this->GetCurrentPlayer()->GetColor() == ChessPieceColor::White) {
|
||||
this->draw.second = true;
|
||||
} else {
|
||||
this->draw.first = true;
|
||||
}
|
||||
if (this->draw.first == true && this->draw.second == true) {
|
||||
this->game_status = {true, true};
|
||||
}
|
||||
} else {
|
||||
this->draw = {false, false};
|
||||
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()) {
|
||||
if (field.second->GetNextPositions().count(toChessPiecePosition) == 1) {
|
||||
|
||||
if ((fromPositionFile >= 'a' && fromPositionFile <= 'h') && (fromPositionRank >= 1 && fromPositionRank <= 8)) {
|
||||
if ((field.second->GetPosition().GetFile() == std::toupper(fromPositionFile)) && (field.second->GetPosition().GetRank() == fromPositionRank)) {
|
||||
chessPiece = field.second;
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else if (fromPositionFile >= 'a' && fromPositionFile <= 'h') {
|
||||
if (field.second->GetPosition().GetFile() == std::toupper(fromPositionFile)) {
|
||||
chessPiece = field.second;
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else 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;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return "chess piece not found!";
|
||||
}
|
||||
}
|
||||
|
||||
this->SetToHistory(chessPieceMove);
|
||||
this->UpdateChessPieces();
|
||||
this->SwitchCurrentPlayer();
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
int Chessboard::GetCountMoves() {
|
||||
return this->_historyPositions.size();
|
||||
}
|
||||
|
||||
std::pair<ChessPiecePosition, ChessPiecePosition> Chessboard::GetLastMovePositions() {
|
||||
return this->_historyPositions.back();
|
||||
}
|
||||
|
||||
std::vector<ChessPiece*> Chessboard::GetChessPieces() {
|
||||
std::vector<ChessPiece*> chessPieces;
|
||||
|
||||
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
|
||||
chessPieces.push_back(field.second);
|
||||
}
|
||||
|
||||
return chessPieces;
|
||||
}
|
||||
|
||||
void Chessboard::SetPlayers(Player* playerA, Player* playerB) {
|
||||
this->players = {playerB, playerA};
|
||||
|
||||
this->GetPlayer(ChessPieceColor::White)->SetColor(ChessPieceColor::White);
|
||||
this->GetPlayer(ChessPieceColor::Black)->SetColor(ChessPieceColor::Black);
|
||||
this->currentPlayer = this->GetPlayer(ChessPieceColor::White);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the players to the chessboard.
|
||||
* This function also determines the starting player.
|
||||
* @param Player* playerA The first player.
|
||||
* @param Player* playerB The second player.
|
||||
*/
|
||||
void Chessboard::SetPlayersRdm(Player* playerA, Player* playerB) {
|
||||
std::random_device rngdevice;
|
||||
std::mt19937 generator(rngdevice());
|
||||
std::uniform_int_distribution<int> distr(0, 1);
|
||||
|
||||
if (distr(generator) == 0) { // 0 = A starts (white)
|
||||
this->players = {playerB, playerA};
|
||||
} else { // 1 = B starts (white)
|
||||
this->players = {playerA, playerB};
|
||||
}
|
||||
|
||||
this->GetPlayer(ChessPieceColor::White)->SetColor(ChessPieceColor::White);
|
||||
this->GetPlayer(ChessPieceColor::Black)->SetColor(ChessPieceColor::Black);
|
||||
this->currentPlayer = this->GetPlayer(ChessPieceColor::White);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Update the next positions of all chess pieces.
|
||||
*/
|
||||
void Chessboard::UpdateChessPieces() {
|
||||
for (char chessPieceCode : {'B', 'N', 'P', 'Q', 'R', 'K'}) {
|
||||
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
|
||||
if (field.second->GetChar() == chessPieceCode) {
|
||||
field.second->UpdateNextPositions(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given position is under attack.
|
||||
* @param ChessPiecePosition position The position which should be checked.
|
||||
* @param ChessPieceColor color The color that attacks (opponent color).
|
||||
* @return bool The status whether the given position is under attack.
|
||||
*/
|
||||
bool Chessboard::IsPositionUnderAttack(ChessPiecePosition position, ChessPieceColor color) {
|
||||
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
|
||||
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;
|
||||
}
|
||||
this->chessboard.clear();
|
||||
}
|
||||
|
||||
ChessPiece* Chessboard::GetChessPieceKing(ChessPieceColor color) {
|
||||
ChessPiece* chessPieceKing;
|
||||
|
||||
for (ChessPiece* chessPiece : this->GetChessPieces()) {
|
||||
if (chessPiece->IsKing() && chessPiece->GetColor() == color) {
|
||||
chessPieceKing = chessPiece;
|
||||
}
|
||||
}
|
||||
|
||||
return chessPieceKing;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool Chessboard::ExportFilePGN(std::string filePath) {
|
||||
if (std::filesystem::exists(filePath)) {
|
||||
if (std::filesystem::is_directory(filePath)) {
|
||||
|
||||
filePath = this->GenerateExportFilePath(filePath);
|
||||
std::string player1 = this->GetPlayer(ChessPieceColor::White)->GetName();
|
||||
std::string player2 = this->GetPlayer(ChessPieceColor::Black)->GetName();
|
||||
std::string round;
|
||||
|
||||
// Regex-Pattern, um die Zahl nach "Runde " zu finden
|
||||
std::regex pattern(R"(\bRunde\s+(\d+))");
|
||||
std::smatch match;
|
||||
|
||||
if (std::regex_search(filePath, match, pattern)) {
|
||||
round = match[1]; // Die gefundene Zahl (Gruppe 1)
|
||||
}
|
||||
|
||||
std::ofstream file(filePath);
|
||||
if (file.is_open()) {
|
||||
file << GeneratePGNContent(GetTodaysDate("%Y.%m.%d"), player1, player2, round);
|
||||
file.close();
|
||||
} else {
|
||||
throw "File could not be created and opened";
|
||||
}
|
||||
} else {
|
||||
throw "Path exists but is not a directory";
|
||||
}
|
||||
} else {
|
||||
throw "Path could not be found";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Chessboard::GenerateExportFilePath(std::string& filePath) {
|
||||
if (this->GetCurrentSaveFilename().empty()) {
|
||||
std::string player1 = this->GetPlayer(ChessPieceColor::White)->GetName();
|
||||
std::string player2 = this->GetPlayer(ChessPieceColor::Black)->GetName();
|
||||
|
||||
std::string fileName = GetTodaysDate("%Y%m%d").append(" - ").append(player1).append(" gegen ").append(player2);
|
||||
|
||||
fileName.append(" - Runde ").append(GetRound(filePath, fileName));
|
||||
|
||||
this->SetCurrentSaveFilename(fileName);
|
||||
|
||||
filePath.append(fileName).append(".PGN");
|
||||
return filePath;
|
||||
} else {
|
||||
return filePath.append(this->GetCurrentSaveFilename()).append(".PGN");
|
||||
}
|
||||
}
|
||||
|
||||
std::string Chessboard::GetTodaysDate(std::string format) {
|
||||
// Hole aktuelle Zeit
|
||||
std::time_t now = std::time(nullptr);
|
||||
// Zerlege die Zeit in eine Struktur
|
||||
std::tm* localTime = std::localtime(&now);
|
||||
|
||||
// Formatiere das Datum
|
||||
std::ostringstream dateStream;
|
||||
dateStream << std::put_time(localTime, format.c_str());
|
||||
|
||||
return dateStream.str();
|
||||
}
|
||||
|
||||
// ToDo: Zählt noch nicht richtig
|
||||
std::string Chessboard::GetRound(const std::string& folderPath, const std::string& baseFileName) {
|
||||
size_t count = 0;
|
||||
|
||||
// Escape-Funktion für Regex
|
||||
auto escapeRegex = [](const std::string& str) {
|
||||
std::string escaped;
|
||||
for (char c : str) {
|
||||
if (std::string(".^$|()[]*+?\\").find(c) != std::string::npos) {
|
||||
escaped += '\\';
|
||||
}
|
||||
escaped += c;
|
||||
}
|
||||
return escaped;
|
||||
};
|
||||
|
||||
// Erstelle das Regex-Pattern
|
||||
std::string pattern = "^" + escapeRegex(baseFileName) + R"(\d+\.pgn$)";
|
||||
std::regex regexPattern(pattern, std::regex::icase); // Case-insensitive
|
||||
|
||||
std::cout << "Regex-Pattern: " << pattern << std::endl;
|
||||
|
||||
// Iteriere durch den Ordner
|
||||
for (const auto& entry : std::filesystem::directory_iterator(folderPath)) {
|
||||
if (entry.is_regular_file()) {
|
||||
std::string fileName = entry.path().filename().string();
|
||||
std::cout << "Prüfe Datei: " << fileName << std::endl;
|
||||
|
||||
if (std::regex_match(fileName, regexPattern)) {
|
||||
std::cout << "Datei passt: " << fileName << std::endl;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::to_string(count + 1);
|
||||
}
|
||||
|
||||
std::string Chessboard::GetWinner() {
|
||||
if (this->game_status.first == true && this->game_status.second == true) {
|
||||
return "";
|
||||
} else if (this->game_status.first == true) {
|
||||
return "Black";
|
||||
//return this->GetPlayer(ChessPieceColor::Black)->GetName();
|
||||
} else if (this->game_status.second == true) {
|
||||
return "White";
|
||||
//return this->GetPlayer(ChessPieceColor::White)->GetName();
|
||||
} else {
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
void Chessboard::SetWinner(Player* winner) {
|
||||
if (winner->GetColor() == ChessPieceColor::White) {
|
||||
this->game_status = {false, true};
|
||||
} else {
|
||||
this->game_status = {true, false};
|
||||
}
|
||||
}
|
||||
|
||||
std::string Chessboard::GeneratePGNContent(std::string today, std::string player1, std::string player2, std::string round) {
|
||||
return std::string("[Event \"Private Game\"]\n") // ersten vier werden nicht berücksichtigt
|
||||
.append("[Site \"Horb am Neckar, Germany\"]\n")
|
||||
.append("[Date \"").append(today).append("\"]\n")
|
||||
.append("[Round \"").append(round).append("\"]\n")
|
||||
.append("[White \"").append(player1).append("\"]\n")
|
||||
.append("[Black \"").append(player2).append("\"]\n")
|
||||
.append("[PlyCount \"").append(std::to_string(this->GetHistoryMoves().size())).append("\"]\n")
|
||||
.append("[Result \"").append(GetPGNResult()).append("\"]\n")
|
||||
.append("\n")
|
||||
.append(GenerateMoveList());
|
||||
}
|
||||
|
||||
std::string Chessboard::GenerateMoveList() {
|
||||
std::string result;
|
||||
std::vector<ChessPieceMove> moves = this->GetHistoryMoves();
|
||||
|
||||
if (this->HasMoves()) {
|
||||
for (size_t moveIndex = 0; moveIndex < moves.size() ; ++moveIndex) {
|
||||
if (moveIndex % 6 == 0) {
|
||||
result.append("\n");
|
||||
}
|
||||
if (moveIndex % 2 == 0) {
|
||||
result.append(std::to_string((moveIndex / 2) + 1)).append(".");
|
||||
}
|
||||
result.append(" ").append(moves[moveIndex].ToString()).append(" ");
|
||||
}
|
||||
}
|
||||
|
||||
result.append(GetPGNResult());
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::pair<bool, bool> Chessboard::GetGameState() {
|
||||
return this->game_status;
|
||||
};
|
||||
|
||||
void Chessboard::SetGameState(std::pair<bool, bool> gameState) {
|
||||
this->game_status = gameState;
|
||||
};
|
||||
|
||||
std::string Chessboard::GetPGNResult() {
|
||||
std::pair<bool, bool> state = GetGameState();
|
||||
|
||||
if (state.first == true && state.second == true) {
|
||||
return std::string("1/2-1/2");
|
||||
} else if (state.first == true && state.second == false) {
|
||||
return std::string("0-1");
|
||||
} else if (state.first == false && state.second == true) {
|
||||
return std::string("1-0");
|
||||
} else {
|
||||
return std::string("*");
|
||||
}
|
||||
}
|
||||
|
||||
std::string Chessboard::GetCurrentSaveFilename() {
|
||||
return this->currentSaveFilename;
|
||||
}
|
||||
|
||||
void Chessboard::SetCurrentSaveFilename(std::string currentSaveFilename) {
|
||||
this->currentSaveFilename = currentSaveFilename;
|
||||
}
|
89
src/Chessboard/Chessboard.hpp
Normal file
89
src/Chessboard/Chessboard.hpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#ifndef Chessboard_H
|
||||
#define Chessboard_H
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <filesystem>
|
||||
#include "../Player/Player.hpp"
|
||||
#include "../ChessPieces/ChessPieceColor.hpp"
|
||||
#include "../ChessPieces/ChessPieceMove.hpp"
|
||||
#include "../ChessPieces/ChessPiecePosition.hpp"
|
||||
|
||||
class ChessPiece;
|
||||
|
||||
class Chessboard {
|
||||
private:
|
||||
inline static const std::string save_path = "chessgames/save_files/";
|
||||
|
||||
Player* winner = nullptr;
|
||||
std::string currentSaveFilename;
|
||||
|
||||
// false, false => Laufend
|
||||
// true, false => black win
|
||||
// false, true => white win
|
||||
// true, true => Draw/Stalmate
|
||||
std::pair<bool, bool> game_status = std::make_pair(false, false);
|
||||
|
||||
std::vector<ChessPieceMove> _historyMoves;
|
||||
std::vector<std::pair<ChessPiecePosition, ChessPiecePosition>> _historyPositions;
|
||||
std::map<ChessPiecePosition, ChessPiece*> chessboard;
|
||||
std::pair<Player*, Player*> players;
|
||||
std::pair<bool, bool> draw = std::make_pair(false, false);;
|
||||
Player* currentPlayer;
|
||||
void RemoveChessPiece(ChessPiecePosition position);
|
||||
|
||||
public:
|
||||
~Chessboard();
|
||||
std::vector<ChessPieceMove> GetHistoryMoves();
|
||||
std::vector<std::pair<ChessPiecePosition, ChessPiecePosition>> GetHistoryPositions();
|
||||
std::string GetTodaysDate(std::string format);
|
||||
void SetStartingPosition();
|
||||
void SetChessPiece(ChessPiece* chesspiece);
|
||||
bool IsDraw();
|
||||
bool IsEmptyField(ChessPiecePosition* position);
|
||||
ChessPiece* GetChessPiece(ChessPiecePosition* position);
|
||||
std::vector<ChessPiece*> GetChessPieces();
|
||||
void UpdateChessPieces();
|
||||
std::string MoveCastling(ChessPieceColor color, bool shortCastling);
|
||||
std::string MoveChessPiece(std::string move);
|
||||
void SetToHistory(ChessPieceMove move);
|
||||
void SetToHistory(ChessPiecePosition fromPosition, ChessPiecePosition toPosition);
|
||||
int GetCountMoves();
|
||||
bool HasMoves();
|
||||
std::pair<ChessPiecePosition, ChessPiecePosition> GetLastMovePositions();
|
||||
bool IsCheckmate();
|
||||
bool IsInCheck();
|
||||
bool IsStalemate();
|
||||
bool IsPositionUnderAttack(ChessPiecePosition position, ChessPieceColor color);
|
||||
Player* GetCurrentPlayer();
|
||||
Player* GetOpponentPlayer();
|
||||
void SetPlayersRdm(Player* playerA, Player* playerB);
|
||||
void SetPlayers(Player* playerA, Player* playerB);
|
||||
void SwitchCurrentPlayer();
|
||||
Player* GetPlayer(ChessPieceColor color);
|
||||
ChessPiece* GetChessPieceKing(ChessPieceColor color);
|
||||
std::string ImportFilePGN(std::string filePath);
|
||||
bool ExportFilePGN(std::string filePath);
|
||||
void SetWinner(Player* winner);
|
||||
std::string GeneratePGNContent(std::string today, std::string player1, std::string player2, std::string round);
|
||||
std::string GenerateMoveList();
|
||||
std::pair<bool, bool> GetGameState();
|
||||
void SetGameState(std::pair<bool, bool> gameState);
|
||||
std::string GetPGNResult();
|
||||
std::string GetRound(const std::string& folderPath, const std::string& baseFileName);
|
||||
std::string GetCurrentSaveFilename();
|
||||
void SetCurrentSaveFilename(std::string currentSaveFileName);
|
||||
std::string GenerateExportFilePath(std::string& filePath);
|
||||
std::string GetWinner();
|
||||
bool IsFinished();
|
||||
};
|
||||
|
||||
#endif
|
1
src/Chessboard/ChessboardVisualizer.cpp
Normal file
1
src/Chessboard/ChessboardVisualizer.cpp
Normal file
@@ -0,0 +1 @@
|
||||
//#include "../Chessboard/ChessboardVisualizer.hpp"
|
13
src/Chessboard/ChessboardVisualizer.hpp
Normal file
13
src/Chessboard/ChessboardVisualizer.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
/*#ifndef ChessboardVisualizer_H
|
||||
#define ChessboardVisualizer_H
|
||||
|
||||
#include "../ChessPieces/ChessPiece.hpp"
|
||||
#include "../Chessboard/Chessboard.hpp"
|
||||
|
||||
class ChessboardVisualizer {
|
||||
public:
|
||||
ChessboardVisualizer() = default;
|
||||
virtual void Draw(Chessboard* chessboard) = 0;
|
||||
};
|
||||
|
||||
#endif*/
|
254
src/Controller/MenuController.cpp
Normal file
254
src/Controller/MenuController.cpp
Normal file
@@ -0,0 +1,254 @@
|
||||
#include "MenuController.hpp"
|
||||
#include "../Player/Player.hpp"
|
||||
#include "../Visualizer/BaseVisualizer.hpp"
|
||||
#include "../Visualizer/InstructionsVisualizer.hpp"
|
||||
#include "../Visualizer/ImportVisualizer.hpp"
|
||||
#include "../Visualizer/PlayingViewVisualizer.hpp"
|
||||
#include "../Visualizer/PlaySelectVisualizer.hpp"
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
MenuController::MenuController() {
|
||||
// Konstruktor, falls nötig
|
||||
}
|
||||
|
||||
MenuController::~MenuController() {
|
||||
// Destruktor, falls nötig
|
||||
}
|
||||
|
||||
void MenuController::HandlePlayingNavigation(std::string choice, Chessboard& chessboard, PlayingViewVisualizer& playView, bool isGameOver) {
|
||||
if (choice.empty() == false) {
|
||||
if (choice.at(0) == '$') {
|
||||
if (choice == "$0") {
|
||||
return;
|
||||
} else if (choice == "$1") {
|
||||
HandleThirdOption();
|
||||
} else if (choice == "$2") {
|
||||
chessboard.ExportFilePGN("chessgames/save_games/");
|
||||
} else if (choice == "$3") {
|
||||
if (isGameOver) {
|
||||
playView.SetMessage("Game is over! No moves allowed.");
|
||||
} else {
|
||||
chessboard.SetWinner(chessboard.GetOpponentPlayer());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isGameOver) {
|
||||
playView.SetMessage("Game is over! No moves allowed.");
|
||||
} else {
|
||||
playView.SetMessage(chessboard.MoveChessPiece(choice));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MenuController::HandleFirstOption() {
|
||||
PlaySelectVisualizer playerSelect(3);
|
||||
|
||||
std::string choiceA;
|
||||
std::string choiceB;
|
||||
|
||||
do {
|
||||
playerSelect.SetLabelPlayerName("first player");
|
||||
choiceA = playerSelect.ShowMenu();
|
||||
|
||||
if (choiceA.length() > 40) {
|
||||
choiceA.clear();
|
||||
}
|
||||
} while (choiceA.empty());
|
||||
|
||||
do {
|
||||
playerSelect.SetLabelPlayerName("second player");
|
||||
choiceB = playerSelect.ShowMenu();
|
||||
|
||||
if (choiceB.length() > 40) {
|
||||
choiceB.clear();
|
||||
};
|
||||
} while (choiceB.empty());
|
||||
|
||||
// Spieler initialisieren
|
||||
Player* playerA = new Player(choiceA);
|
||||
Player* playerB = new Player(choiceB);
|
||||
|
||||
// Schachbrett initialisieren
|
||||
Chessboard chessboard;
|
||||
chessboard.SetPlayersRdm(playerA, playerB);
|
||||
chessboard.SetStartingPosition();
|
||||
chessboard.UpdateChessPieces();
|
||||
|
||||
SetChessboard(&chessboard);
|
||||
|
||||
PlayingViewVisualizer playView(&chessboard, 4, 12);
|
||||
|
||||
std::string choiceChessboard;
|
||||
|
||||
do {
|
||||
while (!chessboard.IsCheckmate() && !chessboard.IsStalemate() && !chessboard.IsDraw() && chessboard.IsFinished() == false) {
|
||||
choiceChessboard = playView.ShowMenu();
|
||||
this->HandlePlayingNavigation(choiceChessboard, chessboard, playView, false);
|
||||
}
|
||||
|
||||
std::string winner = chessboard.GetWinner();
|
||||
choiceChessboard = "$0";
|
||||
|
||||
if (winner.empty()) {
|
||||
playView.SetMessage("Game is over! - Draw");
|
||||
} else {
|
||||
playView.SetMessage("Game is over! - " + winner + " has won the game!");
|
||||
}
|
||||
} while (choiceChessboard != "$0");
|
||||
|
||||
do {
|
||||
choiceChessboard = playView.ShowMenu();
|
||||
this->HandlePlayingNavigation(choiceChessboard, chessboard, playView, true);
|
||||
} while (choiceChessboard != "$0");
|
||||
|
||||
|
||||
// while (!chessboard.IsCheckmate() && !chessboard.IsStalemate() && !chessboard.IsDraw() && chessboard.IsFinished() == false) {
|
||||
// playView.DisplayElement();
|
||||
|
||||
// std::string input;
|
||||
// std::cout << "\x1B[u"; // Cursor-Positionierung
|
||||
// std::cin >> input;
|
||||
|
||||
// std::string command = ExtractAfterDollar(input);
|
||||
// if (!command.empty()) {
|
||||
// HandleCommandOptions(command);
|
||||
// if (command == "0") {
|
||||
// return;
|
||||
// }
|
||||
// } else {
|
||||
// std::string status = chessboard.MoveChessPiece(input);
|
||||
// if (status.empty() == false) {
|
||||
// playView.SetMessage(status);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// std::string winner = chessboard.GetWinner();
|
||||
|
||||
// if (winner.empty()) {
|
||||
// playView.SetMessage("Game is over! - Draw");
|
||||
// } else {
|
||||
// playView.SetMessage("Game is over! - " + winner + " has won the game!");
|
||||
// }
|
||||
|
||||
// while (true) {
|
||||
// playView.DisplayElement();
|
||||
// std::string input;
|
||||
// std::cout << "\x1B[u"; // Cursor-Positionierung
|
||||
// std::cin >> input;
|
||||
|
||||
// std::string command = ExtractAfterDollar(input);
|
||||
// if (command == "0") {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
void MenuController::HandleSecondOption() {
|
||||
ImportVisualizer importSelect;
|
||||
|
||||
std::string filePath;
|
||||
|
||||
do {
|
||||
filePath = importSelect.ShowMenu();
|
||||
|
||||
if (std::filesystem::exists(filePath) == false) {
|
||||
filePath.clear();
|
||||
}
|
||||
} while (filePath.empty());
|
||||
|
||||
Chessboard chessboard;
|
||||
chessboard.SetStartingPosition();
|
||||
chessboard.UpdateChessPieces();
|
||||
SetChessboard(&chessboard);
|
||||
|
||||
std::string status = chessboard.ImportFilePGN(filePath);
|
||||
|
||||
PlayingViewVisualizer playView(&chessboard, 4, 12);
|
||||
playView.SetMessage(status);
|
||||
std::string choiceChessboard;
|
||||
|
||||
do {
|
||||
while (!chessboard.IsCheckmate() && !chessboard.IsStalemate() && !chessboard.IsDraw() && chessboard.IsFinished() == false) {
|
||||
choiceChessboard = playView.ShowMenu();
|
||||
this->HandlePlayingNavigation(choiceChessboard, chessboard, playView, false);
|
||||
}
|
||||
|
||||
std::string winner = chessboard.GetWinner();
|
||||
choiceChessboard = "$0";
|
||||
|
||||
if (winner.empty()) {
|
||||
playView.SetMessage("Game is over! - Draw");
|
||||
} else {
|
||||
playView.SetMessage("Game is over! - " + winner + " has won the game!");
|
||||
}
|
||||
} while (choiceChessboard != "$0");
|
||||
|
||||
do {
|
||||
choiceChessboard = playView.ShowMenu();
|
||||
this->HandlePlayingNavigation(choiceChessboard, chessboard, playView, true);
|
||||
} while (choiceChessboard != "$0");
|
||||
}
|
||||
|
||||
// ToDo:: Hier muss aus irgendeinem Grund immer zweimal die Eingabe abgeschickt werden. Why!?
|
||||
void MenuController::HandleThirdOption() {
|
||||
InstructionsVisualizer instructions;
|
||||
instructions.DrawView();
|
||||
}
|
||||
|
||||
// void MenuController::HandleCommandOptions(const std::string& command) {
|
||||
// if (command == "1") {
|
||||
// HandleThirdOption();
|
||||
// return;
|
||||
// } else if (command == "2") {
|
||||
// CHESSBOARD->ExportFilePGN("chessgames/save_games/");
|
||||
// std::cout << "Save game functionality is not implemented yet.\n";
|
||||
// } else if (command == "3") {
|
||||
// CHESSBOARD->SetWinner(CHESSBOARD->GetOpponentPlayer());
|
||||
// return;
|
||||
// } else if (command == "0") { // exit
|
||||
// return;
|
||||
// } else {
|
||||
// std::cout << "Invalid command. Please try again.\n";
|
||||
// }
|
||||
// }
|
||||
|
||||
// std::string MenuController::ExtractAfterDollar(const std::string& userInput) {
|
||||
// if (!userInput.empty() && userInput[0] == '$') {
|
||||
// return userInput.substr(1); // Rückgabe des Teils nach '$'
|
||||
// }
|
||||
// return ""; // Leerer String, wenn kein '$' am Anfang
|
||||
// }
|
||||
|
||||
// Chessboard* MenuController::GetChessboard() {
|
||||
// return CHESSBOARD;
|
||||
// }
|
||||
|
||||
void MenuController::SetChessboard(Chessboard* chessboard) {
|
||||
CHESSBOARD = chessboard;
|
||||
}
|
||||
|
||||
// std::string MenuController::trim(const std::string& str) {
|
||||
// size_t start = str.find_first_not_of(" \t\n\r\f\v");
|
||||
// if (start == std::string::npos) return ""; // Nur Leerzeichen
|
||||
// size_t end = str.find_last_not_of(" \t\n\r\f\v");
|
||||
// return str.substr(start, end - start + 1);
|
||||
// }
|
||||
|
||||
// std::vector<std::string> MenuController::split(const std::string& str, char delimiter, bool trimWhitespace) {
|
||||
// std::vector<std::string> tokens;
|
||||
// std::istringstream stream(str);
|
||||
// std::string token;
|
||||
|
||||
// while (std::getline(stream, token, delimiter)) {
|
||||
// if (trimWhitespace) {
|
||||
// token = trim(token); // Trimme die Tokens, falls gewünscht
|
||||
// }
|
||||
// tokens.push_back(token);
|
||||
// }
|
||||
|
||||
// return tokens;
|
||||
// }
|
29
src/Controller/MenuController.hpp
Normal file
29
src/Controller/MenuController.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef MENU_CONTROLLER_HPP
|
||||
#define MENU_CONTROLLER_HPP
|
||||
|
||||
#include "../Chessboard/Chessboard.hpp"
|
||||
#include "../Visualizer/PlayingViewVisualizer.hpp"
|
||||
#include <string>
|
||||
|
||||
class MenuController {
|
||||
private:
|
||||
std::string ExtractAfterDollar(const std::string& userInput);
|
||||
void HandlePlayingNavigation(std::string choice, Chessboard& chessboard, PlayingViewVisualizer& playView, bool isGameOver);
|
||||
|
||||
Chessboard* CHESSBOARD;
|
||||
|
||||
public:
|
||||
MenuController();
|
||||
~MenuController();
|
||||
|
||||
void HandleFirstOption();
|
||||
void HandleSecondOption();
|
||||
void HandleThirdOption();
|
||||
// void HandleCommandOptions(const std::string& command);
|
||||
// Chessboard* GetChessboard();
|
||||
void SetChessboard(Chessboard* chessboard);
|
||||
// std::string trim(const std::string& str);
|
||||
// std::vector<std::string> split(const std::string& str, char delimiter, bool trimWhitespace = false);
|
||||
};
|
||||
|
||||
#endif // MENU_CONTROLLER_HPP
|
33
src/Player/Player.cpp
Normal file
33
src/Player/Player.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "../Player/Player.hpp"
|
||||
|
||||
/**
|
||||
* Creates a new player with a specific name.
|
||||
* @param name The name of the player.
|
||||
*/
|
||||
Player::Player(std::string name) {
|
||||
this->_name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color of the player.
|
||||
* @return The color of the player.
|
||||
*/
|
||||
ChessPieceColor Player::GetColor() {
|
||||
return this->_color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the player.
|
||||
* @return The name of the player.
|
||||
*/
|
||||
std::string Player::GetName() {
|
||||
return this->_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color of the player.
|
||||
* @param color The color of the player.
|
||||
*/
|
||||
void Player::SetColor(ChessPieceColor color) {
|
||||
this->_color = color;
|
||||
}
|
20
src/Player/Player.hpp
Normal file
20
src/Player/Player.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef Player_H
|
||||
#define Player_H
|
||||
|
||||
#include <string>
|
||||
#include "../ChessPieces/ChessPieceColor.hpp"
|
||||
|
||||
class Player {
|
||||
private:
|
||||
ChessPieceColor _color;
|
||||
std::string _name;
|
||||
|
||||
public:
|
||||
Player() = default;
|
||||
Player(std::string name);
|
||||
ChessPieceColor GetColor();
|
||||
std::string GetName();
|
||||
void SetColor(ChessPieceColor color);
|
||||
};
|
||||
|
||||
#endif
|
281
src/Visualizer/BaseVisualizer.cpp
Normal file
281
src/Visualizer/BaseVisualizer.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
#include "BaseVisualizer.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
void BaseVisualizer::ClearTerminal() {
|
||||
#ifdef __linux__
|
||||
system("clear");
|
||||
#elif _WIN32
|
||||
system("cls");
|
||||
#else
|
||||
system("clear");
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<std::vector<std::string>>* BaseVisualizer::GetDisplayVector() {
|
||||
return &display_vector;
|
||||
}
|
||||
|
||||
size_t BaseVisualizer::CountVisibleCharacters(const std::string& str) {
|
||||
size_t count = 0;
|
||||
size_t i = 0;
|
||||
|
||||
while (i < str.size()) {
|
||||
if (str[i] == '\x1B' && i + 1 < str.size() && str[i + 1] == '[') {
|
||||
// ANSI-Escape-Sequenz erkennen und überspringen
|
||||
i += 2; // Überspringe '\x1B['
|
||||
while (i < str.size() && !std::isalpha(str[i])) {
|
||||
++i; // Fortsetzung der Sequenz überspringen
|
||||
}
|
||||
if (i < str.size() && std::isalpha(str[i])) {
|
||||
++i; // Buchstaben am Ende der Sequenz überspringen
|
||||
}
|
||||
} else {
|
||||
// UTF-8 Startbytes zählen (kein Fortsetzungsbyte)
|
||||
if ((str[i] & 0b11000000) != 0b10000000) {
|
||||
++count;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t BaseVisualizer::GetSumAllCharsFromVector(const std::vector<std::string>& vec) {
|
||||
size_t sum = 0;
|
||||
for (const std::string& str : vec) {
|
||||
sum += CountVisibleCharacters(str);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
void BaseVisualizer::GenerateEmptyLine(const int lengthOfMenu, const bool single) {
|
||||
std::string result = "";
|
||||
for (int i = 0; i < lengthOfMenu; ++i) {
|
||||
if (i == 0) {
|
||||
result += ((single) ? BaseVisualizer::VERTICAL_LINE_SINGLE : BaseVisualizer::VERTICAL_LINE);
|
||||
} else if (i == lengthOfMenu-1) {
|
||||
result += ((single) ? BaseVisualizer::VERTICAL_LINE_SINGLE : BaseVisualizer::VERTICAL_LINE);
|
||||
} else {
|
||||
result += " ";
|
||||
}
|
||||
}
|
||||
display_vector.push_back({result});
|
||||
}
|
||||
|
||||
// ToDo: Auf Max länge bringen
|
||||
void BaseVisualizer::GenerateBoxMenuLine(const size_t length, const std::string& str, const bool single, const size_t padding) {
|
||||
std::string result = ((single) ? BaseVisualizer::VERTICAL_LINE_SINGLE : BaseVisualizer::VERTICAL_LINE) + std::string(padding, ' ') + str;
|
||||
while (CountVisibleCharacters(result) < length-1) {
|
||||
result += " ";
|
||||
}
|
||||
display_vector.push_back({result.append(((single) ? BaseVisualizer::VERTICAL_LINE_SINGLE : BaseVisualizer::VERTICAL_LINE))});
|
||||
}
|
||||
|
||||
void BaseVisualizer::GenerateCenteredString(const size_t widthOfMenu, const std::string& str, const bool single) {
|
||||
std::string result = (single) ? BaseVisualizer::VERTICAL_LINE_SINGLE : BaseVisualizer::VERTICAL_LINE;
|
||||
|
||||
float newLength = widthOfMenu - CountVisibleCharacters(str) - 2;
|
||||
|
||||
int firstHalfLength = std::floor(newLength / 2);
|
||||
int secondHalfLength = std::ceil(newLength / 2);
|
||||
|
||||
for (int i = 0; i < firstHalfLength; ++i) {
|
||||
result += " ";
|
||||
}
|
||||
|
||||
result += str;
|
||||
|
||||
for (int i = 0; i < secondHalfLength; ++i) {
|
||||
result += " ";
|
||||
}
|
||||
|
||||
display_vector.push_back({result+ ((single) ? BaseVisualizer::VERTICAL_LINE_SINGLE : BaseVisualizer::VERTICAL_LINE)});
|
||||
}
|
||||
|
||||
void BaseVisualizer::DisplayElement() {
|
||||
for (const auto& row : display_vector) {
|
||||
for (const auto& cell : row) {
|
||||
std::cout << cell;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
size_t BaseVisualizer::CalculateMaxMenuWidth(const size_t longestStringLength, const size_t padding) {
|
||||
return longestStringLength + 2*padding + 2;
|
||||
}
|
||||
|
||||
void BaseVisualizer::GenerateTopBottomBorder(const size_t totalLength, const bool top, const bool single) {
|
||||
std::string result = (top) ? ((single) ? BaseVisualizer::TOP_LEFT_CORNER_SINGLE : BaseVisualizer::TOP_LEFT_CORNER) : ((single) ? BaseVisualizer::BOTTOM_LEFT_CORNER_SINGLE : BaseVisualizer::BOTTOM_LEFT_CORNER);
|
||||
for (size_t i = 0; i < totalLength-2; ++i) {
|
||||
result += (single) ? BaseVisualizer::HORIZONTAL_LINE_SINGLE : BaseVisualizer::HORIZONTAL_LINE;
|
||||
}
|
||||
display_vector.push_back({result + ((top) ? ((single) ? BaseVisualizer::TOP_RIGHT_CORNER_SINGLE : BaseVisualizer::TOP_RIGHT_CORNER) : ((single) ? BaseVisualizer::BOTTOM_RIGHT_CORNER_SINGLE : BaseVisualizer::BOTTOM_RIGHT_CORNER))});
|
||||
}
|
||||
|
||||
void BaseVisualizer::AddEmptyLines(const size_t lines, const size_t length, const bool sinlge) {
|
||||
for (size_t i = 0; i < lines; ++i) {
|
||||
BaseVisualizer::GenerateEmptyLine(length, sinlge);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseVisualizer::GenerateTableTopBottom(const size_t totalLength, const bool top, const bool single) {
|
||||
int firstHalfLength = std::floor(totalLength / 2);
|
||||
|
||||
std::string result = (top) ? ((single) ? BaseVisualizer::TOP_LEFT_CORNER_SINGLE : BaseVisualizer::TOP_LEFT_CORNER) : ((single) ? BaseVisualizer::BOTTOM_LEFT_CORNER_SINGLE : BaseVisualizer::BOTTOM_LEFT_CORNER);
|
||||
|
||||
for (size_t i = 0; i < firstHalfLength-1; ++i) {
|
||||
result += (single) ? BaseVisualizer::HORIZONTAL_LINE_SINGLE : BaseVisualizer::HORIZONTAL_LINE;
|
||||
}
|
||||
|
||||
result += (top) ? ((single) ? BaseVisualizer::TOP_CROSS_SINGLE : BaseVisualizer::TOP_CROSS) : ((single) ? BaseVisualizer::BOTTOM_CROSS_SINGLE : BaseVisualizer::BOTTOM_CROSS);
|
||||
|
||||
for (size_t i = 0; i < firstHalfLength-1; ++i) {
|
||||
result += (single) ? BaseVisualizer::HORIZONTAL_LINE_SINGLE : BaseVisualizer::HORIZONTAL_LINE;
|
||||
}
|
||||
|
||||
display_vector.push_back({result + ((top) ? ((single) ? BaseVisualizer::TOP_RIGHT_CORNER_SINGLE : BaseVisualizer::TOP_RIGHT_CORNER) : ((single) ? BaseVisualizer::BOTTOM_RIGHT_CORNER_SINGLE : BaseVisualizer::BOTTOM_RIGHT_CORNER))});
|
||||
}
|
||||
|
||||
// Todo: pair anstatt vector
|
||||
void BaseVisualizer::GenerateTableLine(const float length, const std::vector<std::string>& str, const bool single) {
|
||||
int firstHalfLength = std::floor(length / 2);
|
||||
std::string result = ((single) ? BaseVisualizer::VERTICAL_LINE_SINGLE : BaseVisualizer::VERTICAL_LINE);
|
||||
std::string temp;
|
||||
|
||||
for (size_t i = 0; i < 2; ++i) {
|
||||
temp += " " + str[i];
|
||||
|
||||
while (CountVisibleCharacters(temp) < firstHalfLength-1) {
|
||||
temp += " ";
|
||||
}
|
||||
temp += ((single) ? BaseVisualizer::VERTICAL_LINE_SINGLE : BaseVisualizer::VERTICAL_LINE);
|
||||
result += temp;
|
||||
temp = "";
|
||||
}
|
||||
display_vector.push_back({result});
|
||||
}
|
||||
|
||||
void BaseVisualizer::GenerateTableSeperator(const float length, const bool single) {
|
||||
int firstHalfLength = std::floor(length / 2);
|
||||
std::string result = ((single) ? BaseVisualizer::RIGHT_CROSS_SINGLE : BaseVisualizer::RIGHT_CROSS);
|
||||
std::string temp;
|
||||
|
||||
for (size_t i = 0; i < 2; ++i) {
|
||||
while (CountVisibleCharacters(temp) < firstHalfLength-1) {
|
||||
temp += ((single) ? BaseVisualizer::HORIZONTAL_LINE_SINGLE : BaseVisualizer::HORIZONTAL_LINE);
|
||||
}
|
||||
temp += (i != 1) ? ((single) ? BaseVisualizer::CROSS_SINGLE : BaseVisualizer::CROSS) : "";
|
||||
result += temp;
|
||||
temp = "";
|
||||
}
|
||||
result += ((single) ? BaseVisualizer::LEFT_CROSS_SINGLE : BaseVisualizer::LEFT_CROSS);
|
||||
|
||||
display_vector.push_back({result});
|
||||
}
|
||||
|
||||
void BaseVisualizer::GenerateBoxSeperator(const float length, const bool single) {
|
||||
std::string result = ((single) ? BaseVisualizer::RIGHT_CROSS_SINGLE : BaseVisualizer::RIGHT_CROSS);
|
||||
|
||||
while (CountVisibleCharacters(result) < length-1) {
|
||||
result += ((single) ? BaseVisualizer::HORIZONTAL_LINE_SINGLE : BaseVisualizer::HORIZONTAL_LINE);
|
||||
}
|
||||
|
||||
result += ((single) ? BaseVisualizer::LEFT_CROSS_SINGLE : BaseVisualizer::LEFT_CROSS);
|
||||
|
||||
display_vector.push_back({result});
|
||||
}
|
||||
|
||||
void BaseVisualizer::SetConsoleColor(Colors foreground, Colors background) {
|
||||
const std::string SEQ_FOREGROUND = "\x1b[3";
|
||||
const std::string SEQ_FOREGROUND_LIGHT = "\x1b[9";
|
||||
const std::string SEQ_BACKGROUND = "\x1b[4";
|
||||
const std::string SEQ_BACKGROUND_LIGHT = "\x1b[10";
|
||||
|
||||
// set foreground color
|
||||
switch (foreground) {
|
||||
case BLACK:
|
||||
case RED:
|
||||
case GREEN:
|
||||
case YELLOW:
|
||||
case BLUE:
|
||||
case MAGENTA:
|
||||
case CYAN:
|
||||
case WHITE:
|
||||
std::cout << SEQ_FOREGROUND << foreground << "m";
|
||||
break;
|
||||
case LIGHT_BLACK:
|
||||
case LIGHT_RED:
|
||||
case LIGHT_GREEN:
|
||||
case LIGHT_YELLOW:
|
||||
case LIGHT_BLUE:
|
||||
case LIGHT_MAGENTA:
|
||||
case LIGHT_CYAN:
|
||||
case LIGHT_WHITE:
|
||||
std::cout << SEQ_FOREGROUND_LIGHT << foreground << "m";
|
||||
break;
|
||||
case DEFAULT:
|
||||
default:
|
||||
std::cout << SEQ_FOREGROUND << foreground << "m";
|
||||
break;
|
||||
}
|
||||
|
||||
// set background color
|
||||
switch (background) {
|
||||
case BLACK:
|
||||
case RED:
|
||||
case GREEN:
|
||||
case YELLOW:
|
||||
case BLUE:
|
||||
case MAGENTA:
|
||||
case CYAN:
|
||||
case WHITE:
|
||||
std::cout << SEQ_BACKGROUND << background << "m";
|
||||
break;
|
||||
case LIGHT_BLACK:
|
||||
case LIGHT_RED:
|
||||
case LIGHT_GREEN:
|
||||
case LIGHT_YELLOW:
|
||||
case LIGHT_BLUE:
|
||||
case LIGHT_MAGENTA:
|
||||
case LIGHT_CYAN:
|
||||
case LIGHT_WHITE:
|
||||
std::cout << SEQ_BACKGROUND_LIGHT << background << "m";
|
||||
break;
|
||||
case DEFAULT:
|
||||
default:
|
||||
std::cout << SEQ_BACKGROUND << background << "m";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t BaseVisualizer::FindMaxLength(const std::vector<std::string> vec) {
|
||||
size_t max = 0;
|
||||
size_t currentLength = 0;
|
||||
for (const std::string& str : vec) {
|
||||
currentLength = CountVisibleCharacters(str);
|
||||
(currentLength > max) ? max = CountVisibleCharacters(str) : max;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
std::pair<int, int> BaseVisualizer::GetCursorPosition() {
|
||||
std::cout << "\033[6n"; // ANSI-Escape-Sequenz, um die Cursorposition abzufragen
|
||||
std::fflush(stdout); // Ausgabe sofort erzwingen
|
||||
|
||||
int row, col;
|
||||
|
||||
// Eingabe im Format "\033[row;colR" lesen
|
||||
char buf[32];
|
||||
if (std::fgets(buf, sizeof(buf), stdin)) {
|
||||
if (std::sscanf(buf, "\033[%d;%dR", &row, &col) != 2) {
|
||||
row = col = -1; // Fehlerbehandlung
|
||||
}
|
||||
} else {
|
||||
row = col = -1; // Fehlerbehandlung
|
||||
}
|
||||
|
||||
return std::pair<int, int>(row, col); // Rückgabe als Tupel
|
||||
};
|
100
src/Visualizer/BaseVisualizer.hpp
Normal file
100
src/Visualizer/BaseVisualizer.hpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef BASEVISUALIZER_H
|
||||
#define BASEVISUALIZER_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
enum Colors {
|
||||
BLACK = 0, RED = 1, GREEN = 2, YELLOW = 3, BLUE = 4, MAGENTA = 5, CYAN = 6, WHITE = 7,
|
||||
LIGHT_BLACK = 10, LIGHT_RED = 11, LIGHT_GREEN = 12, LIGHT_YELLOW = 13, LIGHT_BLUE = 14,
|
||||
LIGHT_MAGENTA = 15, LIGHT_CYAN = 16, LIGHT_WHITE = 17, DEFAULT = 9
|
||||
};
|
||||
|
||||
class BaseVisualizer {
|
||||
protected:
|
||||
inline static const std::string TOP_LEFT_CORNER = "\u2554";
|
||||
inline static const std::string TOP_RIGHT_CORNER = "\u2557";
|
||||
inline static const std::string HORIZONTAL_LINE = "\u2550";
|
||||
inline static const std::string VERTICAL_LINE = "\u2551";
|
||||
inline static const std::string BOTTOM_LEFT_CORNER = "\u255A";
|
||||
inline static const std::string BOTTOM_RIGHT_CORNER = "\u255D";
|
||||
inline static const std::string RIGHT_CROSS = "\u2560";
|
||||
inline static const std::string LEFT_CROSS = "\u2563";
|
||||
inline static const std::string CROSS = "\u256C";
|
||||
inline static const std::string TOP_CROSS = "\u2566";
|
||||
inline static const std::string BOTTOM_CROSS = "\u2569";
|
||||
|
||||
inline static const std::string TOP_LEFT_CORNER_SINGLE = "\u250C";
|
||||
inline static const std::string TOP_RIGHT_CORNER_SINGLE = "\u2510";
|
||||
inline static const std::string HORIZONTAL_LINE_SINGLE = "\u2500";
|
||||
inline static const std::string VERTICAL_LINE_SINGLE = "\u2502";
|
||||
inline static const std::string BOTTOM_LEFT_CORNER_SINGLE = "\u2514";
|
||||
inline static const std::string BOTTOM_RIGHT_CORNER_SINGLE = "\u2518";
|
||||
inline static const std::string RIGHT_CROSS_SINGLE = "\u251C";
|
||||
inline static const std::string LEFT_CROSS_SINGLE = "\u2524";
|
||||
inline static const std::string CROSS_SINGLE = "\u253C";
|
||||
inline static const std::string TOP_CROSS_SINGLE = "\u252C";
|
||||
inline static const std::string BOTTOM_CROSS_SINGLE = "\u2534";
|
||||
|
||||
const size_t MAX_MENU_WIDTH;
|
||||
const size_t PADDING;
|
||||
|
||||
std::vector<std::vector<std::string>> display_vector;
|
||||
|
||||
void GenerateEmptyLine(const int lengthOfMenu, const bool single);
|
||||
void GenerateBoxMenuLine(const size_t length, const std::string& str, const bool single, const size_t padding);
|
||||
void GenerateCenteredString(const size_t widthOfMenu, const std::string& str, const bool single);
|
||||
size_t CalculateMaxMenuWidth(const size_t longestStringLength, const size_t padding);
|
||||
size_t FindMaxLength(const std::vector<std::string> vec);
|
||||
void GenerateTopBottomBorder(const size_t totalLength, const bool top, const bool single);
|
||||
void AddEmptyLines(const size_t lines, const size_t length, const bool sinlge);
|
||||
void GenerateTableTopBottom(const size_t totalLength, const bool top, const bool single);
|
||||
void GenerateTableLine(const float length, const std::vector<std::string>& str, const bool single);
|
||||
void GenerateTableSeperator(const float length, const bool single);
|
||||
void GenerateBoxSeperator(const float length, const bool single);
|
||||
void SetConsoleColor(Colors foreground, Colors background);
|
||||
std::pair<int, int> GetCursorPosition();
|
||||
|
||||
static size_t GetSumAllCharsFromVector(const std::vector<std::string>& vec);
|
||||
static size_t CountVisibleCharacters(const std::string& str);
|
||||
|
||||
private:
|
||||
virtual void GenerateElement() = 0;
|
||||
|
||||
public:
|
||||
virtual ~BaseVisualizer() = default;
|
||||
static void ClearTerminal();
|
||||
void DisplayElement();
|
||||
std::vector<std::vector<std::string>>* GetDisplayVector();
|
||||
BaseVisualizer(size_t longestStringLength, size_t padding) : MAX_MENU_WIDTH(BaseVisualizer::CalculateMaxMenuWidth(longestStringLength, padding)), PADDING(padding) {}
|
||||
};
|
||||
|
||||
#endif //BASEVISUALIZER_H
|
||||
|
||||
/**
|
||||
*
|
||||
ChessboardVisualizer.cpp
|
||||
HistorieVisualizer.cpp
|
||||
CommandVisualizer.cpp
|
||||
|
||||
PlayVisualizer.cpp {
|
||||
ChessboardVisualizer.display_vector
|
||||
|
||||
|
||||
HistorieVisualizer.display_vector;
|
||||
|
||||
CommandVisualizer.display_vector;
|
||||
|
||||
st::cout << ChessboardVisualizer.display_vector[i] (isEmpty) ? << std:endl : << Padding << Menus.display_vector[i] << std:endl;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
166
src/Visualizer/ChessboardVisualizer.cpp
Normal file
166
src/Visualizer/ChessboardVisualizer.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
#include "ChessboardVisualizer.hpp"
|
||||
|
||||
size_t ChessboardVisualizer::GetMaxBoardWidth() {
|
||||
std::string temp;
|
||||
|
||||
temp.append(3, ' ');
|
||||
|
||||
temp += BaseVisualizer::TOP_LEFT_CORNER;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
for (int j = 0; j < 7; j++) {
|
||||
temp += BaseVisualizer::HORIZONTAL_LINE;
|
||||
}
|
||||
}
|
||||
temp += BaseVisualizer::TOP_RIGHT_CORNER;
|
||||
|
||||
return BaseVisualizer::CountVisibleCharacters(temp);
|
||||
}
|
||||
|
||||
void ChessboardVisualizer::GenerateTopBottomBorder(bool top, bool single) {
|
||||
std::string temp;
|
||||
|
||||
temp.append(3, ' '); // ToDo: Padding?
|
||||
|
||||
temp += (top)
|
||||
? ((single)
|
||||
? BaseVisualizer::TOP_LEFT_CORNER_SINGLE
|
||||
: BaseVisualizer::TOP_LEFT_CORNER)
|
||||
: ((single)
|
||||
? BaseVisualizer::BOTTOM_LEFT_CORNER_SINGLE
|
||||
: BaseVisualizer::BOTTOM_LEFT_CORNER);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
for (int j = 0; j < 7; j++) {
|
||||
temp += (single) ? BaseVisualizer::HORIZONTAL_LINE_SINGLE : BaseVisualizer::HORIZONTAL_LINE;
|
||||
}
|
||||
}
|
||||
display_vector.push_back({temp + ((top) ? ((single) ? BaseVisualizer::TOP_RIGHT_CORNER_SINGLE : BaseVisualizer::TOP_RIGHT_CORNER) : ((single) ? BaseVisualizer::BOTTOM_RIGHT_CORNER_SINGLE : BaseVisualizer::BOTTOM_RIGHT_CORNER))});
|
||||
}
|
||||
|
||||
void ChessboardVisualizer::GenerateElement() {
|
||||
BaseVisualizer::ClearTerminal();
|
||||
|
||||
GeneratePlayers();
|
||||
|
||||
GenerateChessboard();
|
||||
|
||||
//GenerateDisplayVector();
|
||||
}
|
||||
|
||||
void ChessboardVisualizer::GeneratePlayers() {
|
||||
std::string result;
|
||||
result.append(3, ' ');
|
||||
display_vector.push_back({result + CHESSBOARD->GetPlayer(ChessPieceColor::White)->GetName()
|
||||
+ " vs. " + CHESSBOARD->GetPlayer(ChessPieceColor::Black)->GetName()});
|
||||
}
|
||||
|
||||
void ChessboardVisualizer::GenerateChessboard() {
|
||||
std::vector<std::string> temp;
|
||||
for (int rank = 8; rank >= 1; rank--) {
|
||||
if (rank == 8) GenerateTopBottomBorder(true, true);
|
||||
for (int heightOfField = 0; heightOfField < 3; heightOfField++) {
|
||||
for (char file = 'A'; file <= 'H'; file++) {
|
||||
if (file == 'A') {
|
||||
temp.push_back((heightOfField == 1) ? " " + std::to_string(rank) + " " : std::string(3, ' '));
|
||||
temp.push_back(BaseVisualizer::VERTICAL_LINE_SINGLE);
|
||||
}
|
||||
|
||||
temp.push_back(GenerateBoardField(file, rank, heightOfField));
|
||||
|
||||
if (file == 'H') {
|
||||
temp.push_back(BaseVisualizer::VERTICAL_LINE_SINGLE);
|
||||
}
|
||||
}
|
||||
display_vector.push_back(temp);
|
||||
temp.clear();
|
||||
}
|
||||
if (rank == 1) {
|
||||
GenerateTopBottomBorder(false, true);
|
||||
GenerateFiles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string ChessboardVisualizer::GenerateBoardField(char file, int rank, int height) {
|
||||
ChessPiecePosition* position = new ChessPiecePosition(file, rank);
|
||||
if (height == 1 && !CHESSBOARD->IsEmptyField(position)) {
|
||||
ChessPiece* piece = CHESSBOARD->GetChessPiece(position);
|
||||
return std::string(3, ' ') + piece->GetUnicode() + std::string(3, ' ');
|
||||
}
|
||||
return std::string(7, ' ');
|
||||
}
|
||||
|
||||
void ChessboardVisualizer::GenerateFiles() {
|
||||
std::string temp;
|
||||
|
||||
for (char file = 'A'; file <= 'H'; file++) {
|
||||
if (file == 'A') {
|
||||
temp += (std::string(4, ' '));
|
||||
}
|
||||
|
||||
temp += (std::string(3, ' ') + std::string(1, file) + std::string(3, ' '));
|
||||
|
||||
if (file == 'H') {
|
||||
temp += " ";
|
||||
}
|
||||
}
|
||||
display_vector.push_back({temp});
|
||||
}
|
||||
|
||||
|
||||
// board[2] bis board[25] Feld des Schachbretts
|
||||
// in vectoren von 2 bis 25 index von 2 bis 9
|
||||
void ChessboardVisualizer::DisplayElement() {
|
||||
size_t size;
|
||||
std::string temp;
|
||||
for (int row = 0; row < display_vector.size(); ++row) {
|
||||
for (int column = 0; column < display_vector[row].size(); ++column) {
|
||||
if (row >= 2 && row <= 25 && column >= 2 && column <= 9) {
|
||||
bool isSecondRowOfField = (row % 3 == 2);
|
||||
bool isBlackWhite = (column % 2 == row % 2);
|
||||
|
||||
if ((row - 1) % 3 == 0) { // Change after 3 rows
|
||||
if (isSecondRowOfField) {
|
||||
this->SetConsoleColor(isBlackWhite ? WHITE : BLACK, isBlackWhite ? BLACK : WHITE);
|
||||
} else {
|
||||
this->SetConsoleColor(isBlackWhite ? BLACK : WHITE, isBlackWhite ? WHITE : BLACK);
|
||||
}
|
||||
} else {
|
||||
if (isSecondRowOfField) {
|
||||
this->SetConsoleColor(isBlackWhite ? BLACK : WHITE, isBlackWhite ? WHITE : BLACK);
|
||||
} else {
|
||||
this->SetConsoleColor(isBlackWhite ? WHITE : BLACK, isBlackWhite ? BLACK : WHITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << display_vector[row][column];
|
||||
this->SetConsoleColor(DEFAULT, DEFAULT);
|
||||
}
|
||||
this->SetConsoleColor(DEFAULT, DEFAULT);
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/*void ChessboardVisualizer::GenerateDisplayVector() {
|
||||
std::string temp;
|
||||
size_t size;
|
||||
|
||||
for (auto& row : draw_vector) {
|
||||
if (row.size() > 1) {
|
||||
for (const auto& element : row) {
|
||||
temp += element;
|
||||
}
|
||||
display_vector.push_back(FillStringToMaxLength(temp));
|
||||
temp = "";
|
||||
} else {
|
||||
display_vector.push_back(FillStringToMaxLength(row[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string ChessboardVisualizer::FillStringToMaxLength(std::string& str) {
|
||||
size_t size = BaseVisualizer::CountVisibleCharacters(str);
|
||||
return ((size == MAX_MENU_WIDTH)
|
||||
? str
|
||||
: str.append(std::string(MAX_MENU_WIDTH - size, ' ')));
|
||||
}*/
|
32
src/Visualizer/ChessboardVisualizer.hpp
Normal file
32
src/Visualizer/ChessboardVisualizer.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef CHESSBOARDVISUALIZER_HPP
|
||||
#define CHESSBOARDVISUALIZER_HPP
|
||||
|
||||
#include "BaseVisualizer.hpp"
|
||||
#include "../Chessboard/Chessboard.hpp"
|
||||
#include "../Chesspieces/Chesspiece.hpp"
|
||||
|
||||
class ChessboardVisualizer : public BaseVisualizer {
|
||||
|
||||
private:
|
||||
Chessboard* CHESSBOARD;
|
||||
std::vector<std::vector<std::string>> draw_vector;
|
||||
|
||||
static size_t GetMaxBoardWidth();
|
||||
|
||||
void GenerateTopBottomBorder(bool top, bool single);
|
||||
void GenerateElement() override;
|
||||
void GeneratePlayers();
|
||||
void GenerateChessboard();
|
||||
void GenerateFiles();
|
||||
std::string GenerateBoardField(char file, int rank, int height);
|
||||
void GenerateDisplayVector();
|
||||
std::string FillStringToMaxLength(std::string& str);
|
||||
|
||||
public:
|
||||
ChessboardVisualizer(Chessboard* chessboard, size_t padding) : BaseVisualizer(ChessboardVisualizer::GetMaxBoardWidth(), padding), CHESSBOARD(chessboard) {
|
||||
ChessboardVisualizer::GenerateElement();
|
||||
};
|
||||
void DisplayElement();
|
||||
};
|
||||
|
||||
#endif //CHESSBOARDVISUALIZER_HPP
|
20
src/Visualizer/CommandMenuVisualizer.cpp
Normal file
20
src/Visualizer/CommandMenuVisualizer.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "CommandMenuVisualizer.hpp"
|
||||
|
||||
// ToDo: Cursor wird nicht an die richtige Stelle gesetzt
|
||||
void CommandMenuVisualizer::GenerateElement() {
|
||||
BaseVisualizer::display_vector.push_back({" Commands"});
|
||||
BaseVisualizer::GenerateTopBottomBorder(MAX_MENU_WIDTH, true, true);
|
||||
for (const auto& content : menuContent) {
|
||||
BaseVisualizer::GenerateBoxMenuLine(MAX_MENU_WIDTH, content, true, PADDING);
|
||||
}
|
||||
BaseVisualizer::GenerateBoxSeperator(MAX_MENU_WIDTH, true);
|
||||
std::string str_temp = ((CHESSBOARD->GetCurrentPlayer()->GetColor() == ChessPieceColor::White) ? "White" : "Black");
|
||||
BaseVisualizer::GenerateBoxMenuLine(MAX_MENU_WIDTH, "Move [" + str_temp + "] : \x1B[s", true, PADDING);
|
||||
BaseVisualizer::GenerateTopBottomBorder(MAX_MENU_WIDTH, false, true);
|
||||
display_vector.push_back({" "});
|
||||
BaseVisualizer::display_vector.push_back({std::string(" ").append(GetMessage())});
|
||||
}
|
||||
|
||||
std::string CommandMenuVisualizer::GetMessage() {
|
||||
return this->message;
|
||||
}
|
32
src/Visualizer/CommandMenuVisualizer.hpp
Normal file
32
src/Visualizer/CommandMenuVisualizer.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef COMMANDMENUVISUALIZER_HPP
|
||||
#define COMMANDMENUVISUALIZER_HPP
|
||||
|
||||
#include "BaseVisualizer.hpp"
|
||||
#include "../Chessboard/Chessboard.hpp"
|
||||
|
||||
class CommandMenuVisualizer : public BaseVisualizer {
|
||||
private:
|
||||
inline static const std::vector<std::string> menuContent = {
|
||||
"To execute command: $<command_no>",
|
||||
"",
|
||||
"1 - Instructions",
|
||||
"2 - Save",
|
||||
"3 - Resign",
|
||||
"",
|
||||
"0 - Exit",
|
||||
};
|
||||
|
||||
Chessboard* CHESSBOARD;
|
||||
|
||||
std::string message;
|
||||
|
||||
void GenerateElement() override;
|
||||
|
||||
public:
|
||||
CommandMenuVisualizer(Chessboard* chessboard, size_t padding, std::string msg) : BaseVisualizer(CommandMenuVisualizer::FindMaxLength(menuContent), padding), CHESSBOARD(chessboard), message(msg) {
|
||||
CommandMenuVisualizer::GenerateElement();
|
||||
}
|
||||
std::string GetMessage();
|
||||
};
|
||||
|
||||
#endif //COMMANDMENUVISUALIZER_HPP
|
52
src/Visualizer/HistorieVisualizer.cpp
Normal file
52
src/Visualizer/HistorieVisualizer.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "HistorieVisualizer.hpp"
|
||||
#include <algorithm>
|
||||
#include "../ChessPieces/ChessPieceMove.hpp"
|
||||
|
||||
void HistorieVisualizer::GenerateElement() {
|
||||
BaseVisualizer::display_vector.push_back({"Historie"});
|
||||
BaseVisualizer::GenerateTableTopBottom(MAX_MENU_WIDTH, true, true);
|
||||
|
||||
std::string playerName1 = HistorieVisualizer::PLAYER1;
|
||||
std::string playerName2 = HistorieVisualizer::PLAYER2;
|
||||
|
||||
if (playerName1.length() > 14) {
|
||||
playerName1 = playerName1.substr(0, 11) + "...";
|
||||
}
|
||||
if(playerName2.length() > 14) {
|
||||
playerName2 = playerName2.substr(0, 11) + "...";
|
||||
}
|
||||
|
||||
BaseVisualizer::GenerateTableLine(MAX_MENU_WIDTH, {playerName1, playerName2}, true);
|
||||
BaseVisualizer::GenerateTableSeperator(MAX_MENU_WIDTH, true);
|
||||
|
||||
std::vector<ChessPieceMove> moves = this->CHESSBOARD->GetHistoryMoves();
|
||||
|
||||
if (moves.size() % 2 == 0) {
|
||||
moves = std::vector<ChessPieceMove>(moves.end() - std::min<int>(moves.size(), 10), moves.end());
|
||||
} else {
|
||||
moves = std::vector<ChessPieceMove>(moves.end() - std::min<int>(moves.size(), 9), moves.end());
|
||||
}
|
||||
|
||||
std::vector<std::string> rowMoves;
|
||||
int rows = 0;
|
||||
|
||||
for (int moveIndex = 0; moveIndex < moves.size(); moveIndex++) {
|
||||
if (moveIndex % 2 == 0) rowMoves.clear();
|
||||
rowMoves.push_back(moves[moveIndex].ToString());
|
||||
|
||||
if (moveIndex % 2 == 1) {
|
||||
BaseVisualizer::GenerateTableLine(MAX_MENU_WIDTH, rowMoves, true);
|
||||
rows++;
|
||||
} else if (moveIndex == moves.size() - 1) {
|
||||
rowMoves.push_back("");
|
||||
BaseVisualizer::GenerateTableLine(MAX_MENU_WIDTH, rowMoves, true);
|
||||
rows++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int rowIndex = rows; rowIndex < 5; rowIndex++) {
|
||||
BaseVisualizer::GenerateTableLine(MAX_MENU_WIDTH, {"", ""}, true);
|
||||
}
|
||||
|
||||
BaseVisualizer::GenerateTableTopBottom(MAX_MENU_WIDTH, false, true);
|
||||
}
|
27
src/Visualizer/HistorieVisualizer.hpp
Normal file
27
src/Visualizer/HistorieVisualizer.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef HISTORIEVISUALIZER_HPP
|
||||
#define HISTORIEVISUALIZER_HPP
|
||||
|
||||
#include "BaseVisualizer.hpp"
|
||||
#include "../Chessboard/Chessboard.hpp"
|
||||
|
||||
class HistorieVisualizer : public BaseVisualizer {
|
||||
private:
|
||||
const std::string PLAYER1;
|
||||
const std::string PLAYER2;
|
||||
|
||||
Chessboard* CHESSBOARD;
|
||||
|
||||
void GenerateElement() override;
|
||||
|
||||
public:
|
||||
HistorieVisualizer(Chessboard* chessbord,const size_t menuWidth, const size_t padding) :
|
||||
BaseVisualizer(menuWidth, padding),
|
||||
CHESSBOARD(chessbord),
|
||||
PLAYER1(chessbord->GetPlayer(ChessPieceColor::White)->GetName()),
|
||||
PLAYER2(chessbord->GetPlayer(ChessPieceColor::Black)->GetName())
|
||||
{
|
||||
HistorieVisualizer::GenerateElement();
|
||||
};
|
||||
};
|
||||
|
||||
#endif //HISTORIEVISUALIZER_HPP
|
36
src/Visualizer/ImportVisualizer.cpp
Normal file
36
src/Visualizer/ImportVisualizer.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "ImportVisualizer.hpp"
|
||||
|
||||
void ImportVisualizer::GenerateElement() {
|
||||
// BaseVisualizer::GenerateTopBottomBorder(MAX_MENU_WIDTH, true, false);
|
||||
// BaseVisualizer::AddEmptyLines(2, MAX_MENU_WIDTH, false);
|
||||
// for (const auto& content : menuContent) {
|
||||
// BaseVisualizer::GenerateBoxMenuLine(MAX_MENU_WIDTH, content, false, PADDING);
|
||||
// }
|
||||
// BaseVisualizer::AddEmptyLines(2, MAX_MENU_WIDTH, false);
|
||||
// BaseVisualizer::GenerateBoxMenuLine(MAX_MENU_WIDTH, "You can get a lot of PGN files from chessgames.com.", false, PADDING);
|
||||
// BaseVisualizer::AddEmptyLines(5, MAX_MENU_WIDTH, false);
|
||||
// BaseVisualizer::GenerateTopBottomBorder(MAX_MENU_WIDTH, false, false);
|
||||
}
|
||||
void ImportVisualizer::DrawView() {
|
||||
BaseVisualizer::ClearTerminal();
|
||||
this->display_vector.clear();
|
||||
BaseVisualizer::GenerateTopBottomBorder(MAX_MENU_WIDTH, true, false);
|
||||
BaseVisualizer::GenerateEmptyLine(MAX_MENU_WIDTH, false);
|
||||
BaseVisualizer::GenerateBoxMenuLine(MAX_MENU_WIDTH, "Please enter a path to a PGN (Portable Game Notation) file:", false, PADDING);
|
||||
BaseVisualizer::GenerateBoxMenuLine(MAX_MENU_WIDTH, "\x1B[s", false, PADDING);
|
||||
BaseVisualizer::GenerateEmptyLine(MAX_MENU_WIDTH, false);
|
||||
BaseVisualizer::GenerateEmptyLine(MAX_MENU_WIDTH, false);
|
||||
BaseVisualizer::GenerateBoxMenuLine(MAX_MENU_WIDTH, "On https://chessgames.com/ you will find many more chess", false, PADDING);
|
||||
BaseVisualizer::GenerateBoxMenuLine(MAX_MENU_WIDTH, "games in PGN format, to import into TurboSchach.", false, PADDING);
|
||||
BaseVisualizer::GenerateEmptyLine(MAX_MENU_WIDTH, false);
|
||||
BaseVisualizer::GenerateTopBottomBorder(MAX_MENU_WIDTH, false, false);
|
||||
BaseVisualizer::DisplayElement();
|
||||
std::cout << "\x1B[u";
|
||||
}
|
||||
|
||||
std::string ImportVisualizer::ShowMenu() {
|
||||
this->DrawView();
|
||||
std::string choice;
|
||||
std::getline(std::cin, choice);
|
||||
return choice;
|
||||
}
|
22
src/Visualizer/ImportVisualizer.hpp
Normal file
22
src/Visualizer/ImportVisualizer.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef ImportVisualizer_HPP
|
||||
#define ImportVisualizer_HPP
|
||||
|
||||
#include "BaseVisualizer.hpp"
|
||||
|
||||
class ImportVisualizer : public BaseVisualizer {
|
||||
private:
|
||||
inline static const std::vector<std::string> menuContent = {
|
||||
"Please enter the path to the PGN file:",
|
||||
"\x1B[s"
|
||||
};
|
||||
void GenerateElement() override;
|
||||
|
||||
public:
|
||||
ImportVisualizer() : BaseVisualizer(60, 4) {
|
||||
ImportVisualizer::GenerateElement();
|
||||
};
|
||||
std::string ShowMenu();
|
||||
void DrawView();
|
||||
};
|
||||
|
||||
#endif //INSTRUCTIONSVISUALIZER_HPP
|
20
src/Visualizer/InstructionsVisualizer.cpp
Normal file
20
src/Visualizer/InstructionsVisualizer.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "InstructionsVisualizer.hpp"
|
||||
|
||||
void InstructionsVisualizer::GenerateElement() {
|
||||
BaseVisualizer::GenerateTopBottomBorder(MAX_MENU_WIDTH, true, false);
|
||||
BaseVisualizer::AddEmptyLines(2, MAX_MENU_WIDTH, false);
|
||||
for (const auto& content : menuContent) {
|
||||
BaseVisualizer::GenerateBoxMenuLine(MAX_MENU_WIDTH, content, false, PADDING);
|
||||
}
|
||||
BaseVisualizer::AddEmptyLines(2, MAX_MENU_WIDTH, false);
|
||||
BaseVisualizer::GenerateBoxMenuLine(MAX_MENU_WIDTH, "Press enter to continue: \x1B[s", false, PADDING);
|
||||
BaseVisualizer::AddEmptyLines(2, MAX_MENU_WIDTH, false);
|
||||
BaseVisualizer::GenerateTopBottomBorder(MAX_MENU_WIDTH, false, false);
|
||||
}
|
||||
void InstructionsVisualizer::DrawView() {
|
||||
BaseVisualizer::ClearTerminal();
|
||||
BaseVisualizer::DisplayElement();
|
||||
std::cout << "\x1B[u";
|
||||
std::string choice;
|
||||
std::getline(std::cin, choice);
|
||||
}
|
60
src/Visualizer/InstructionsVisualizer.hpp
Normal file
60
src/Visualizer/InstructionsVisualizer.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef INSTRUCTIONSVISUALIZER_HPP
|
||||
#define INSTRUCTIONSVISUALIZER_HPP
|
||||
|
||||
#include "BaseVisualizer.hpp"
|
||||
|
||||
class InstructionsVisualizer : public BaseVisualizer {
|
||||
private:
|
||||
inline static const std::vector<std::string> menuContent = {
|
||||
"1. Algebraische Notation",
|
||||
"",
|
||||
"Um die Figuren auf dem Schachbrett zu bewegen, wird die kurze algebraische Notation",
|
||||
"FIDE-Handbuch (siehe Appendix C) unterstützt. Es werden die Schachfiguren sowohl in",
|
||||
"deutscher als auch in englischer Sprache unterstützt.",
|
||||
"",
|
||||
"┌───────────────────┬─────────┬──────────┐",
|
||||
"│ Schachfigur │ Deutsch │ Englisch │",
|
||||
"├───────────────────┼─────────┼──────────┤",
|
||||
"│ Bauer (Pawn) │ B │ P │",
|
||||
"│ Springer (Knight) │ S │ N │",
|
||||
"│ Läufer (Bishop) │ L │ B │",
|
||||
"│ Turm (Rook) │ T │ R │",
|
||||
"│ Dame (Queen) │ D │ Q │",
|
||||
"│ König (King) │ K │ K │",
|
||||
"└───────────────────┴─────────┴──────────┘",
|
||||
"",
|
||||
"Bei der kurzen Notation wird immer nur das Zielfeld eines Spielzuges genannt. Sollte",
|
||||
"der Ursprung nicht eindeutig sein kann auch die Spalte oder Zeile der zu bewegenden Figur",
|
||||
"genannt werden. So gibt der Spielzug 'dxe5' an dass der Bauer in der Spalte D auf das Feld",
|
||||
"E5 zieht. Das 'x' gibt an dass dabei eine Spielfigur des Gegeners geschlagen wird. Dies muss",
|
||||
"aber nicht zwingend angegeben werden. Der Bauer wird in den Spielzügen nicht benannt. Der",
|
||||
"Spielzug 'b4' ist daher der Zug eines Bauern auf das Feld B4. Alle anderen Schachfiguren werden",
|
||||
"im Zug mit ihrem entsprechenden Buchstaben genannt. So gibt der Spielzug 'Bc4' an dass der Läufer",
|
||||
"auf das Feld C4 gezogen wird.",
|
||||
"",
|
||||
"Beispiele:",
|
||||
"",
|
||||
" - (=) Angebot für ein Draw. Um das Spiel mit einem Draw zu beenden muss das Angebot direkt im",
|
||||
" nächsten Zug bestätigt werden.",
|
||||
" - b4 Der Bauer zieht auf das Feld B4.",
|
||||
" - Bc4 Der Läufer zieht auf das Feld C4.",
|
||||
" - 0-0 Kleine Rochade (Rochade am Königsflügel)",
|
||||
" - 0-0-0 Große Rochade (Rochade am Damenflügel)",
|
||||
" - d8Q Umwandlung: Der Bauer zieht auf D8 und wandelt sich zur Dame.",
|
||||
"",
|
||||
"2. PGN (Portable Game Notation)",
|
||||
"",
|
||||
"TurboSchach unterstützt das Datenformat PGN, welches zur Speicherung von Schachpartien verwendet",
|
||||
"wird. Auf der Website https://www.chessgames.com/ werden viele Schachpartien von offiziellen Turnieren",
|
||||
"im PGN-Format zur Verfügung gestellt. Diese Dateien können in TurboSchach importiert werden."
|
||||
};
|
||||
void GenerateElement() override;
|
||||
|
||||
public:
|
||||
InstructionsVisualizer() : BaseVisualizer(BaseVisualizer::FindMaxLength(menuContent), 4) {
|
||||
InstructionsVisualizer::GenerateElement();
|
||||
};
|
||||
void DrawView();
|
||||
};
|
||||
|
||||
#endif //INSTRUCTIONSVISUALIZER_HPP
|
43
src/Visualizer/PlaySelectVisualizer.cpp
Normal file
43
src/Visualizer/PlaySelectVisualizer.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "PlaySelectVisualizer.hpp"
|
||||
|
||||
void PlaySelectVisualizer::GenerateElement() {
|
||||
// BaseVisualizer::GenerateTopBottomBorder(MAX_MENU_WIDTH, true, false);
|
||||
// BaseVisualizer::GenerateEmptyLine(MAX_MENU_WIDTH, false);
|
||||
// for (const auto& content : menuContent) {
|
||||
// BaseVisualizer::GenerateBoxMenuLine(MAX_MENU_WIDTH, content, false, PADDING);
|
||||
// }
|
||||
// BaseVisualizer::GenerateEmptyLine(MAX_MENU_WIDTH, false);
|
||||
// BaseVisualizer::GenerateTopBottomBorder(MAX_MENU_WIDTH, false, false);
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, int>> PlaySelectVisualizer::GetCursorPositions() {
|
||||
return this->cursorPositions;
|
||||
}
|
||||
|
||||
void PlaySelectVisualizer::AddToCursorPositions(const std::pair<int, int>& position) {
|
||||
this->cursorPositions.push_back(position);
|
||||
}
|
||||
void PlaySelectVisualizer::DrawView() {
|
||||
BaseVisualizer::ClearTerminal();
|
||||
this->display_vector.clear();
|
||||
BaseVisualizer::GenerateTopBottomBorder(MAX_MENU_WIDTH, true, false);
|
||||
BaseVisualizer::GenerateEmptyLine(MAX_MENU_WIDTH, false);
|
||||
if (this->labelPlayerName.empty()) {
|
||||
this->labelPlayerName = "player";
|
||||
}
|
||||
BaseVisualizer::GenerateBoxMenuLine(MAX_MENU_WIDTH, "Please enter a name for the " + this->labelPlayerName + ":", false, PADDING);
|
||||
BaseVisualizer::GenerateBoxMenuLine(MAX_MENU_WIDTH, "\x1B[s", false, PADDING);
|
||||
BaseVisualizer::GenerateEmptyLine(MAX_MENU_WIDTH, false);
|
||||
BaseVisualizer::GenerateTopBottomBorder(MAX_MENU_WIDTH, false, false);
|
||||
BaseVisualizer::DisplayElement();
|
||||
std::cout << "\x1B[u";
|
||||
}
|
||||
std::string PlaySelectVisualizer::ShowMenu() {
|
||||
this->DrawView();
|
||||
std::string choice;
|
||||
std::getline(std::cin, choice);
|
||||
return choice;
|
||||
}
|
||||
void PlaySelectVisualizer::SetLabelPlayerName(std::string label) {
|
||||
this->labelPlayerName = label;
|
||||
}
|
30
src/Visualizer/PlaySelectVisualizer.hpp
Normal file
30
src/Visualizer/PlaySelectVisualizer.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef PLAYSELECTVISUALIZER_HPP
|
||||
#define PLAYSELECTVISUALIZER_HPP
|
||||
|
||||
#include "BaseVisualizer.hpp"
|
||||
|
||||
class PlaySelectVisualizer : public BaseVisualizer {
|
||||
private:
|
||||
std::string labelPlayerName;
|
||||
// ToDo: Exit
|
||||
inline static const std::vector<std::string> menuContent = {
|
||||
"Please enter your names! Enter your names with '&' seperated.",
|
||||
"",
|
||||
"Playernames: \x1B[s"
|
||||
};
|
||||
std::vector<std::pair<int, int>> cursorPositions;
|
||||
|
||||
|
||||
public:
|
||||
PlaySelectVisualizer(size_t padding) : BaseVisualizer(PlaySelectVisualizer::FindMaxLength(menuContent), padding) {
|
||||
PlaySelectVisualizer::GenerateElement();
|
||||
}
|
||||
void GenerateElement();
|
||||
void AddToCursorPositions(const std::pair<int, int>& position);
|
||||
std::vector<std::pair<int, int>> GetCursorPositions();
|
||||
void DrawView();
|
||||
std::string ShowMenu();
|
||||
void SetLabelPlayerName(std::string label);
|
||||
};
|
||||
|
||||
#endif //PLAYSELECTVISUALIZER_HPP
|
129
src/Visualizer/PlayingViewVisualizer.cpp
Normal file
129
src/Visualizer/PlayingViewVisualizer.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
#include "PlayingViewVisualizer.hpp"
|
||||
#include "ChessboardVisualizer.hpp"
|
||||
#include "HistorieVisualizer.hpp"
|
||||
#include "CommandMenuVisualizer.hpp"
|
||||
|
||||
|
||||
// ToDo: Beim Fenster größer ziehen verschiebt sich die Ansicht
|
||||
void PlayingViewVisualizer::GenerateElement() {
|
||||
position_vector.clear();
|
||||
display_vector.clear();
|
||||
ChessboardVisualizer chessboardVisualizer = ChessboardVisualizer(this->CHESSBOARD, 2);
|
||||
HistorieVisualizer historieVisualizer = HistorieVisualizer(this->CHESSBOARD, 32, 2);
|
||||
CommandMenuVisualizer cmdMenuVisualizer = CommandMenuVisualizer(this->CHESSBOARD, 2, this->GetMessage());
|
||||
|
||||
GeneratePositionVector(chessboardVisualizer.GetDisplayVector(), historieVisualizer.GetDisplayVector(), cmdMenuVisualizer.GetDisplayVector());
|
||||
|
||||
BaseVisualizer::GenerateTopBottomBorder(MAX_MENU_WIDTH, true, false);
|
||||
BaseVisualizer::GenerateEmptyLine(MAX_MENU_WIDTH, false);
|
||||
|
||||
std::vector<std::string> temp;
|
||||
|
||||
//((single) ? BaseVisualizer::VERTICAL_LINE_SINGLE : BaseVisualizer::VERTICAL_LINE) + std::string(padding, ' ')
|
||||
|
||||
for (const auto& row : position_vector) {
|
||||
display_vector.push_back(row);
|
||||
}
|
||||
|
||||
BaseVisualizer::GenerateEmptyLine(MAX_MENU_WIDTH, false);
|
||||
BaseVisualizer::GenerateTopBottomBorder(MAX_MENU_WIDTH, false, false);
|
||||
//size_t maxWidth = BaseVisualizer::FindMaxLength(chessboardVisualizer.display_vector);
|
||||
|
||||
}
|
||||
|
||||
void PlayingViewVisualizer::GeneratePositionVector(
|
||||
std::vector<std::vector<std::string>>* chessboard_display_vector,
|
||||
std::vector<std::vector<std::string>>* historie_display_vector,
|
||||
std::vector<std::vector<std::string>>* command_menu_display_vector)
|
||||
{
|
||||
for (size_t i = 0; i < chessboard_display_vector->size(); ++i) {
|
||||
size_t test;
|
||||
std::vector<std::string> temp;
|
||||
temp.push_back({BaseVisualizer::VERTICAL_LINE + " "});
|
||||
if (i == 0) {
|
||||
const std::string& first_element = chessboard_display_vector->at(i).at(0);
|
||||
temp.push_back(first_element);
|
||||
temp.push_back(std::string(62-BaseVisualizer::CountVisibleCharacters(first_element), ' '));
|
||||
} else if (chessboard_display_vector->at(i).size() > 1 && i != 0) {
|
||||
temp.insert(temp.end(), chessboard_display_vector->at(i).begin(), chessboard_display_vector->at(i).end());
|
||||
} else {
|
||||
temp.push_back(chessboard_display_vector->at(i).at(0));
|
||||
}
|
||||
|
||||
temp.push_back(std::string(DISTANCE_BETWEEN_ELEMENTS, ' '));
|
||||
|
||||
if (i < historie_display_vector->size()) {
|
||||
temp.push_back(historie_display_vector->at(i)[0]);
|
||||
} else if (i-3-historie_display_vector->size() < command_menu_display_vector->size()) {
|
||||
temp.push_back(command_menu_display_vector->at(i-3-historie_display_vector->size())[0]);
|
||||
//} else if (i-3-historie_display_vector->size()-command_menu_display_vector->size()-1 == 0 && !this->GetMessage().empty()) {
|
||||
//temp.push_back(this->GetMessage());
|
||||
} else {
|
||||
temp.push_back({"", ""});
|
||||
}
|
||||
|
||||
BaseVisualizer::GetSumAllCharsFromVector(temp);
|
||||
temp.push_back(std::string(MAX_MENU_WIDTH-BaseVisualizer::GetSumAllCharsFromVector(temp)-1, ' ') + BaseVisualizer::VERTICAL_LINE);
|
||||
|
||||
position_vector.push_back(temp);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayingViewVisualizer::DisplayElement() {
|
||||
PlayingViewVisualizer::GenerateElement();
|
||||
size_t size;
|
||||
std::string temp;
|
||||
for (int row = 0; row < display_vector.size(); ++row) {
|
||||
for (int column = 0; column < display_vector[row].size(); ++column) {
|
||||
if (row >= 4 && row <= 27 && column >= 3 && column <= 10) {
|
||||
bool isSecondRowOfField = ((row-1) % 3 == 2);
|
||||
bool isBlackWhite = ((column-1) % 2 == (row-1) % 2);
|
||||
|
||||
if ((row - 1) % 3 == 0) { // Change after 3 rows
|
||||
if (isSecondRowOfField) {
|
||||
this->SetConsoleColor(isBlackWhite ? BLACK : WHITE, isBlackWhite ? WHITE : BLACK);
|
||||
} else {
|
||||
this->SetConsoleColor(isBlackWhite ? WHITE : BLACK, isBlackWhite ? BLACK : WHITE);
|
||||
}
|
||||
} else {
|
||||
if (isSecondRowOfField) {
|
||||
this->SetConsoleColor(isBlackWhite ? WHITE : BLACK, isBlackWhite ? BLACK : WHITE);
|
||||
} else {
|
||||
this->SetConsoleColor(isBlackWhite ? BLACK : WHITE, isBlackWhite ? WHITE : BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << display_vector[row][column];
|
||||
this->SetConsoleColor(DEFAULT, DEFAULT);
|
||||
}
|
||||
this->SetConsoleColor(DEFAULT, DEFAULT);
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::string PlayingViewVisualizer::GetMove() {
|
||||
return move;
|
||||
}
|
||||
|
||||
std::string PlayingViewVisualizer::GetMessage() {
|
||||
return this->message;
|
||||
}
|
||||
|
||||
void PlayingViewVisualizer::SetMessage(std::string message) {
|
||||
this->message = message;
|
||||
}
|
||||
|
||||
void PlayingViewVisualizer::DrawView() {
|
||||
BaseVisualizer::ClearTerminal();
|
||||
this->DisplayElement();
|
||||
std::cout << "\x1B[u";
|
||||
}
|
||||
|
||||
std::string PlayingViewVisualizer::ShowMenu() {
|
||||
this->DrawView();
|
||||
this->SetMessage("");
|
||||
std::string choice;
|
||||
std::getline(std::cin, choice);
|
||||
return choice;
|
||||
}
|
34
src/Visualizer/PlayingViewVisualizer.hpp
Normal file
34
src/Visualizer/PlayingViewVisualizer.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef PLAYINGVIEWVISUALIZER_HPP
|
||||
#define PLAYINGVIEWVISUALIZER_HPP
|
||||
|
||||
#include "BaseVisualizer.hpp"
|
||||
#include "../Chessboard/Chessboard.hpp"
|
||||
|
||||
class PlayingViewVisualizer : public BaseVisualizer {
|
||||
private:
|
||||
const size_t DISTANCE_BETWEEN_ELEMENTS;
|
||||
|
||||
Chessboard* CHESSBOARD;
|
||||
std::string move;
|
||||
std::string message = "";
|
||||
|
||||
std::vector<std::vector<std::string>> position_vector;
|
||||
|
||||
void GenerateElement();
|
||||
void GeneratePositionVector(
|
||||
std::vector<std::vector<std::string>>* chessboard_display_vector,
|
||||
std::vector<std::vector<std::string>>* historie_display_vector,
|
||||
std::vector<std::vector<std::string>>* command_menu_display_vector);
|
||||
|
||||
public:
|
||||
PlayingViewVisualizer(Chessboard* chessboard, size_t padding, size_t distance) :
|
||||
BaseVisualizer(110, padding), CHESSBOARD(chessboard), DISTANCE_BETWEEN_ELEMENTS(distance) {}
|
||||
void DisplayElement();
|
||||
std::string GetMove();
|
||||
std::string GetMessage();
|
||||
void SetMessage(std::string message);
|
||||
void DrawView();
|
||||
std::string ShowMenu();
|
||||
};
|
||||
|
||||
#endif //PLAYINGVIEWVISUALIZER_HPP
|
71
src/Visualizer/StartMenuVisualizer.cpp
Normal file
71
src/Visualizer/StartMenuVisualizer.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "StartMenuVisualizer.hpp"
|
||||
|
||||
void StartMenuVisualizer::GenerateElement() {
|
||||
BaseVisualizer::GenerateTopBottomBorder(MAX_MENU_WIDTH, true, false);
|
||||
|
||||
BaseVisualizer::AddEmptyLines(2, MAX_MENU_WIDTH, false);
|
||||
|
||||
for (const auto& str : StartMenuVisualizer::ACSII_ART_TURBO_SCHACH) {
|
||||
BaseVisualizer::GenerateCenteredString(MAX_MENU_WIDTH, str, false);
|
||||
}
|
||||
|
||||
BaseVisualizer::AddEmptyLines(2, MAX_MENU_WIDTH, false);
|
||||
|
||||
BaseVisualizer::GenerateCenteredString(MAX_MENU_WIDTH, "Welcome to TurboSchach!", false);
|
||||
|
||||
BaseVisualizer::AddEmptyLines(2, MAX_MENU_WIDTH, false);
|
||||
|
||||
BaseVisualizer::GenerateCenteredString(MAX_MENU_WIDTH, "Please select one of the menu options:", false);
|
||||
BaseVisualizer::GenerateCenteredString(MAX_MENU_WIDTH, "\x1B[s", false);
|
||||
|
||||
BaseVisualizer::AddEmptyLines(1, MAX_MENU_WIDTH, false);
|
||||
|
||||
// ToDo: Attribute zum direkten setzen des Types der Außenbegrenzung -> Allgemein in BaseVisualizer auch?
|
||||
// ToDo: Überarbeiten -> Extra Methode um Menus im Menu zu generieren
|
||||
BaseVisualizer::GenerateTopBottomBorder(24, true, true);
|
||||
BaseVisualizer::GenerateCenteredString(MAX_MENU_WIDTH, BaseVisualizer::display_vector[BaseVisualizer::display_vector.size()-1][0], false);
|
||||
BaseVisualizer::display_vector.erase(BaseVisualizer::display_vector.begin() + (BaseVisualizer::display_vector.size() - 2));
|
||||
|
||||
|
||||
for (const auto& str : StartMenuVisualizer::MENU_OPTIONS) {
|
||||
BaseVisualizer::GenerateBoxMenuLine(24, str, true, 1);
|
||||
BaseVisualizer::GenerateCenteredString(MAX_MENU_WIDTH, BaseVisualizer::display_vector[BaseVisualizer::display_vector.size()-1][0], false);
|
||||
BaseVisualizer::display_vector.erase(BaseVisualizer::display_vector.begin() + (BaseVisualizer::display_vector.size() - 2));
|
||||
|
||||
}
|
||||
|
||||
BaseVisualizer::GenerateTopBottomBorder(24, false, true);
|
||||
BaseVisualizer::GenerateCenteredString(MAX_MENU_WIDTH, BaseVisualizer::display_vector[BaseVisualizer::display_vector.size()-1][0], false);
|
||||
BaseVisualizer::display_vector.erase(BaseVisualizer::display_vector.begin() + (BaseVisualizer::display_vector.size() - 2));
|
||||
|
||||
|
||||
BaseVisualizer::AddEmptyLines(2, MAX_MENU_WIDTH, false);
|
||||
|
||||
BaseVisualizer::GenerateTopBottomBorder(MAX_MENU_WIDTH, false, false);
|
||||
}
|
||||
|
||||
int StartMenuVisualizer::GetSelectedOption() {
|
||||
return selectedOption;
|
||||
}
|
||||
|
||||
void StartMenuVisualizer::SetSelectedOption(int optionSelect) {
|
||||
selectedOption = selectedOption;
|
||||
};
|
||||
|
||||
void StartMenuVisualizer::DrawView() {
|
||||
BaseVisualizer::ClearTerminal();
|
||||
BaseVisualizer::DisplayElement();
|
||||
std::cout << "\x1B[u";
|
||||
}
|
||||
|
||||
StartMenuVisualizer::StartMenuOption StartMenuVisualizer::ShowMenu() {
|
||||
this->DrawView();
|
||||
std::string choice;
|
||||
std::getline(std::cin, choice);
|
||||
|
||||
if (choice == "1") return StartMenuOption::NewGame;
|
||||
if (choice == "2") return StartMenuOption::LoadGame;
|
||||
if (choice == "3") return StartMenuOption::Instructions;
|
||||
if (choice == "0") return StartMenuOption::Exit;
|
||||
return StartMenuOption::None;
|
||||
}
|
46
src/Visualizer/StartMenuVisualizer.hpp
Normal file
46
src/Visualizer/StartMenuVisualizer.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef STARTMENUVISUALIZER_H
|
||||
#define STARTMENUVISUALIZER_H
|
||||
|
||||
#include "BaseVisualizer.hpp"
|
||||
|
||||
class StartMenuVisualizer : public BaseVisualizer {
|
||||
private:
|
||||
inline static const std::vector<std::string> ACSII_ART_TURBO_SCHACH = {
|
||||
"████████╗██╗░░░██╗██████╗░██████╗░░█████╗░░██████╗░█████╗░██╗░░██╗░█████╗░░█████╗░██╗░░██╗",
|
||||
"╚══██╔══╝██║░░░██║██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔══██╗██║░░██║██╔══██╗██╔══██╗██║░░██║",
|
||||
"░░░██║░░░██║░░░██║██████╔╝██████╦╝██║░░██║╚█████╗░██║░░╚═╝███████║███████║██║░░╚═╝███████║",
|
||||
"░░░██║░░░██║░░░██║██╔══██╗██╔══██╗██║░░██║░╚═══██╗██║░░██╗██╔══██║██╔══██║██║░░██╗██╔══██║",
|
||||
"░░░██║░░░╚██████╔╝██║░░██║██████╦╝╚█████╔╝██████╔╝╚█████╔╝██║░░██║██║░░██║╚█████╔╝██║░░██║",
|
||||
"░░░╚═╝░░░░╚═════╝░╚═╝░░╚═╝╚═════╝░░╚════╝░╚═════╝░░╚════╝░╚═╝░░╚═╝╚═╝░░╚═╝░╚════╝░╚═╝░░╚═╝"
|
||||
};
|
||||
|
||||
inline static const std::vector<std::string> MENU_OPTIONS = {
|
||||
"1 - New Game",
|
||||
"2 - Load Game",
|
||||
"3 - Instructions",
|
||||
"",
|
||||
"0 - Exit"
|
||||
};
|
||||
|
||||
int selectedOption = -1;
|
||||
|
||||
void GenerateElement() override;
|
||||
|
||||
public:
|
||||
StartMenuVisualizer(size_t padding) : BaseVisualizer(BaseVisualizer::CountVisibleCharacters(StartMenuVisualizer::ACSII_ART_TURBO_SCHACH[0]), padding) {
|
||||
StartMenuVisualizer::GenerateElement();
|
||||
}
|
||||
enum StartMenuOption {
|
||||
NewGame,
|
||||
LoadGame,
|
||||
Instructions,
|
||||
Exit,
|
||||
None
|
||||
};
|
||||
int GetSelectedOption();
|
||||
void SetSelectedOption(int selectedOption);
|
||||
void DrawView();
|
||||
StartMenuOption ShowMenu();
|
||||
};
|
||||
|
||||
#endif //STARTMENUVISUALIZER_H
|
38
src/main.cpp
Normal file
38
src/main.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "./Controller/MenuController.hpp"
|
||||
#include "./Visualizer/StartMenuVisualizer.hpp"
|
||||
#include "./Visualizer/BaseVisualizer.hpp"
|
||||
#include "./Visualizer/PlaySelectVisualizer.hpp"
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
#endif
|
||||
|
||||
StartMenuVisualizer startMenu(3);
|
||||
MenuController menuController;
|
||||
StartMenuVisualizer::StartMenuOption optionStartMenu = StartMenuVisualizer::StartMenuOption::None;
|
||||
|
||||
do {
|
||||
switch (optionStartMenu = startMenu.ShowMenu()) {
|
||||
case StartMenuVisualizer::StartMenuOption::NewGame:
|
||||
menuController.HandleFirstOption();
|
||||
break;
|
||||
case StartMenuVisualizer::StartMenuOption::LoadGame:
|
||||
menuController.HandleSecondOption();
|
||||
break;
|
||||
case StartMenuVisualizer::StartMenuOption::Instructions:
|
||||
menuController.HandleThirdOption();
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
} while (optionStartMenu != StartMenuVisualizer::StartMenuOption::Exit);
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user