aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam <adammegarules1@gmail.com>2026-07-27 09:45:58 +0200
committerAdam <adammegarules1@gmail.com>2026-07-27 09:45:58 +0200
commit9b1665430fd0379db92000740c7a70045baa1746 (patch)
tree728993a22f9610a1299c0ef1dd27a63a292b3fc3
parent94cc9240eabf78cbbc9006cb70ca74b6bc3f54e6 (diff)
lot of changes
-rw-r--r--CMakeLists.txt27
-rw-r--r--ChessRules.pdfbin0 -> 300696 bytes
-rw-r--r--src/board.cpp4
-rw-r--r--src/board.hpp16
-rw-r--r--src/fen.cpp7
-rw-r--r--src/main.cpp14
-rw-r--r--src/moves.cpp76
-rw-r--r--src/moves.hpp2
-rw-r--r--src/repl.cpp124
9 files changed, 199 insertions, 71 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a970f50..2ecb495 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,13 +3,10 @@ project(chess)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
-set(CMAKE_C_STANDARD 23)
-set(CMAKE_C_STANDARD_REQUIRED ON)
-
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
-file(GLOB_RECURSE SOURCE_FILES src/*.c src/*.cpp src/*.h src/*.hpp)
+file(GLOB_RECURSE SOURCE_FILES src/*.cpp src/*.hpp)
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
@@ -23,6 +20,28 @@ else()
-Wextra
-Wpedantic
-Werror
+
+ -Wshadow
+ -Wconversion
+ -Wsign-conversion
+ -Wcast-align
+ -Wcast-qual
+ -Wformat=2
+ -Wnull-dereference
+ -Wdouble-promotion
+ -Wimplicit-fallthrough
+ -Wmissing-declarations
+ -Wredundant-decls
+ -Wundef
+ -Wwrite-strings
+ -Wold-style-cast # C++ only
+ -Woverloaded-virtual # C++ only
+ -Wnon-virtual-dtor # C++ only
+ -Wduplicated-cond # GCC
+ -Wduplicated-branches # GCC
+ -Wlogical-op # GCC
+ -Wuseless-cast # C++ only
+ -Wextra-semi # Clang
)
endif()
diff --git a/ChessRules.pdf b/ChessRules.pdf
new file mode 100644
index 0000000..5861575
--- /dev/null
+++ b/ChessRules.pdf
Binary files differ
diff --git a/src/board.cpp b/src/board.cpp
index 54df662..877e8c7 100644
--- a/src/board.cpp
+++ b/src/board.cpp
@@ -6,7 +6,7 @@
#include <print>
#include <string>
-char toChar(pieceType type) {
+char toChar(PieceType type) {
switch (type) {
case NONE:
return '.';
@@ -29,7 +29,7 @@ char toChar(pieceType type) {
}
}
-Piece createPiece(pieceType type, bool color, bool moved) {
+Piece createPiece(PieceType type, bool color, bool moved) {
Piece piece;
piece.type = type;
piece.color = color;
diff --git a/src/board.hpp b/src/board.hpp
index 9e80025..05706ff 100644
--- a/src/board.hpp
+++ b/src/board.hpp
@@ -3,7 +3,7 @@
#include <cstdint>
#include <string>
-enum pieceType {
+enum PieceType {
NONE,
PAWN,
KNIGHT,
@@ -13,11 +13,18 @@ enum pieceType {
KING,
};
-char toChar(pieceType type);
+enum GameState {
+ TURN,
+ WHITE_WON,
+ BLACK_WON,
+ STALEMATE,
+ DRAW,
+};
+char toChar(PieceType type);
struct Piece {
bool color;
- pieceType type;
+ PieceType type;
bool moved;
};
@@ -27,6 +34,7 @@ struct Board {
std::string castle;
uint8_t halfMoveClock;
uint16_t MoveClock;
+ GameState state;
};
struct Position {
@@ -43,7 +51,7 @@ struct Move {
bool operator==(const Move &) const = default;
};
void printBoard(Board *b);
-Piece createPiece(pieceType type, bool color, bool moved = false);
+Piece createPiece(PieceType type, bool color, bool moved = false);
void PlayMove(Move move, Board *b);
int PositionToIndex(Position i);
diff --git a/src/fen.cpp b/src/fen.cpp
index 9ddc795..2808a4c 100644
--- a/src/fen.cpp
+++ b/src/fen.cpp
@@ -2,6 +2,7 @@
#include "board.hpp"
#include <cctype>
#include <cstdio>
+#include <cstdlib>
#include <print>
#include <string>
@@ -81,8 +82,9 @@ void setBoardFen(std::string fen, Board *b) {
if (isdigit(fen[i])) {
int move = fen[i] - '0';
file += move;
- if (file > 8) {
+ if (file >= 8) {
std::println("Fatal: file was to big");
+ exit(1);
return; // malformed rank
}
continue;
@@ -90,8 +92,9 @@ void setBoardFen(std::string fen, Board *b) {
if (fen[i] == '/') {
file = 0;
rank++;
- if (rank > 8) {
+ if (rank >= 8) {
std::println("Fatal: rank was to big");
+ exit(1);
return; // malformed rank
}
continue;
diff --git a/src/main.cpp b/src/main.cpp
index 4b9e965..b8dd297 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -2,8 +2,6 @@
#include "fen.hpp"
#include "repl.hpp"
#include <cstdio>
-#include <iostream>
-#include <print>
#include <string>
using namespace std;
@@ -25,6 +23,7 @@ void setAllPiecesToEmpty(Board *b) {
};
Board initBoard(string startingFEN) {
Board b;
+ b.state = TURN;
b.turn = true;
b.castle = "";
b.halfMoveClock = 0;
@@ -32,6 +31,7 @@ Board initBoard(string startingFEN) {
setBoardFen(startingFEN, &b);
return b;
};
+#define askMode = false
int main(int argc, char **argv) {
std::string startingFen =
@@ -44,16 +44,8 @@ int main(int argc, char **argv) {
if (argc == 2) {
startingFen = argv[1];
}
- println("ENTER which mode you want (REPL)");
- Mode mode = EXIT;
- string modeString;
- cin >> modeString;
- std::transform(modeString.begin(), modeString.end(), modeString.begin(),
- ::toupper);
- if (modeString == "REPL") {
- mode = REPL;
- }
+ Mode mode = REPL;
// ADD support for other modes here
Board b = initBoard(startingFen);
diff --git a/src/moves.cpp b/src/moves.cpp
index 0059039..7efd00a 100644
--- a/src/moves.cpp
+++ b/src/moves.cpp
@@ -2,9 +2,38 @@
#include <cassert>
#include <cstdint>
#include <sys/types.h>
+#include <vector>
#include "board.hpp"
#include "moves.hpp"
+void GenerateKnightMoves(Board *b, int from, std::vector<Move> &moves) {
+
+ Piece knight = b->pieces[from];
+ if (knight.type != KNIGHT) {
+ assert(false && "Calling generate knight moves on non knight");
+ return;
+ }
+ // Knight moves: https://www.chessprogramming.org/Knight_Pattern
+ constexpr std::array<int, 8> knight_moves{-10, 6, 15, 17, 10, -6, -15, -17};
+
+ for (int offset : knight_moves) {
+ int next = from + offset;
+ if (next >= 64 || next < 0) {
+ continue;
+ }
+ const int fileDelta = std::abs((next % 8) - (from % 8));
+ if (fileDelta != 1 && fileDelta != 2) {
+ continue;
+ };
+ if (b->pieces[next].type != NONE) {
+ if (b->pieces[next].color == b->turn) {
+ continue;
+ };
+ }
+
+ moves.push_back({IndexToPosition(from), IndexToPosition(next)});
+ }
+};
void GeneratePawnMoves(Board *b, int from, std::vector<Move> &moves) {
Piece pawn = b->pieces[from];
if (pawn.type != PAWN) {
@@ -49,7 +78,7 @@ void GeneratePawnMoves(Board *b, int from, std::vector<Move> &moves) {
// the end
}
-std::vector<Move> GetLegalMoves(Board *b) {
+std::vector<Move> GetPseudoLegalMoves(Board *b) {
std::vector<Move> moves;
moves.reserve(50); // almost all position dont have that many moves
@@ -69,6 +98,9 @@ std::vector<Move> GetLegalMoves(Board *b) {
if (piece.type == PAWN) {
GeneratePawnMoves(b, i, moves);
}
+ if (piece.type == KNIGHT) {
+ GenerateKnightMoves(b, i, moves);
+ };
if (piece.type == BISHOP) {
GenerateSlidingMoves(b, i, bishop_Moves, moves);
}
@@ -86,6 +118,48 @@ std::vector<Move> GetLegalMoves(Board *b) {
return moves;
}
+std::vector<Move> GetLegalMoves(Board *b) {
+ std::vector<Move> moves = GetPseudoLegalMoves(b);
+ std::vector<Move> legalMove;
+ for (Move move : moves) {
+ bool isLegal = true;
+ Board testBoard = *b;
+ PlayMove(move, &testBoard);
+ testBoard.turn = !testBoard.turn;
+ auto opponentResponse = GetPseudoLegalMoves(&testBoard);
+ for (Move move : opponentResponse) {
+ if (testBoard.pieces[PositionToIndex(move.To)].type == KING) {
+ isLegal = false;
+ }
+ }
+ if (isLegal) {
+ legalMove.push_back(move);
+ };
+ }
+ if (legalMove.size() == 0) {
+ bool isCheck = false;
+
+ Board testBoard = *b;
+ testBoard.turn = !testBoard.turn;
+ auto opponentResponse = GetPseudoLegalMoves(&testBoard);
+ for (Move move : opponentResponse) {
+ if (testBoard.pieces[PositionToIndex(move.To)].type == KING) {
+ isCheck = true;
+ }
+ }
+
+ if (isCheck) {
+ if (b->turn) {
+ b->state = BLACK_WON;
+ } else {
+ b->state = WHITE_WON;
+ };
+ } else {
+ b->state = STALEMATE;
+ }
+ }
+ return legalMove;
+}
Position IndexToPosition(int i) {
uint8_t rank = i / 8; // 0-7
diff --git a/src/moves.hpp b/src/moves.hpp
index ae26e29..a957d3a 100644
--- a/src/moves.hpp
+++ b/src/moves.hpp
@@ -5,11 +5,13 @@
#include <vector>
std::vector<Move> GetLegalMoves(Board *b);
+std::vector<Move> GetPseudoLegalMoves(Board *b);
Position IndexToPosition(int i);
void GenerateSlidingMoves(Board *b, int from,
const std::array<int, 4> &directions,
std::vector<Move> &moves);
void GenerateKingMoves(Board *b, int from, std::vector<Move> &moves);
+void GenerateKningtMoves(Board *b, int from, std::vector<Move> &moves);
void GeneratePawnMoves(Board *b, int from, std::vector<Move> &moves);
#endif /* SRC_MOVES_H_ */
diff --git a/src/repl.cpp b/src/repl.cpp
index 642419a..ffa52b6 100644
--- a/src/repl.cpp
+++ b/src/repl.cpp
@@ -25,12 +25,36 @@ bool isValidChessFile(char rank) {
}
int startREPL(Board *b) {
printBoard(b);
+ GetLegalMoves(b);
while (true) {
+ if (b->state == STALEMATE) {
+ printBoard(b);
+ std::println();
+ std::cout << "\033[1mStalemate\033[0m" << "\n";
+ std::println();
+ return 0;
+ }
+ if (b->state == WHITE_WON) {
+ printBoard(b);
+ std::cout << "\033[1mWhite won\033[0m" << "\n";
+ return 0;
+ }
+ if (b->state == BLACK_WON) {
+ printBoard(b);
+ std::cout << "\033[1mBlack won\033[0m" << "\n";
+ return 0;
+ }
std::cout << "> ";
std::string command;
- std::cin >> command;
+ if (!(std::cin >> command)) {
+ std::println();
+ std::cout << "Exiting...\n";
+ return EXIT_SUCCESS;
+ }
std::transform(command.begin(), command.end(), command.begin(), ::toupper);
+
+ std::cout << command << "\n";
if (command == "EXIT") {
std::println();
std::cout << "Exiting...\n";
@@ -40,61 +64,67 @@ int startREPL(Board *b) {
printBoard(b);
continue;
}
-
- if (command.length() != 4) {
- std::cout << "Unknown Command, use EXIT to exit";
- std::println();
+ if (command == "MOVES" || command == "MOVE") {
+ auto moves = GetLegalMoves(b);
+ PrintMoves(moves);
continue;
}
- if (!isValidChessFile(command[0])) {
- std::cout << "ERROR: Expected valid file at first place";
+ if (command.length() != 4) {
+ std::cout << "Unknown Command, use EXIT to exit";
std::println();
continue;
}
+ if (b->state == TURN) {
+ if (!isValidChessFile(command[0])) {
+ std::cout << "ERROR: Expected valid file at first place";
+ std::println();
+ continue;
+ }
- if (!isValidChessRank(command[1])) {
- std::cout << "ERROR: Expected valid rank at second place";
- std::println();
- continue;
- }
+ if (!isValidChessRank(command[1])) {
+ std::cout << "ERROR: Expected valid rank at second place";
+ std::println();
+ continue;
+ }
- if (!isValidChessFile(command[2])) {
- std::cout << "ERROR: Expected valid file at third place";
- std::println();
- continue;
- }
+ if (!isValidChessFile(command[2])) {
+ std::cout << "ERROR: Expected valid file at third place";
+ std::println();
+ continue;
+ }
- if (!isValidChessRank(command[3])) {
- std::cout << "ERROR: Expected valid rank at four place";
- std::println();
- continue;
- }
+ if (!isValidChessRank(command[3])) {
+ std::cout << "ERROR: Expected valid rank at four place";
+ std::println();
+ continue;
+ }
- // we know its a valid chess pos
- int fromFile = command[0] - 'A';
- int fromRank = '8' - command[1];
- int toFile = command[2] - 'A';
- int toRank = '8' - command[3];
- std::cout << fromFile << "\n";
- std::cout << fromRank << "\n";
- std::cout << toFile << "\n";
- std::cout << toRank << "\n";
- Move move = {
- {static_cast<uint8_t>(fromRank), static_cast<uint8_t>(fromFile)},
- {static_cast<uint8_t>(toRank), static_cast<uint8_t>(toFile)}};
+ // we know its a valid chess pos
+ int fromFile = command[0] - 'A';
+ int fromRank = '8' - command[1];
+ int toFile = command[2] - 'A';
+ int toRank = '8' - command[3];
+ std::cout << fromFile << "\n";
+ std::cout << fromRank << "\n";
+ std::cout << toFile << "\n";
+ std::cout << toRank << "\n";
+ Move move = {
+ {static_cast<uint8_t>(fromRank), static_cast<uint8_t>(fromFile)},
+ {static_cast<uint8_t>(toRank), static_cast<uint8_t>(toFile)}};
- auto moves = GetLegalMoves(b);
- if (!(std::ranges::find(moves, move) != moves.end())) {
- std::cout << "Invalid move!";
+ auto moves = GetLegalMoves(b);
+ if (!(std::ranges::find(moves, move) != moves.end())) {
+ std::cout << "Invalid move!";
+ std::println();
+ PrintMoves(moves);
+ continue;
+ }
std::println();
- PrintMoves(moves);
- continue;
+ PlayMove(move, b);
+ b->MoveClock++;
+ b->turn = !b->turn;
+ printBoard(b);
}
- std::println();
- PlayMove(move, b);
- b->MoveClock++;
- b->turn = !b->turn;
- printBoard(b);
}
}
@@ -102,8 +132,8 @@ void PrintMoves(const std::vector<Move> &moves) {
std::cout << "Moves (" << moves.size() << "):\n";
for (const Move &move : moves) {
- std::cout << "(" << (int)move.From.file << ", " << (int)move.From.rank
- << ") -> (" << (int)move.To.file << ", " << (int)move.To.rank
- << ")\n";
+ std::cout << (char)((int)move.From.file + 'A') << 8 - (int)move.From.rank
+ << " -> " << (char)((int)move.To.file + 'A')
+ << 8 - (int)move.To.rank << "\n";
}
}