I'd like to preface this by saying firstly, my code is absolutely nowhere near complete, and secondly that I am essentially brand new to coding and this is my first real project.
I like to be thrown in the deep end and so have set myself the challenge of making a chess AI using machine learning. I'm still in the logic stage of the chess game itself and have had to rewrite much of it more times than I can count.
Before I get too deep into this, does anyone have any advice how I can improve and ensure the game I make now will be easily usable when I eventually get to the machine learning phase? Mainly I'm looking for techniques I could use to stop having so many nested loops and repetitive code etc. Hopefully you can see where I'm going with the provided code and any advice is helpful advice.
#include <iostream>
#include <map>
enum class PieceType {
KING,
QUEEN,
PAWN,
PAWN_PASSANT,
BISHOP,
ROOK,
HORSE,
EMPTY,
};
enum class PieceColour {
WHITE,
BLACK,
NONE,
};
struct Piece {
PieceType type;
PieceColour colour;
bool EnPassant = false;
bool ExposedKing = false;
};
struct Move {
int x1, x2, y1, y2;
};
class ChessBoard {
private:
Piece board[8][8];
std::map<PieceType, std::string> PieceTypeToString = {
{PieceType::KING, "K"}, {PieceType::QUEEN, "Q"},
{PieceType::BISHOP, "B"}, {PieceType::HORSE, "H"},
{PieceType::PAWN, "P"}, {PieceType::ROOK, "R"},
{PieceType::EMPTY, "X"},
};
std::map<PieceColour, std::string> PieceColourToString = {
{PieceColour::WHITE, "W"},
{PieceColour::BLACK, "B"},
{PieceColour::NONE, "X"},
};
public:
ChessBoard() {
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
PieceType type;
PieceColour colour = (x < 2) ? PieceColour::WHITE : PieceColour::BLACK;
switch (x) {
case 0:
case 7:
if (y == 0 || y == 7) {
type = PieceType::ROOK;
} else if (y == 1 || y == 6) {
type = PieceType::HORSE;
} else if (y == 2 || y == 5) {
type = PieceType::BISHOP;
} else if (y == 3) {
type = PieceType::QUEEN;
} else if (y == 4) {
type = PieceType::KING;
}
break;
case 1:
case 6:
type = PieceType::PAWN;
break;
default:
type = PieceType::EMPTY;
colour = PieceColour::NONE;
}
board[x][y] = {type, colour};
}
}
}
void printboard() {
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
std::cout << PieceTypeToString[board[x][y].type];
std::cout << PieceColourToString[board[x][y].colour] << " ";
}
std::cout << std::endl;
}
}
Piece getPieceAt(int x, int y) const { return board[x][y]; }
void setPieceAt(int x, int y, const Piece &piece) { board[x][y] = piece; }
void Promotion(int x, int y, PieceType PromoPiece) {
board[x][y].type = PromoPiece;
}
Move LastMove;
};
bool Obstructions(int x1, int x2, int y1, int y2, ChessBoard chessBoard) {
int x, y;
Piece CheckSpace = chessBoard.getPieceAt(x, y);
Piece source = chessBoard.getPieceAt(x1, y1);
Piece destination = chessBoard.getPieceAt(x2, y2);
int xSteps = x2 - x1;
int ySteps = y2 - y1;
bool Obstructed;
// check for x obs
// check for y obs
switch (source.type, source.colour) {
case (PieceType::PAWN, PieceColour::WHITE):
for (int i = 0; i <= abs(xSteps); i++) {
CheckSpace = chessBoard.getPieceAt(x1 + i, y1);
if (CheckSpace.type != PieceType::EMPTY) {
Obstructed = false;
} else {
Obstructed = true;
}
}
}
return Obstructed;
}
bool MoveCheck(int x1, int y1, int x2, int y2, ChessBoard &chessBoard) {
Piece CheckSpace;
Piece source = chessBoard.getPieceAt(x1, y1);
Piece destination = chessBoard.getPieceAt(x2, y2);
int xSteps = x2 - x1;
int ySteps = y2 - y1;
int xStepsABS = abs(xSteps);
int yStepsABS = abs(ySteps);
int xDirection;
int yDirection;
if (x2 < 0 || x2 > 7 || y2 < 0 || y2 > 7) {
return false;
}
if (destination.colour == source.colour) {
return false;
}
if (x1 < 0 || x2 > 7 || y1 < 0 || y1 > 7) {
return false;
}
switch (source.type) {
case PieceType::PAWN:
if (!Obstructions(x1, x2, y1, y2, chessBoard)) {
return false;
}
if (Obstructions(x1, x2, y1, y2, chessBoard)) {
return true;
}
if ((source.colour == PieceColour::BLACK && x2 > x1) ||
(source.colour == PieceColour::WHITE && x2 < x1)) {
return false;
}
if ((destination.type == PieceType::EMPTY) &&
(ySteps == 0)) { // PAWN STANDARD MOVES
if (xStepsABS == 1) {
return true; // PAWN MOVE 1 FORWARD
}
if (((xSteps == 2) && (source.colour == PieceColour::WHITE) &&
(x1 == 1)) ||
((xSteps == -2) && (source.colour == PieceColour::BLACK) &&
(x1 == 6))) {
return true; // PAWN MOVE 2 FORWARD
}
}
if (((xStepsABS == 1) && ((ySteps == 1) || (ySteps == -1))) &&
(destination.colour != PieceColour::NONE)) {
if (((source.colour == PieceColour::WHITE) &&
(destination.colour != source.colour) && (x2 > x1)) ||
((source.colour == PieceColour::BLACK) &&
(destination.type != PieceType::EMPTY) && (x2 < x1))) {
return true; // PAWN TAKE MOVE
}
}
if (((x2 == 7) && (source.colour == PieceColour::WHITE)) ||
((x2 == 0) && (source.colour == PieceColour::BLACK))) {
std::cout << "promotion!!! Choose your upgrade (NOT FUNCTIONAL ATM)"
<< std::endl;
}
return false; // END OF PAWN LOGIC
// case PieceType::ROOK:
} // END OF SWITCH !!!!!!!
return false; // END OF MOVE CHECK FUNCTION!!!!!!!!!!!!!!!!
}
void movePiece(ChessBoard &chessBoard) {
// Get player move
int x1, y1, x2, y2;
Piece emptypiece;
emptypiece.type = PieceType::EMPTY;
emptypiece.colour = PieceColour::NONE;
std::cout << "Enter your move (x,y)(x,y): " << std::endl;
std::cin >> x1 >> y1 >> x2 >> y2;
std::cout << "You move the piece at(" << x1 << "," << y1 << ") to (" << x2
<< "," << y2 << ")" << std::endl;
if (!MoveCheck(x1, y1, x2, y2, chessBoard)) {
std::cout << "INVALID MOVE" << std::endl;
return;
}
else if (MoveCheck(x1, y1, x2, y2, chessBoard)) {
Piece piece = chessBoard.getPieceAt(x1, y1);
chessBoard.setPieceAt(x2, y2, piece);
chessBoard.setPieceAt(x1, y1, emptypiece);
return;
}
};
int main() {
ChessBoard chessBoard;
chessBoard.printboard();
while (true) {
movePiece(chessBoard);
chessBoard.printboard();
}
return 0;
};