Compare commits

...

54 Commits

Author SHA1 Message Date
3891d90f43 updated docs path 2025-01-19 13:43:02 +01:00
a7ef306800 reorganized repository 2025-01-19 13:33:30 +01:00
a1e1205247 moved controller to subfolder 2025-01-18 13:28:32 +01:00
1007b6744d removed .idea folder 2025-01-18 13:12:55 +01:00
f0701fe9e7 fixed navigation and more secure navigation 2025-01-18 13:11:27 +01:00
b462ae3c7e removed second layer to set the start positions 2025-01-16 20:56:06 +01:00
8ad2a1342a fixed history 2025-01-16 19:30:40 +01:00
99007047eb final commit for Torsten 2025-01-16 16:03:56 +01:00
Fabian505
d8b9430120 Changes 2025-01-16 15:34:29 +01:00
Fabian505
4f4d598eb6 Changes 2025-01-16 15:29:18 +01:00
Fabian505
2f065f4917 Finish export method 2025-01-16 13:38:16 +01:00
3e8d6d9557 draw / resign and feedback from moves in ui 2025-01-16 12:48:39 +01:00
Fabian505
6c8d033629 Add SetPlayers with set order 2025-01-16 12:33:58 +01:00
Fabian505
3dfe314836 Set instruction to InstructionVisualizer 2025-01-16 12:17:56 +01:00
Fabian505
ca546e5ba6 Merge branch 'main' of https://gitea.hb.dhbw-stuttgart.de/FabianHamacher/TurboSchach 2025-01-16 11:50:53 +01:00
Fabian505
6b01273580 Add menu for Player name input 2025-01-16 11:50:48 +01:00
625d607ef0 added some information 2025-01-16 10:03:24 +01:00
Fabian505
14869f4a83 Modify eport method; round counter doesnt work quiet right 2025-01-16 09:26:53 +01:00
Fabian505
47b880e737 Implement export method 2025-01-16 08:49:01 +01:00
efd60b89e4 import of pgn file with ui and bug fixes 2025-01-16 01:29:45 +01:00
bbd92bfa8b Remove old files; Add final todos 2025-01-15 21:15:57 +01:00
65227d12ab Small changes 2025-01-15 20:45:55 +01:00
d2fbaa4af6 updated make files and removed old files 2025-01-15 10:11:16 +01:00
Sebastian Brosch
2ae5de715d added cross platform clear 2025-01-15 10:08:30 +01:00
8d75c055c0 Remove methods out of main.cpp 2025-01-15 08:24:40 +01:00
83eb22e627 Merge branch 'Export_Import_Feature' 2025-01-15 06:43:53 +01:00
30cd5c4af8 Add ToDos 2025-01-15 06:42:10 +01:00
faa0e236b6 Add ToDo for later 2025-01-15 06:36:43 +01:00
Fabian505
12a009b7db Address merge bugs; Move old code 2025-01-15 05:30:54 +01:00
Fabian505
373c186075 Merge new Feature 2025-01-15 04:43:40 +01:00
Fabian505
9821f78c87 Trying to Merge 2025-01-15 04:25:34 +01:00
Fabian505
9fb66b6755 Darstellung der Menüs größten Teils umgesetzt; Navigation noch sehr buggy; Einige Features muessen noch umgesetzt werden (siehe README) 2025-01-15 04:04:08 +01:00
50b3312f49 added draw 2025-01-14 23:10:10 +01:00
6127e4cf37 Refactor Visualizer, Add CommandVisualizer 2025-01-14 17:27:20 +01:00
Fabian505
8636bcfb0c push all changes for work 2025-01-14 06:13:47 +01:00
aa06318c07 import of pgn files with examples 2025-01-14 00:05:37 +01:00
7695efa5d3 now adding the whole chess piece move object to the internal vector 2025-01-12 22:31:24 +01:00
c38f134254 improved detection for checkmate 2025-01-12 21:05:20 +01:00
99c644bffb updated build command and created makefile 2025-01-11 23:49:17 +01:00
4602cbe187 optimized chessboard 2025-01-11 23:14:31 +01:00
0a1f3d1e26 optimized next positions for pawn 2025-01-11 23:07:17 +01:00
Fabian505
b4c2f84ea0 Provisorisches Padding wieder eingefügt 2025-01-11 14:31:47 +01:00
Fabian505
c9f83c396f Implement first displays of new elements (StartMenu and Historie) 2025-01-11 13:55:50 +01:00
Fabian505
5a77578699 Change compile command to include new visualizer classes 2025-01-11 13:54:43 +01:00
Fabian505
0fbe762391 Create Base Class for Visualization of different elements (First implementation of StartMenu and Historie) 2025-01-11 13:53:42 +01:00
Fabian505
85d22b4736 Add first version of start menu (not functional) 2025-01-11 05:09:19 +01:00
4cf45f75bc tested stalemate 2025-01-11 00:46:13 +01:00
4bf1447673 code style 2025-01-11 00:18:20 +01:00
Fabian505
d1286469c3 Save and display chessboard over a 2d vector 2025-01-10 22:30:45 +01:00
27b684c550 compile and run for windows and linux 2025-01-10 17:37:06 +01:00
Fabian505
de0cbf5a8b Refactor Draw method for better readablity (introduce helper methods) 2025-01-10 17:01:22 +01:00
47b6a82be8 added special moves and support for short algebraic notation 2025-01-10 17:00:20 +01:00
Fabian505
e24ebcfc0b Move Start Board from main.cpp to Chessboard.cpp/.hpp and add method to initilize any board from given set 2025-01-10 17:00:08 +01:00
af5b4ceb66 consider protective positions between attacker and king 2025-01-10 10:29:42 +01:00
86 changed files with 6252 additions and 2421 deletions

