aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/cmake-single-platform.yml43
-rw-r--r--CMakeLists.txt22
-rw-r--r--README.md1
-rw-r--r--src/board.cpp189
-rw-r--r--src/board.hpp20
-rw-r--r--src/fen.cpp139
-rw-r--r--src/fen.hpp9
-rw-r--r--src/main.cpp114
-rw-r--r--src/moves.cpp10
-rw-r--r--src/moves.hpp18
10 files changed, 374 insertions, 191 deletions
diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml
new file mode 100644
index 0000000..844556e
--- /dev/null
+++ b/.github/workflows/cmake-single-platform.yml
@@ -0,0 +1,43 @@
+# This starter workflow is for a CMake project running on a single platform. There is a different starter workflow if you need cross-platform coverage.
+# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-multi-platform.yml
+name: CMake on a single platform
+
+on:
+ push:
+ branches: ["main"]
+ pull_request:
+ branches: ["main"]
+
+env:
+ # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
+ BUILD_TYPE: Debug
+
+jobs:
+ build:
+ # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
+ # You can convert this to a matrix build if you need cross-platform coverage.
+ # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install GCC 14
+ run: |
+ sudo apt update
+ sudo apt install -y gcc-14 g++-14
+
+ - name: Configure CMake
+ env:
+ CC: gcc-14
+ CXX: g++-14
+ run: cmake -B build -S . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
+
+ - name: Build
+ env:
+ CC: gcc-14
+ CXX: g++-14
+ run: cmake --build build
+
+ - name: Run clang-tidy
+ run: clang-tidy src/*.cpp -p build
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e8858ca..3f5a42f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -15,9 +15,21 @@ add_executable(${PROJECT_NAME} ${SOURCE_FILES})
target_include_directories(${PROJECT_NAME} PRIVATE src)
-include(CTest)
-if(BUILD_TESTING)
- add_executable(pawn_moves_test tests/pawn_moves_test.cpp src/board.cpp src/moves.cpp)
- target_include_directories(pawn_moves_test PRIVATE src)
- add_test(NAME pawn_moves COMMAND pawn_moves_test)
+if (MSVC)
+ target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX)
+else()
+ target_compile_options(${PROJECT_NAME} PRIVATE
+ -Wall
+ -Wextra
+ -Wpedantic
+ -Werror
+ )
endif()
+
+target_compile_options(${PROJECT_NAME} PRIVATE
+ -fsanitize=address,undefined
+)
+
+target_link_options(${PROJECT_NAME} PRIVATE
+ -fsanitize=address,undefined
+)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f55aeca
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+# NO AI CODE ALLOWED
diff --git a/src/board.cpp b/src/board.cpp
index 5c30d75..172a9be 100644
--- a/src/board.cpp
+++ b/src/board.cpp
@@ -1,14 +1,12 @@
#include "board.hpp"
+#include <cassert>
#include <cctype>
#include <cstdio>
#include <iostream>
#include <print>
#include <string>
-bool WHITE = true;
-bool BLACK = false;
-
-const char toChar(pieceType type) {
+char toChar(pieceType type) {
switch (type) {
case NONE:
return '.';
@@ -25,6 +23,8 @@ const char toChar(pieceType type) {
case KING:
return 'K';
default:
+ std::cout << "\n" << type << "\n";
+ assert(false && "Unknown piece" && type);
return '?';
}
}
@@ -38,158 +38,55 @@ Piece createPiece(pieceType type, bool color, bool moved) {
};
void printBoard(board *b) {
std::string line = " +-----------------+\n";
- std::string letter = " a b c d e f g h\n";
+ std::string whiteLetters = " a b c d e f g h\n";
+ std::string blackLetters = " h g f e d c b a\n";
- std::cout << letter;
+ std::cout << (b->turn ? whiteLetters : blackLetters);
std::cout << line;
- for (int rank = 0; rank < 8; rank++) {
- std::printf("%d |", 8 - rank);
- for (int file = 0; file < 8; file++) {
- Piece piece = b->pieces[rank * 8 + file];
- char symbol = toChar(piece.type);
- if (piece.color == BLACK) {
- symbol = std::tolower(static_cast<unsigned char>(symbol));
+ if (b->turn) {
+ for (int rank = 0; rank < 8; rank++) {
+ std::printf("%d |", 8 - rank);
+
+ for (int file = 0; file < 8; file++) {
+ Piece piece = b->pieces[rank * 8 + file];
+ char symbol = toChar(piece.type);
+
+ if (!piece.color)
+ symbol = std::tolower(static_cast<unsigned char>(symbol));
+
+ std::printf(" %c", symbol);
}
- std::printf(" %c", symbol);
+
+ std::printf(" | %d\n", 8 - rank);
+ }
+ } else {
+ for (int rank = 7; rank >= 0; rank--) {
+ std::printf("%d |", 8 - rank);
+
+ for (int file = 7; file >= 0; file--) {
+ Piece piece = b->pieces[rank * 8 + file];
+ char symbol = toChar(piece.type);
+
+ if (!piece.color)
+ symbol = std::tolower(static_cast<unsigned char>(symbol));
+
+ std::printf(" %c", symbol);
+ }
+
+ std::printf(" | %d\n", 8 - rank);
}
- std::printf(" | %d\n", 8 - rank);
}
std::cout << line;
- std::cout << letter;
+ std::cout << (b->turn ? whiteLetters : blackLetters);
std::println();
- std::println("Turn: {}", b->turn == WHITE ? "White" : "Black");
+ std::println("Turn: {}", b->turn ? "White" : "Black");
+
+ assert(b->castle != "");
+
std::println("Castling: {}", b->castle);
}
-void setBoardFen(std::string fen, board *b) {
- // example fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
- int file = 0;
- int rank = 0;
-
- std::string castle = "";
-
- enum parserState {
- POSITION,
- TURN,
- CASTLE,
- ENPASSANT,
- CLOCK1,
- CLOCK2,
- };
- parserState state = POSITION;
-
- for (int i = 0; i < fen.length(); i++) {
- std::printf("state: %d\n", state);
- std::printf("doing: %c\n", fen[i]);
- if (fen[i] == ' ') {
- if (state == POSITION) {
- state = TURN;
- continue;
- }
- if (state == TURN) {
- state = CASTLE;
- continue;
- }
- if (state == CASTLE) {
- state = ENPASSANT;
- continue;
- }
- if (state == ENPASSANT) {
- state = CLOCK1;
- continue;
- }
- if (state == CLOCK1) {
- state = CLOCK2;
- continue;
- }
- }
- if (state == ENPASSANT) {
- continue;
- }
- if (state == CASTLE) {
- if (fen[i] == '-') {
- castle = "-";
- continue;
- }
- if (toupper(fen[i]) == 'K') {
- castle += fen[i];
- continue;
- }
- if (toupper(fen[i]) == 'Q') {
- castle += fen[i];
- continue;
- }
- }
- if (state == TURN) {
- if (toupper(fen[i]) == 'W') {
- b->turn = WHITE;
- continue;
- }
- if (toupper(fen[i]) == 'B') {
- b->turn = BLACK;
- continue;
- }
- }
- if (state == POSITION) {
- if (isdigit(fen[i])) {
- int move = fen[i] - '0';
- file += move;
- if (file > 8) {
- std::println("Fatal: file was to big");
- return; // malformed rank
- }
- continue;
- }
- if (fen[i] == '/') {
- file = 0;
- rank++;
- if (rank > 8) {
- std::println("Fatal: rank was to big");
- return; // malformed rank
- }
- continue;
- }
- if (toupper(fen[i]) == 'N') {
- Piece piece = createPiece(KNIGHT, isupper(fen[i]));
- b->pieces[rank * 8 + file] = piece;
- file++;
- continue;
- }
- if (toupper(fen[i]) == 'R') {
- Piece piece = createPiece(ROOK, isupper(fen[i]));
- b->pieces[rank * 8 + file] = piece;
- file++;
- continue;
- }
- if (toupper(fen[i]) == 'Q') {
- Piece piece = createPiece(QUEEN, isupper(fen[i]));
- b->pieces[rank * 8 + file] = piece;
- file++;
- continue;
- }
- if (toupper(fen[i]) == 'B') {
- Piece piece = createPiece(BISHOP, isupper(fen[i]));
- b->pieces[rank * 8 + file] = piece;
- file++;
- continue;
- }
- if (toupper(fen[i]) == 'P') {
- Piece piece = createPiece(PAWN, isupper(fen[i]));
- b->pieces[rank * 8 + file] = piece;
- file++;
- continue;
- }
- if (toupper(fen[i]) == 'K') {
- Piece piece = createPiece(KING, isupper(fen[i]));
- b->pieces[rank * 8 + file] = piece;
- file++;
- continue;
- }
- }
- break;
- }
- b->castle = castle.empty() ? "-" : castle;
-}
+// void PlayMove(Move move, board *b) { return; }
diff --git a/src/board.hpp b/src/board.hpp
index 3bf581a..afdb938 100644
--- a/src/board.hpp
+++ b/src/board.hpp
@@ -13,7 +13,7 @@ enum pieceType {
KING,
};
-const char toChar(pieceType type);
+char toChar(pieceType type);
struct Piece {
bool color;
@@ -29,7 +29,21 @@ struct board {
uint16_t MoveClock;
};
+struct Position {
+ uint8_t rank;
+ uint8_t file;
+
+ bool operator==(const Position &) const = default;
+};
+
+struct Move {
+ Position From;
+ Position To;
+
+ bool operator==(const Move &) const = default;
+};
void printBoard(board *b);
-void setBoardFen(std::string fen, board *b);
Piece createPiece(pieceType type, bool color, bool moved = false);
-#endif /* SRC_BOARD_H_ */
+
+void PlayMove(Move move, board *b); // TODO implement
+#endif /* SRC_BOARD_H_ */
diff --git a/src/fen.cpp b/src/fen.cpp
new file mode 100644
index 0000000..9c3154c
--- /dev/null
+++ b/src/fen.cpp
@@ -0,0 +1,139 @@
+#include "fen.hpp"
+#include "board.hpp"
+#include <cctype>
+#include <cstdio>
+#include <print>
+#include <string>
+
+bool WHITE = true;
+bool BLACK = false;
+
+void setBoardFen(std::string fen, board *b) {
+ // example fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
+ int file = 0;
+ int rank = 0;
+
+ std::string castle = "";
+
+ enum parserState {
+ POSITION,
+ TURN,
+ CASTLE,
+ ENPASSANT,
+ CLOCK1,
+ CLOCK2,
+ };
+ parserState state = POSITION;
+
+ for (uint i = 0; i < fen.length(); i++) {
+ std::printf("state: %d\n", state);
+ std::printf("doing: %c\n", fen[i]);
+ if (fen[i] == ' ') {
+ if (state == POSITION) {
+ state = TURN;
+ continue;
+ }
+ if (state == TURN) {
+ state = CASTLE;
+ continue;
+ }
+ if (state == CASTLE) {
+ state = ENPASSANT;
+ continue;
+ }
+ if (state == ENPASSANT) {
+ state = CLOCK1;
+ continue;
+ }
+ if (state == CLOCK1) {
+ state = CLOCK2;
+ continue;
+ }
+ }
+ if (state == ENPASSANT) {
+ continue;
+ }
+ if (state == CASTLE) {
+ if (fen[i] == '-') {
+ castle = "-";
+ continue;
+ }
+ if (toupper(fen[i]) == 'K') {
+ castle += fen[i];
+ continue;
+ }
+ if (toupper(fen[i]) == 'Q') {
+ castle += fen[i];
+ continue;
+ }
+ }
+ if (state == TURN) {
+ if (toupper(fen[i]) == 'W') {
+ b->turn = WHITE;
+ continue;
+ }
+ if (toupper(fen[i]) == 'B') {
+ b->turn = BLACK;
+ continue;
+ }
+ }
+ if (state == POSITION) {
+ if (isdigit(fen[i])) {
+ int move = fen[i] - '0';
+ file += move;
+ if (file > 8) {
+ std::println("Fatal: file was to big");
+ return; // malformed rank
+ }
+ continue;
+ }
+ if (fen[i] == '/') {
+ file = 0;
+ rank++;
+ if (rank > 8) {
+ std::println("Fatal: rank was to big");
+ return; // malformed rank
+ }
+ continue;
+ };
+ if (toupper(fen[i]) == 'N') {
+ Piece piece = createPiece(KNIGHT, isupper(fen[i]));
+ b->pieces[rank * 8 + file] = piece;
+ file++;
+ continue;
+ };
+ if (toupper(fen[i]) == 'R') {
+ Piece piece = createPiece(ROOK, isupper(fen[i]));
+ b->pieces[rank * 8 + file] = piece;
+ file++;
+ continue;
+ }
+ if (toupper(fen[i]) == 'Q') {
+ Piece piece = createPiece(QUEEN, isupper(fen[i]));
+ b->pieces[rank * 8 + file] = piece;
+ file++;
+ continue;
+ }
+ if (toupper(fen[i]) == 'B') {
+ Piece piece = createPiece(BISHOP, isupper(fen[i]));
+ b->pieces[rank * 8 + file] = piece;
+ file++;
+ continue;
+ }
+ if (toupper(fen[i]) == 'P') {
+ Piece piece = createPiece(PAWN, isupper(fen[i]));
+ b->pieces[rank * 8 + file] = piece;
+ file++;
+ continue;
+ }
+ if (toupper(fen[i]) == 'K') {
+ Piece piece = createPiece(KING, isupper(fen[i]));
+ b->pieces[rank * 8 + file] = piece;
+ file++;
+ continue;
+ }
+ }
+ break;
+ }
+ b->castle = castle.empty() ? "-" : castle;
+}
diff --git a/src/fen.hpp b/src/fen.hpp
new file mode 100644
index 0000000..19f88e2
--- /dev/null
+++ b/src/fen.hpp
@@ -0,0 +1,9 @@
+#ifndef SRC_FEN_H_
+#define SRC_FEN_H_
+
+#include <string>
+
+#include "board.hpp"
+
+void setBoardFen(std::string fen, board *b);
+#endif /* SRC_FEN_H_ */
diff --git a/src/main.cpp b/src/main.cpp
index 5be1194..261c19d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,17 +1,41 @@
+#include "board.hpp"
+#include "fen.hpp"
+#include "moves.hpp"
+#include <algorithm>
+#include <cctype>
+#include <cstdint>
#include <cstdio>
#include <cstdlib>
+#include <print>
#include <string>
+#include <vector>
-#include "board.hpp"
-#include "moves.hpp"
-
-Piece NonePiece = {
- false,
- NONE,
- false,
-};
#include <iostream>
+bool isValidChessRank(char rank) {
+ if (rank >= '1' && rank <= '8') {
+ return true;
+ } else {
+ return false;
+ }
+}
+bool isValidChessFile(char rank) {
+ if (rank >= 'A' && rank <= 'H') {
+ return true;
+ } else {
+ return false;
+ }
+}
+void setAllPiecesToEmpty(board *b) {
+ Piece NonePiece = {
+ false,
+ NONE,
+ false,
+ };
+ for (int i = 0; i < 64; i++) {
+ b->pieces[i] = NonePiece;
+ };
+}
void PrintMoves(const std::vector<Move> &moves) {
std::cout << "Moves (" << moves.size() << "):\n";
@@ -37,12 +61,76 @@ int main(int argc, char **argv) {
b.castle = "";
b.halfMoveClock = 0;
b.MoveClock = 0;
- for (int i = 0; i < 64; i++) {
- b.pieces[i] = NonePiece;
- };
setBoardFen(startingFen, &b);
printBoard(&b);
- auto moves = GetLegalMoves(&b);
- PrintMoves(moves);
+ while (true) {
+ std::cout << "> ";
+ std::string command;
+ std::cin >> command;
+ std::cout << command << "\n";
+
+ transform(command.begin(), command.end(), command.begin(), ::toupper);
+ if (command == "EXIT") {
+ std::println();
+ std::cout << "Exiting...\n";
+ return 0;
+ }
+
+ if (command.length() != 4) {
+ std::cout << "Unknown Command, use EXIT to exit";
+ std::println();
+ continue;
+ }
+ 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 (!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;
+ }
+
+ // 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 << "Move is legal!\n";
+ } else {
+ std::cout << "Move is not legal!\n";
+ PrintMoves(moves);
+ continue;
+ }
+ std::println();
+ // PlayMove(move, &b); // todo implement
+ b.MoveClock++;
+ b.turn = !b.turn;
+ printBoard(&b);
+ }
return EXIT_SUCCESS;
}
diff --git a/src/moves.cpp b/src/moves.cpp
index e36d77c..0a997db 100644
--- a/src/moves.cpp
+++ b/src/moves.cpp
@@ -1,5 +1,6 @@
#include <array>
#include <cstdint>
+#include <sys/types.h>
#include "board.hpp"
#include "moves.hpp"
@@ -62,8 +63,7 @@ std::vector<Move> GetLegalMoves(board *b)
constexpr std::array<int, 4> rook_Moves{-1, 1, 8, -8};
constexpr std::array<int, 4> bishop_Moves{-9, 9, -7, 7};
- for (int i = 0; i < sizeof(b->pieces) / sizeof(b->pieces[0]); i++)
- {
+ for (uint i = 0; i < sizeof(b->pieces) / sizeof(b->pieces[0]); i++) {
Piece piece = b->pieces[i];
if (piece.type == NONE)
{
@@ -105,10 +105,8 @@ Position IndexToPosition(int i)
void GenerateSlidingMoves(board *b, int from,
const std::array<int, 4> &directions,
- std::vector<Move> &moves)
-{
- for (int i = 0; i < directions.size(); i++)
- {
+ std::vector<Move> &moves) {
+ for (uint i = 0; i < directions.size(); i++) {
int direction = directions[i];
int i2 = from;
diff --git a/src/moves.hpp b/src/moves.hpp
index 7e95910..d99fc68 100644
--- a/src/moves.hpp
+++ b/src/moves.hpp
@@ -2,26 +2,8 @@
#define SRC_MOVES_H_
#include "board.hpp"
-#include <cstdint>
#include <vector>
-struct Position {
- uint8_t rank;
- uint8_t file;
-};
-
-struct Move {
- Position From;
- Position To;
-};
-
-enum MoveResult {
- SUCCES,
- FRIENDLY_PIECE,
- PIECE_IN_WAY,
-};
-
-MoveResult PlayMove(Move move, board *b); // TODO implement
std::vector<Move> GetLegalMoves(board *b);
Position IndexToPosition(int i);
void GenerateSlidingMoves(board *b, int from,