Compare commits

...

43 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
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
Fabian505
d1286469c3 Save and display chessboard over a 2d vector 2025-01-10 22:30:45 +01:00
Fabian505
de0cbf5a8b Refactor Draw method for better readablity (introduce helper methods) 2025-01-10 17:01:22 +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
82 changed files with 4998 additions and 2025 deletions

3
.gitignore vendored
View File

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

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

172
README.md
View File

@@ -1,121 +1,69 @@
# c/c++ Abschlussprojekt - Schach
# TurboSchach
## Notwendige Umsetzungen
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
### 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
- Menü (ASCII Art of TurboSchach)
- Startmenü
- Spielmenü mit Historie, Kommand
- Plattformunabhängiges CMD clearing vor dem Zeichnen
## Kompilieren und Ausführen
Um TurboSchach kompilieren zu können wird mindestens C++ 17 benötigt.
### Windows
```
g++ -std=c++17 -o chess.exe main.cpp ChessPieces/*.cpp Chessboard/*.cpp Player/*.cpp
./chess.exe
```
## 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.
### Linux und MacOS
```
# compile the project
# build
make
# run the project
# run
make run
```
# remove all output files
make clean
```
### Microsoft Windows
```
# 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,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

128
main.cpp
View File

@@ -1,128 +0,0 @@
#ifdef _WIN32
#include <windows.h>
#endif
#include <fstream>
#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[]) {
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
#endif
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();
visualizer->Draw(&chessboard);
// Import chessboard from PGN file.
// https://www.chessgames.com/perl/chessgame?gid=1799484
// 1588906 - Checkmate
// 1482551 - Checkmate
// 1348060 - Checkmate
// 1799484 - Checkmate
// 1284086 - Stalemate
// 1258478 - Stalemate
// 1229276 - En Passant (black fxg3)
// 1775611 - En Passant (white axb6)
// 1427255 - En Passant (white bxc6+ and dxe6)
chessboard.ImportFilePGN("./chessgames/1258478.pgn");
visualizer->Draw(&chessboard);
while (chessboard.IsCheckmate() == false && chessboard.IsStalemate() == false) {
std::string move;
std::cout << "Move [" << ((chessboard.GetCurrentPlayer()->GetColor() == ChessPieceColor::White) ? "White" : "Black") << "] : ";
std::cin >> move;
chessboard.MoveChessPiece(move);
visualizer->Draw(&chessboard);
}
std::cout << "Game is over! - " << chessboard.GetOpponentPlayer()->GetName() << " has won the game!" << std::endl;
/**
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;
}

View File

@@ -1,10 +1,12 @@
all:
g++ -std=c++17 -o chess main.cpp ChessPieces/*.cpp Chessboard/*.cpp Player/*.cpp
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:
./chess
./build/chess
clean:
rm -f main.exe
rm -f chess.exe
rm -f chess
rm -f build/*
rm -f -r doc/html
doc:
doxygen doc/Doxyfile

View File

@@ -1,2 +1,2 @@
& "g++" -std=c++17 -o chess main.cpp ChessPieces/*.cpp Chessboard/*.cpp Player/*.cpp
./chess.exe
& "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

@@ -5,13 +5,10 @@
* @param move The move that uses the short algebraic notation.
*/
ChessPieceMove::ChessPieceMove(std::string move) {
move = this->Normalize(move);
this->_move = this->Normalize(move);
if (this->IsValidShortNotation(move)) {
this->_move = move;
if (this->IsValidShortNotation()) {
this->ParseShortNotation();
} else {
throw std::invalid_argument("invalid move notation");
}
}

View File

@@ -41,6 +41,8 @@ std::set<ChessPiecePosition> King::GetNextPositions(Chessboard* chessboard, std:
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;

View File

@@ -67,7 +67,7 @@ std::set<ChessPiecePosition> Pawn::GetNextPositions(Chessboard* chessboard, std:
// 3.7.3.1. A pawn occupying a square on the same rank as and on an adjacent file to an opponent's pawn which has just advanced two squares
// in one move from its original square may capture this opponent's pawn as though the latter had been moved only one square.
// 3.7.3.2. This capture is only legal on the move following this advance and is called an "en passant" capture.
if (chessboard->HasMoves()) {
if (chessboard->GetCountMoves() > 0) {
std::pair<ChessPiecePosition, ChessPiecePosition> lastMovePositions = chessboard->GetLastMovePositions();
std::string moveDifference = ChessPiecePosition::GetDifference(lastMovePositions.first, lastMovePositions.second);

View File

@@ -1,9 +1,62 @@
#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.
@@ -66,7 +119,7 @@ bool Chessboard::HasMoves() {
* @param filePath The file path of the PGN file to load.
* @return The status whether the PGN file was loaded successfully.
*/
bool Chessboard::ImportFilePGN(std::string filePath) {
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.)*");
@@ -103,19 +156,25 @@ bool Chessboard::ImportFilePGN(std::string filePath) {
pgnFile.close();
if (expectedNumberOfMoves != moves.size()) {
return false;
}
// 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) {
this->MoveChessPiece(move);
std::string status = this->MoveChessPiece(move);
if (status.empty() == false) {
return status;
}
}
return true;
return "";
} else {
return false;
return "file not found!";
}
}
@@ -288,13 +347,6 @@ void Chessboard::SwitchCurrentPlayer() {
this->currentPlayer = this->GetOpponentPlayer();
}
void Chessboard::RemoveChessPiece(ChessPiecePosition position) {
if (this->chessboard.count(position) == 1) {
delete this->chessboard[position];
@@ -312,26 +364,36 @@ ChessPiece* Chessboard::GetChessPiece(ChessPiecePosition* 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;
}
bool Chessboard::MoveCastling(ChessPieceColor color, bool shortCastling) {
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 false;
return "Castling: Chess pieces not found!";
}
if (chessPieceKing->IsKing() == false || chessPieceRook->IsRook() == false) {
return false;
return "Castling: Wrong chess pieces found!";
}
if (chessPieceKing->IsFirstMove() == false || chessPieceRook->IsFirstMove() == false) {
return false;
return "Castling: Chess pieces already moved!";
}
std::string step = ChessPiecePosition::GetStep(chessPieceKing->GetPosition(), toPositionKing);
@@ -344,7 +406,7 @@ bool Chessboard::MoveCastling(ChessPieceColor color, bool shortCastling) {
if (field.second->GetNextPositions().count(*nextPosition) == 0) {
continue;
} else {
return false;
return "Castling: Field " + nextPosition->ToString() + " is under attack!";
}
}
}
@@ -352,7 +414,7 @@ bool Chessboard::MoveCastling(ChessPieceColor color, bool shortCastling) {
break;
}
} else {
return false;
return "Castling: Field " + nextPosition->ToString() + " is not empty!";
}
}
@@ -364,23 +426,46 @@ bool Chessboard::MoveCastling(ChessPieceColor color, bool shortCastling) {
this->chessboard.erase(chessPieceRook->GetPosition());
this->SetToHistory(chessPieceRook->GetPosition(), toPositionRook);
chessPieceRook->SetPosition(toPositionRook);
return true;
return "";
}
void Chessboard::MoveChessPiece(std::string move) {
std::string Chessboard::MoveChessPiece(std::string move) {
ChessPieceMove chessPieceMove = ChessPieceMove(move);
if (chessPieceMove.IsValidShortNotation() == false) {
return;
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()) {
this->MoveCastling(this->GetCurrentPlayer()->GetColor(), true);
std::string status = this->MoveCastling(this->GetCurrentPlayer()->GetColor(), true);
this->draw = {false, false};
if (status.empty() == false) {
return status;
}
} else if (chessPieceMove.IsLongCastling()) {
this->MoveCastling(this->GetCurrentPlayer()->GetColor(), false);
std::string status = this->MoveCastling(this->GetCurrentPlayer()->GetColor(), false);
this->draw = {false, false};
if (status.empty() == false) {
return status;
}
} else if (chessPieceMove.IsDrawOffer()) {
// Remis
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();
@@ -388,28 +473,35 @@ void Chessboard::MoveChessPiece(std::string move) {
for (std::pair<ChessPiecePosition, ChessPiece*> field : this->chessboard) {
if (field.second->GetColor() == this->GetCurrentPlayer()->GetColor()) {
if (field.second->GetChar() == chessPieceMove.GetChessPieceChar() && field.second->GetNextPositions().count(toChessPiecePosition) == 1) {
if (field.second->GetChar() == chessPieceMove.GetChessPieceChar()) {
if (field.second->GetNextPositions().count(toChessPiecePosition) == 1) {
if (fromPositionFile >= 'a' && fromPositionFile <= 'h') {
if (field.second->GetPosition().GetFile() == std::toupper(fromPositionFile)) {
chessPiece = field.second;
break;
} else {
continue;
if ((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;
}
}
}
if (fromPositionRank >= 1 && fromPositionRank <= 8) {
if (field.second->GetPosition().GetRank() == fromPositionRank) {
chessPiece = field.second;
break;
} else {
continue;
}
chessPiece = field.second;
break;
}
chessPiece = field.second;
break;
}
}
}
@@ -503,12 +595,15 @@ void Chessboard::MoveChessPiece(std::string move) {
break;
}
}
} else {
return "chess piece not found!";
}
}
this->SetToHistory(chessPieceMove);
this->UpdateChessPieces();
this->SwitchCurrentPlayer();
return "";
}
@@ -531,8 +626,13 @@ std::vector<ChessPiece*> Chessboard::GetChessPieces() {
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);
}
/**
@@ -541,7 +641,7 @@ std::vector<ChessPiece*> Chessboard::GetChessPieces() {
* @param Player* playerA The first player.
* @param Player* playerB The second player.
*/
void Chessboard::SetPlayers(Player* playerA, Player* playerB) {
void Chessboard::SetPlayersRdm(Player* playerA, Player* playerB) {
std::random_device rngdevice;
std::mt19937 generator(rngdevice());
std::uniform_int_distribution<int> distr(0, 1);
@@ -626,40 +726,189 @@ ChessPiece* Chessboard::GetChessPieceKing(ChessPieceColor color) {
bool Chessboard::ExportFilePGN(std::string filePath) {
std::string moveList;
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();
time_t timestamp = time(NULL);
struct tm datetime = *localtime(&timestamp);
char output[50];
strftime(output, 50, "%Y.%m.%d", &datetime);
/**
[Result "1/2-1/2"]
[ECO "A29"]
[WhiteElo "2750"]
[BlackElo "2595"]
[PlyCount "155"]
*/
moveList.append("[Date \"").append(output).append("\"]").append("\n");
moveList.append("[White \"").append(this->GetPlayer(ChessPieceColor::White)->GetName()).append("\"]").append("\n");
moveList.append("[Black \"").append(this->GetPlayer(ChessPieceColor::Black)->GetName()).append("\"]").append("\n");
for (int moveIndex = 0; moveIndex < moves.size() - 1; moveIndex++) {
if (moveIndex % 6 == 0) {
moveList.append("\n");
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(" ");
}
if (moveIndex % 2 == 0) {
moveList.append(std::to_string((moveIndex / 2) + 1)).append(".");
}
moveList.append(" ").append(moves[moveIndex].ToString()).append(" ");
}
std::cout << moveList;
return true;
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

@@ -1,15 +1,17 @@
#ifndef Chessboard_H
#define Chessboard_H
#include <ctime>
#include <fstream>
#include <iostream>
#include <map>
#include <random>
#include <regex>
#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"
@@ -19,10 +21,22 @@ 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);
@@ -30,13 +44,16 @@ class Chessboard {
~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();
void MoveChessPiece(std::string move);
bool MoveCastling(ChessPieceColor color, bool shortCastling);
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();
@@ -48,12 +65,25 @@ class Chessboard {
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);
bool ImportFilePGN(std::string filePath);
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;
}