View File

@@ -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

6
.gitignore vendored
View File

@@ -1,3 +1,7 @@
.vscode/
.idea/
build/
docs/
*.exe
*.txt
*.txt
chess

View File

@@ -1,174 +0,0 @@
#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 = &currentPosition;
if (this->GetColor() == ChessPieceColor::Black) {
// 3.7.1. The pawn may move forward to the square immediately in front of it on the same file, provided that this square is unoccupied.
// 3.7.2. On its first move the pawn may move as in 3.7.1 or alternatively it may advance two squares along the same file, provided that both squares are unoccupied.
for (int move = 1; move <= 2; move++) {
nextPosition = nextPosition->NextBottom();
if (nextPosition) {
if (chessboard->IsEmptyField(nextPosition) == false) {
break;
}
if (move == 1) {
positions.insert(*nextPosition);
} else if (move == 2 && this->IsFirstMove()) {
positions.insert(*nextPosition);
}
} else {
break;
}
}
// 3.7.3. the pawn may move to a square occupied by an opponent's piece diagonally in front of it on an adjacent file, capturing that piece.
for (std::string move : {"B1L1", "B1R1"}) {
nextPosition = &currentPosition;
nextPosition = nextPosition->NextFromString(move);
if (nextPosition && chessboard->IsEmptyField(nextPosition) == false) {
ChessPiece* chessPiece = chessboard->GetChessPiece(nextPosition);
if (chessPiece->GetColor() != this->GetColor()) {
positions.insert(*nextPosition);
}
}
}
// 3.7.3.1. A pawn occupying a square on the same rank as and on an adjacent file to an opponent's pawn which has just advanced two squares
// in one move from its original square may capture this opponent's pawn as though the latter had been moved only one square.
// 3.7.3.2. This capture is only legal on the move following this advance and is called an "en passant" capture.
if (chessboard->GetCountMoves() > 0) {
std::pair<ChessPiecePosition, ChessPiecePosition> lastMove = chessboard->GetLastMove();
std::string moveDifference = ChessPiecePosition::GetDifference(lastMove.first, lastMove.second);
ChessPiecePosition* leftPosition = currentPosition.NextLeft();
ChessPiecePosition* rightPosition = currentPosition.NextRight();
if (leftPosition && chessboard->IsEmptyField(leftPosition) == false) {
ChessPiece* chessPiece = chessboard->GetChessPiece(leftPosition);
if (chessPiece->GetColor() != this->GetColor() && chessPiece->IsPawn()) {
if (chessPiece->GetNumberOfMoves() == 1 && moveDifference == "T2" && chessPiece->GetPosition() == lastMove.second) {
ChessPiecePosition* bottomLeftPosition = currentPosition.NextFromString("B1L1");
if (bottomLeftPosition && chessboard->IsEmptyField(bottomLeftPosition)) {
positions.insert(*bottomLeftPosition);
}
}
}
}
if (rightPosition && chessboard->IsEmptyField(rightPosition) == false) {
ChessPiece* chessPiece = chessboard->GetChessPiece(rightPosition);
if (chessPiece->GetColor() != this->GetColor() && chessPiece->IsPawn()) {
if (chessPiece->GetNumberOfMoves() == 1 && moveDifference == "T2" && chessPiece->GetPosition() == lastMove.second) {
ChessPiecePosition* bottomRightPosition = currentPosition.NextFromString("B1R1");
if (bottomRightPosition && chessboard->IsEmptyField(bottomRightPosition)) {
positions.insert(*bottomRightPosition);
}
}
}
}
}
} else if (this->GetColor() == ChessPieceColor::White) {
// 3.7.1. The pawn may move forward to the square immediately in front of it on the same file, provided that this square is unoccupied.
// 3.7.2. On its first move the pawn may move as in 3.7.1 or alternatively it may advance two squares along the same file, provided that both squares are unoccupied.
for (int move = 1; move <= 2; move++) {
nextPosition = nextPosition->NextTop();
if (nextPosition) {
if (chessboard->IsEmptyField(nextPosition) == false) {
break;
}
if (move == 1) {
positions.insert(*nextPosition);
} else if (move == 2 && this->IsFirstMove()) {
positions.insert(*nextPosition);
}
} else {
break;
}
}
// 3.7.3. the pawn may move to a square occupied by an opponent's piece diagonally in front of it on an adjacent file, capturing that piece.
for (std::string move : {"T1L1", "T1R1"}) {
nextPosition = &currentPosition;
nextPosition = nextPosition->NextFromString(move);
if (nextPosition && chessboard->IsEmptyField(nextPosition) == false) {
ChessPiece* chessPiece = chessboard->GetChessPiece(nextPosition);
if (chessPiece->GetColor() != this->GetColor()) {
positions.insert(*nextPosition);
}
}
}
// 3.7.3.1. A pawn occupying a square on the same rank as and on an adjacent file to an opponent's pawn which has just advanced two squares
// in one move from its original square may capture this opponent's pawn as though the latter had been moved only one square.
// 3.7.3.2. This capture is only legal on the move following this advance and is called an "en passant" capture.
if (chessboard->GetCountMoves() > 0) {
std::pair<ChessPiecePosition, ChessPiecePosition> lastMove = chessboard->GetLastMove();
std::string moveDifference = ChessPiecePosition::GetDifference(lastMove.first, lastMove.second);
ChessPiecePosition* leftPosition = currentPosition.NextLeft();
ChessPiecePosition* rightPosition = currentPosition.NextRight();
if (leftPosition && chessboard->IsEmptyField(leftPosition) == false) {
ChessPiece* chessPiece = chessboard->GetChessPiece(leftPosition);
if (chessPiece->GetColor() != this->GetColor() && chessPiece->IsPawn()) {
if (chessPiece->GetNumberOfMoves() == 1 && moveDifference == "B2" && chessPiece->GetPosition() == lastMove.second) {
ChessPiecePosition* topLeftPosition = currentPosition.NextFromString("T1L1");
if (topLeftPosition && chessboard->IsEmptyField(topLeftPosition)) {
positions.insert(*topLeftPosition);
}
}
}
}
if (rightPosition && chessboard->IsEmptyField(rightPosition) == false) {
ChessPiece* chessPiece = chessboard->GetChessPiece(rightPosition);
if (chessPiece->GetColor() != this->GetColor() && chessPiece->IsPawn()) {
if (chessPiece->GetNumberOfMoves() == 1 && moveDifference == "B2" && chessPiece->GetPosition() == lastMove.second) {
ChessPiecePosition* topRightPosition = currentPosition.NextFromString("T1R1");
if (topRightPosition && chessboard->IsEmptyField(topRightPosition)) {
positions.insert(*topRightPosition);
}
}
}
}
}
}
return positions;
}

View File

@@ -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);
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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();
// This method loads a save state
void loadBoard(std::string filepath);
};
#endif //CHESSBOARD_H

View File

@@ -1,296 +0,0 @@
#include "../Chessboard/Chessboard.hpp"
#include "../ChessPieces/ChessPiece.hpp"
void Chessboard::SetChessPiece(ChessPiece* chesspiece) {
this->chessboard[chesspiece->GetPosition()] = chesspiece;
}
bool Chessboard::IsEmptyField(ChessPiecePosition* position) {
return (this->chessboard.count(*position) == 0);
}
ChessPiece* Chessboard::GetChessPiece(ChessPiecePosition* position) {
return this->chessboard.at(*position);
}
void Chessboard::MoveChessPiece(std::string move) {
if (move.length() == 5) {
ChessPiecePosition* fromPosition = new ChessPiecePosition(move.substr(0, 2));
ChessPiecePosition* toPosition = new ChessPiecePosition(move.substr(3, 2));
ChessPiece* piece = this->GetChessPiece(fromPosition);
if (piece->GetColor() != this->GetCurrentPlayer()->GetColor()) {
return; // wrong player
}
if (piece->GetNextPositions().count(*toPosition) == 1) {
this->chessboard[*toPosition] = this->chessboard[*fromPosition];
this->chessboard.erase(*fromPosition);
piece->SetPosition(*toPosition);
this->SetToHistory(*fromPosition, *toPosition);
} else {
return; // wrong or invalid move
}
}
this->UpdateChessPieces();
this->SwitchCurrentPlayer();
}
void Chessboard::SetToHistory(ChessPiecePosition fromPosition, ChessPiecePosition toPosition) {
this->history.push_back({fromPosition, toPosition});
}
int Chessboard::GetCountMoves() {
return this->history.size();
}
std::pair<ChessPiecePosition, ChessPiecePosition> Chessboard::GetLastMove() {
return this->history.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;
}
bool Chessboard::IsCheck() {
ChessPiece* chessPieceKing = this->GetChessPieceKing(this->GetCurrentPlayer()->GetColor());
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
if (field.second->GetColor() == this->GetOpponentPlayer()->GetColor()) {
if (field.second->GetNextPositions().count(chessPieceKing->GetPosition()) == 1) {
return true;
}
}
}
return false;
}
bool Chessboard::IsCheckmate() {
if (this->IsCheck() == false) {
return false;
}
ChessPiece* chessPieceKing = this->GetChessPieceKing(this->GetCurrentPlayer()->GetColor());
if (chessPieceKing->GetNextPositions().size() > 0) {
return false;
}
int cntAttacker = 0;
for (ChessPiece* chessPieceOpponent : this->GetChessPieces()) {
if (chessPieceOpponent->GetColor() == this->GetOpponentPlayer()->GetColor()) {
if (chessPieceOpponent->GetNextPositions().count(chessPieceKing->GetPosition()) == 1) { // Is an attacker
cntAttacker++;
bool deniable = false;
// remove attacker
for (ChessPiece* chessPieceCurrent : this->GetChessPieces()) {
if (chessPieceCurrent->GetColor() == this->GetCurrentPlayer()->GetColor()) {
if (chessPieceCurrent->GetNextPositions().count(chessPieceOpponent->GetPosition()) == 1) {
deniable = true; // blocked attack because current player can remove attacker
break;
}
}
}
if (deniable == false) {
return true;
}
// move between attacker and king
// this is not possible for pawn, knight and king because they beat on next field.
std::string step = ChessPiecePosition::GetStep(chessPieceOpponent->GetPosition(), chessPieceKing->GetPosition());
ChessPiecePosition currentPosition = chessPieceOpponent->GetPosition();
ChessPiecePosition* nextPosition = &currentPosition;
deniable = false;
while (nextPosition = nextPosition->NextFromString(step)) {
for (ChessPiece* chessPieceCurrent : this->GetChessPieces()) {
if (chessPieceCurrent->GetColor() == this->GetCurrentPlayer()->GetColor()) {
if (chessPieceCurrent->GetNextPositions().count(*nextPosition) == 1) {
deniable = true; // blocked attack because current player can block between attacker and king
break;
}
}
}
}
if (deniable == false) {
return true;
}
}
}
}
if (cntAttacker > 1) {
return true;
}
return false;
}
bool Chessboard::IsStalemate() {
ChessPieceColor currentColor = this->GetCurrentPlayer()->GetColor();
ChessPieceColor opponentColor = this->GetOpponentPlayer()->GetColor();
ChessPiece* chessPieceKing;
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
if (field.second->IsKing() && field.second->GetColor() == currentColor) {
chessPieceKing = field.second;
break;
}
}
// Der König hat aber auch keine validen Züge mehr.
if (chessPieceKing->GetNextPositions().size() > 0) {
return false;
}
// Man hat selbst keine Figuren mehr welche noch ziehen könnten.
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
if (field.second->GetColor() == currentColor) {
if (field.second->GetNextPositions().size() > 0) {
return false;
}
}
}
// König wird nicht direkt attackiert.
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
if (field.second->GetColor() == opponentColor) {
if (field.second->GetNextPositions().count(chessPieceKing->GetPosition()) == 1) {
return false;
}
}
}
return true;
}
/**
* Get the current player.
*/
Player* Chessboard::GetCurrentPlayer() {
return this->currentPlayer;
}
/**
* Get the opponent player.
*/
Player* Chessboard::GetOpponentPlayer() {
if (this->GetCurrentPlayer()->GetColor() == ChessPieceColor::Black) {
return this->GetPlayer(ChessPieceColor::White);
} else {
return this->GetPlayer(ChessPieceColor::Black);
}
}
/**
* Get a player by color.
* @return Player* The player who is associated with the given color.
*/
Player* Chessboard::GetPlayer(ChessPieceColor color) {
if (color == ChessPieceColor::Black) {
return std::get<ChessPieceColor::Black>(this->players);
} else {
return std::get<ChessPieceColor::White>(this->players);
}
}
/**
* Set the players to the chessboard.
* This function also determines the starting player.
* @param Player* playerA The first player.
* @param Player* playerB The second player.
*/
void Chessboard::SetPlayers(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);
}
/**
* Switch the current player.
*/
void Chessboard::SwitchCurrentPlayer() {
if (this->GetCurrentPlayer()->GetColor() == ChessPieceColor::White) {
this->currentPlayer = this->GetPlayer(ChessPieceColor::Black);
} else {
this->currentPlayer = this->GetPlayer(ChessPieceColor::White);
}
}
/**
* Update the next positions of all chess pieces.
*/
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->GetColor() == color && field.second->GetNextPositions().count(position) == 1) {
return true;
}
}
return false;
}
Chessboard::~Chessboard() {
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;
}

View File

@@ -1,46 +0,0 @@
#ifndef Chessboard_H
#define Chessboard_H
#include <iostream>
#include <map>
#include <random>
#include <string>
#include <utility>
#include <vector>
#include "../Player/Player.hpp"
#include "../ChessPieces/ChessPieceColor.hpp"
#include "../ChessPieces/ChessPiecePosition.hpp"
class ChessPiece;
class Chessboard {
private:
std::map<ChessPiecePosition, ChessPiece*> chessboard;
std::vector<std::pair<ChessPiecePosition, ChessPiecePosition>> history;
std::pair<Player*, Player*> players;
Player* currentPlayer;
public:
~Chessboard();
void SetChessPiece(ChessPiece* chesspiece);
bool IsEmptyField(ChessPiecePosition* position);
ChessPiece* GetChessPiece(ChessPiecePosition* position);
std::vector<ChessPiece*> GetChessPieces();
void UpdateChessPieces();
void MoveChessPiece(std::string move);
void SetToHistory(ChessPiecePosition fromPosition, ChessPiecePosition toPosition);
int GetCountMoves();
std::pair<ChessPiecePosition, ChessPiecePosition> GetLastMove();
bool IsCheckmate();
bool IsCheck();
bool IsStalemate();
bool IsPositionUnderAttack(ChessPiecePosition position, ChessPieceColor color);
Player* GetCurrentPlayer();
Player* GetOpponentPlayer();
void SetPlayers(Player* playerA, Player* playerB);
void SwitchCurrentPlayer();
Player* GetPlayer(ChessPieceColor color);
ChessPiece* GetChessPieceKing(ChessPieceColor color);
};
#endif

View File

@@ -1 +0,0 @@
#include "../Chessboard/ChessboardVisualizer.hpp"

View File

@@ -1,168 +0,0 @@
#include "../Chessboard/ChessboardVisualizerGraphic.hpp"
#include "../ChessPieces/ChessPiecePosition.hpp"
void ChessboardVisualizerGraphic::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;
}
}
void ChessboardVisualizerGraphic::Draw(Chessboard* chessboard) {
std::cout << "\033[2J"; // clear the console
std::cout << std::endl;
std::cout << " " << chessboard->GetPlayer(ChessPieceColor::White)->GetName() << " vs. " << chessboard->GetPlayer(ChessPieceColor::Black)->GetName();
std::cout << std::endl;
for (int rank = 8; rank >= 1; rank--) {
if (rank == 8) {
for (int i = 0; i < 8; i++) {
if (i == 0) {
std::cout << " " << " " << " ";
std::cout << "\u2554";
}
std::cout << "\u2550" << "\u2550" << "\u2550" << "\u2550" << "\u2550" << "\u2550" << "\u2550";
if (i == 7) {
std::cout << "\u2557";
}
}
std::cout << std::endl;
}
for (int i = 0; i < 3; i++) {
for (char file = 'A'; file <= 'H'; file++) {
if (i == 1) {
if (file == 'A') {
std::cout << " " << rank << " ";
std::cout << "\u2551";
}
} else {
if (file == 'A') {
std::cout << " " << " " << " ";
std::cout << "\u2551";
}
}
if (file % 2 == (rank % 2)) {
this->SetConsoleColor(BLACK, WHITE);
if (i == 1) {
ChessPiecePosition* position = new ChessPiecePosition(file, rank);
if (chessboard->IsEmptyField(position)) {
std::cout << " ";
} else {
ChessPiece* piece = chessboard->GetChessPiece(position);
std::cout << " " << piece->GetUnicode() << " ";
}
} else {
std::cout << " ";
}
} else {
this->SetConsoleColor(WHITE, BLACK);
if (i == 1) {
ChessPiecePosition* position = new ChessPiecePosition(file, rank);
if (chessboard->IsEmptyField(position)) {
std::cout << " ";
} else {
ChessPiece* piece = chessboard->GetChessPiece(position);
std::cout << " " << piece->GetUnicode() << " ";
}
} else {
std::cout << " ";
}
}
if (file == 'H') {
this->SetConsoleColor(DEFAULT, DEFAULT);
std::cout << "\u2551";
}
}
this->SetConsoleColor(DEFAULT, DEFAULT);
std::cout << std::endl;
}
if (rank == 1) {
for (int i = 0; i < 8; i++) {
if (i == 0) {
std::cout << " " << " " << " ";
std::cout << "\u255A";
}
std::cout << "\u2550" << "\u2550" << "\u2550" << "\u2550" << "\u2550" << "\u2550" << "\u2550";
if (i == 7) {
std::cout << "\u255D";
}
}
std::cout << std::endl;
for (char file = 'A'; file <= 'H'; file++) {
if (file == 'A') {
std::cout << " ";
std::cout << " ";
}
std::cout << " " << file << " ";
if (file == 'H') {
std::cout << " ";
}
}
std::cout << std::endl;
}
}
this->SetConsoleColor(DEFAULT, DEFAULT);
std::cout << std::endl;
}

View File

@@ -1,21 +0,0 @@
#ifndef ChessboardVisualizerGraphic_H
#define ChessboardVisualizerGraphic_H
#include "../Chessboard/ChessboardVisualizer.hpp"
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 ChessboardVisualizerGraphic : public ChessboardVisualizer {
private:
void SetConsoleColor(Colors foreground, Colors background);
public:
ChessboardVisualizerGraphic() = default;
void Draw(Chessboard* chessboard);
};
#endif

View File

@@ -1,91 +0,0 @@
#include "../Chessboard/ChessboardVisualizerText.hpp"
#include "../ChessPieces/ChessPiecePosition.hpp"
void ChessboardVisualizerText::Draw(Chessboard* chessboard) {
std::cout << "\033[2J"; // clear the console
std::cout << std::endl;
std::cout << " " << chessboard->GetPlayer(ChessPieceColor::White)->GetName() << " vs. " << chessboard->GetPlayer(ChessPieceColor::Black)->GetName();
std::cout << std::endl;
for (int rank = 8; rank >= 1; rank--) {
if (rank == 8) {
for (int i = 0; i < 8; i++) {
if (i == 0) {
std::cout << " " << " " << " ";
std::cout << "\u250F";
} else {
std::cout << "\u2533";
}
std::cout << "\u2501" << "\u2501" << "\u2501";
if (i == 7) {
std::cout << "\u2513";
}
}
std::cout << std::endl;
}
for (char file = 'A'; file <= 'H'; file++) {
if (file == 'A') { // Beginn der Zeile
std::cout << " " << rank << " ";
std::cout << "\u2503";
}
ChessPiecePosition* position = new ChessPiecePosition(file, rank);
if (chessboard->IsEmptyField(position)) {
std::cout << " " << " " << " ";
} else {
ChessPiece* piece = chessboard->GetChessPiece(position);
std::cout << " " << piece->GetUnicode() << " ";
}
std::cout << "\u2503";
}
std::cout << std::endl;
if (rank != 1) {
for (int i = 0; i < 8; i++) {
if (i == 0) {
std::cout << " " << " " << " ";
std::cout << "\u2523";
} else {
std::cout << "\u254B";
}
std::cout << "\u2501" << "\u2501" << "\u2501";
if (i == 7) {
std::cout << "\u252B";
}
}
std::cout << std::endl;
} else {
for (int i = 0; i < 8; i++) {
if (i == 0) {
std::cout << " " << " " << " ";
std::cout << "\u2517";
} else {
std::cout << "\u253B";
}
std::cout << "\u2501" << "\u2501" << "\u2501";
if (i == 7) {
std::cout << "\u251B";
}
}
std::cout << std::endl;
for (char file = 'A'; file <= 'H'; file++) {
if (file == 'A') {
std::cout << " " << " " << " ";
std::cout << " ";
} else {
std::cout << " ";
}
std::cout << " " << file << " ";
if (file == 'H') {
std::cout << " ";
}
}
std::cout << std::endl;
}
}
std::cout << std::endl;
}

View File

@@ -1,15 +0,0 @@
#ifndef ChessboardVisualizerText_H
#define ChessboardVisualizerText_H
#include "../Chessboard/ChessboardVisualizer.hpp"
class ChessboardVisualizerText : public ChessboardVisualizer {
private:
void Clear();
public:
ChessboardVisualizerText() = default;
void Draw(Chessboard* chessboard);
};
#endif

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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";
}

164
README.md
View File

@@ -1,109 +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
- 8x8 Matrix
- ANSI Linien checken
- Unterscheidung von schwarzen und weißen Feldern
- UTF-8 Spielfiguren
- draw() Funktion
- movement
- Schachnotation
- oder Klickbar?
- Beschriftung des Spielbretts
### Spielfiguren
- 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
## Optional wenn Lust und Zeit?
1. Bedienung per Maus
2. Multiplayer
3. Historie der Spielzüge
4. Gravejard der geschlagenen Figuren
5. König platzieren -> Anzeigen wer gewinnt
# 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
## Figuren
- Läufer (Bishop)
- König (King)
- Königin (Queen)
- Turm (Rook)
- Bauer (Pawn)
- Pferd (Knight)
## ToDo
- Speicherfunktion
- Schachnotation (Lange)
- Menü (ASCII Art of TurboSchach)
- Startmenü
- Spielmenü mit Historie, Kommand
- Plattformunabhängiges CMD clearing vor dem Zeichnen
- Spezialbewegung (Rochade, Umwandlung, En passant)
## Build
```
g++ -o chess.exe main.cpp ChessPieces/*.cpp Chessboard/*.cpp Player/*.cpp
# build
make
# run
make run
```
## Voraussetzungen
### Microsoft Windows
- C++ 17
```
# build
g++ -std=c++17 -o chess.exe main.cpp ChessPieces/*.cpp Chessboard/*.cpp Controller/*.cpp Player/*.cpp Visualizer/*.cpp
# run
./chess.exe
# build and run (PowerShell)
.\run
```
## 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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 *

View 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 *

View 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 "*"]
*

View File

@@ -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 "*"]
*

View File

@@ -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 *

View File

@@ -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

File diff suppressed because it is too large Load Diff

1
docs.ps1 Normal file
View File

@@ -0,0 +1 @@
& "doxygen" doc/Doxyfile

108
main.cpp
View File

@@ -1,108 +0,0 @@
#include <windows.h>
#include "Player/Player.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"
#include "ChessPieces/ChessPiece.hpp"
#include "Chessboard/Chessboard.hpp"
#include "ChessPieces/ChessPiecePosition.hpp"
#include "Chessboard/ChessboardVisualizerText.hpp"
#include "Chessboard/ChessboardVisualizerGraphic.hpp"
int main(int argc, char* argv[]) {
SetConsoleOutputCP(CP_UTF8);
std::cout << "Spieler A: ";
std::string namePlayerA;
getline(std::cin, namePlayerA);
std::cout << "Spieler B: ";
std::string namePlayerB;
getline(std::cin, namePlayerB);
Player* playerA = new Player(namePlayerA);
Player* playerB = new Player(namePlayerB);
Chessboard chessboard = Chessboard();
chessboard.SetPlayers(playerA, playerB);
// set black chess pieces
chessboard.SetChessPiece(new Rook{ChessPieceColor::Black, ChessPiecePosition{'A', 8}});
chessboard.SetChessPiece(new Knight{ChessPieceColor::Black, ChessPiecePosition{'B', 8}});
chessboard.SetChessPiece(new Bishop{ChessPieceColor::Black, ChessPiecePosition{'C', 8}});
chessboard.SetChessPiece(new Queen{ChessPieceColor::Black, ChessPiecePosition{'D', 8}});
chessboard.SetChessPiece(new King{ChessPieceColor::Black, ChessPiecePosition{'E', 8}});
chessboard.SetChessPiece(new Bishop{ChessPieceColor::Black, ChessPiecePosition{'F', 8}});
chessboard.SetChessPiece(new Knight{ChessPieceColor::Black, ChessPiecePosition{'G', 8}});
chessboard.SetChessPiece(new Rook{ChessPieceColor::Black, ChessPiecePosition{'H', 8}});
chessboard.SetChessPiece(new Pawn{ChessPieceColor::Black, ChessPiecePosition{'A', 7}});
chessboard.SetChessPiece(new Pawn{ChessPieceColor::Black, ChessPiecePosition{'B', 7}});
chessboard.SetChessPiece(new Pawn{ChessPieceColor::Black, ChessPiecePosition{'C', 7}});
chessboard.SetChessPiece(new Pawn{ChessPieceColor::Black, ChessPiecePosition{'D', 7}});
chessboard.SetChessPiece(new Pawn{ChessPieceColor::Black, ChessPiecePosition{'E', 7}});
chessboard.SetChessPiece(new Pawn{ChessPieceColor::Black, ChessPiecePosition{'F', 7}});
chessboard.SetChessPiece(new Pawn{ChessPieceColor::Black, ChessPiecePosition{'G', 7}});
chessboard.SetChessPiece(new Pawn{ChessPieceColor::Black, ChessPiecePosition{'H', 7}});
// set white chess pieces
chessboard.SetChessPiece(new Rook{ChessPieceColor::White, ChessPiecePosition{'A', 1}});
chessboard.SetChessPiece(new Knight{ChessPieceColor::White, ChessPiecePosition{'B', 1}});
chessboard.SetChessPiece(new Bishop{ChessPieceColor::White, ChessPiecePosition{'C', 1}});
chessboard.SetChessPiece(new Queen{ChessPieceColor::White, ChessPiecePosition{'D', 1}});
chessboard.SetChessPiece(new King{ChessPieceColor::White, ChessPiecePosition{'E', 1}});
chessboard.SetChessPiece(new Bishop{ChessPieceColor::White, ChessPiecePosition{'F', 1}});
chessboard.SetChessPiece(new Knight{ChessPieceColor::White, ChessPiecePosition{'G', 1}});
chessboard.SetChessPiece(new Rook{ChessPieceColor::White, ChessPiecePosition{'H', 1}});
chessboard.SetChessPiece(new Pawn{ChessPieceColor::White, ChessPiecePosition{'A', 2}});
chessboard.SetChessPiece(new Pawn{ChessPieceColor::White, ChessPiecePosition{'B', 2}});
chessboard.SetChessPiece(new Pawn{ChessPieceColor::White, ChessPiecePosition{'C', 2}});
chessboard.SetChessPiece(new Pawn{ChessPieceColor::White, ChessPiecePosition{'D', 2}});
chessboard.SetChessPiece(new Pawn{ChessPieceColor::White, ChessPiecePosition{'E', 2}});
chessboard.SetChessPiece(new Pawn{ChessPieceColor::White, ChessPiecePosition{'F', 2}});
chessboard.SetChessPiece(new Pawn{ChessPieceColor::White, ChessPiecePosition{'G', 2}});
chessboard.SetChessPiece(new Pawn{ChessPieceColor::White, ChessPiecePosition{'H', 2}});
// Jetzt kann man Moves machen. Nach jedem Move müssen die ChessPieces aktualisiert werden.
chessboard.UpdateChessPieces();
//ChessboardVisualizerText* visualizer = new ChessboardVisualizerText();
ChessboardVisualizerGraphic* visualizer = new ChessboardVisualizerGraphic();
while (chessboard.IsCheckmate() == false) {
visualizer->Draw(&chessboard);
std::string move;
std::cout << "Move [" << ((chessboard.GetCurrentPlayer()->GetColor() == ChessPieceColor::White) ? "White" : "Black") << "] : ";
std::cin >> move;
chessboard.MoveChessPiece(move);
}
std::cout << "Game is over! - " << chessboard.GetOpponentPlayer()->GetName() << " has won the game!" << std::endl;
/**
std::vector<std::vector<ChessPiece>> chessmatrix;
Chessboard chessboard;
if (argc == 2) {
std::cout << "Spielstand" << std::endl;
chessboard.loadBoard(std::string(argv[1]));
} else {
chessboard.setBoard(chessboard.getStartBoard());
}
//chessboard.init();
chessboard.draw();
while (true) {
std::string move;
std::cout << "Move [" << (chessboard.getTurnOrder() ? "white" : "black") << "]: ";
std::cin >> move;
chessboard.move(move);
chessboard.saveBoard();
}
*/
return 0;
}

12
makefile Normal file
View 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
View 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

View File

@@ -1,34 +0,0 @@
{
"saves": [
[
[],
[],
[],
[],
[],
[],
[],
[]
],
[
[],
[],
[],
[],
[],
[],
[],
[]
],
[
[],
[],
[],
[],
[],
[],
[],
[]
]
]
}

View File

@@ -127,6 +127,54 @@ ChessPiecePosition ChessPiece::GetPosition() {
}
}
/**
* Gets all chess piece positions of the chess piece which are still protective for the king.
* Any position between the attacker chess piece and the current chess piece is protective for the king.
* The position is also protective for the king if the chess piece can beat the attacker chess piece.
* @param chessboard A pointer to the chessboard.
* @param positions A set of positions the chess piece can move next (also positions are not protective).
* @return A set of chess piece positions which are still protective for the king.
*/
std::set<ChessPiecePosition> ChessPiece::GetProtectivePositions(Chessboard* chessboard, std::set<ChessPiecePosition> positions) {
ChessPiece* chessPieceKing = chessboard->GetChessPieceKing(this->GetColor());
std::set<ChessPiecePosition> protectivePositions;
for (ChessPiece* chessPiece : chessboard->GetChessPieces()) {
if (chessPiece->GetColor() != this->GetColor()) {
std::set<ChessPiecePosition> positionsStay = chessPiece->GetNextPositions(chessboard, {});
std::set<ChessPiecePosition> positionsMove = chessPiece->GetNextPositions(chessboard, {this->GetPosition()});
if (positionsStay.count(chessPieceKing->GetPosition()) == 0 && positionsMove.count(chessPieceKing->GetPosition()) == 1) {
// the position is protective if the current chess piece can beat the attacker chess piece.
if (positions.count(chessPiece->GetPosition()) == 1) {
protectivePositions.insert(chessPiece->GetPosition());
}
// every position between the current chess piece and the attacker chess piece is also protective.
std::string step = ChessPiecePosition::GetStep(chessPiece->GetPosition(), chessPieceKing->GetPosition());
if (step.empty()) {
continue;
}
ChessPiecePosition currentPosition = chessPiece->GetPosition();
ChessPiecePosition* nextPosition = &currentPosition;
while (nextPosition = nextPosition->NextFromString(step)) {
if (nextPosition->ToString() == this->GetPosition().ToString()) {
break;
} else if (positions.count(*nextPosition) == 1) {
protectivePositions.insert(*nextPosition);
}
}
}
}
}
return protectivePositions;
}
/**
* Gets a set of all positions along the rank.
* @param chessboard A pointer to the chessboard.
@@ -300,7 +348,7 @@ void ChessPiece::SetPosition(ChessPiecePosition position) {
*/
void ChessPiece::UpdateNextPositions(Chessboard* chessboard) {
if (this->IsProtective(chessboard)) {
this->SetNextPositions({});
this->SetNextPositions(this->GetProtectivePositions(chessboard, this->GetNextPositions(chessboard)));
} else {
this->SetNextPositions(this->GetNextPositions(chessboard));
}

View File

@@ -14,6 +14,8 @@ class ChessPiece {
ChessPieceColor _color;
std::vector<ChessPiecePosition> _positions;
std::set<ChessPiecePosition> _positions_next;
std::set<ChessPiecePosition> GetProtectivePositions(Chessboard* chessboard, std::set<ChessPiecePosition> positions);
bool IsProtective(Chessboard* chessboard);
protected:
char _char = '\0';
@@ -46,7 +48,6 @@ class ChessPiece {
bool IsKing();
bool IsKnight();
bool IsPawn();
bool IsProtective(Chessboard* chessboard);
bool IsQueen();
bool IsRook();
void SetPosition(ChessPiecePosition position);

View 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 : "";
}

View 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

View File

@@ -34,7 +34,35 @@ std::set<ChessPiecePosition> King::GetNextPositions(Chessboard* chessboard, std:
ChessPieceColor opponentColor = (this->GetColor() == ChessPieceColor::Black) ? ChessPieceColor::White : ChessPieceColor::Black;
if (chessboard->IsPositionUnderAttack(*nextPosition, opponentColor) == false) {
positions.insert(*nextPosition);
bool isUnderAttackAfterMove = false;
// Positions are ignored which are still under attack after the king has moved.
// There are fields under attack which are in the shadow of the king.
for (ChessPiece* chessPiece : chessboard->GetChessPieces()) {
if (chessPiece && chessPiece->GetColor() != this->GetColor()) {
if (chessPiece->GetNextPositions().count(this->GetPosition()) == 1) {
if (chessPiece->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);
}
}
}
}

96
src/ChessPieces/Pawn.cpp Normal file
View 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 = &currentPosition;
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 = &currentPosition;
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 = &currentPosition;
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;
}

View 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 = &currentPosition;
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 = &currentPosition;
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 = &currentPosition;
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 = &currentPosition;
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;
}

View 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

View File

@@ -0,0 +1 @@
//#include "../Chessboard/ChessboardVisualizer.hpp"

View File

@@ -1,4 +1,4 @@
#ifndef ChessboardVisualizer_H
/*#ifndef ChessboardVisualizer_H
#define ChessboardVisualizer_H
#include "../ChessPieces/ChessPiece.hpp"
@@ -10,4 +10,4 @@ class ChessboardVisualizer {
virtual void Draw(Chessboard* chessboard) = 0;
};
#endif
#endif*/

View 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;
// }

View 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

View 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
};

View 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;
}
*/

View 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, ' ')));
}*/

View 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

View 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;
}

View 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

View 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);
}

View 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

View 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;
}

View 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

View 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);
}

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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;
}

View 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
View 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;
}