I am currently writing a chess engine to improve my C++. I was able to improve my first results in terms of performance. (It is still really weak, but it does not lose a queen in one move)
I ended up having massive problems regarding namespaces in move generation.
It is really not great.
Here is some code:
#pragma once
#include <list>
#include <memory>
#include <string>
#include <vector>
#include <thread>
#include <functional>
#include "Board.h"
#include "Node.h"
#include "Utility.h"
namespace OdinConstants {
static const std::string standardBoardFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
static const double cpuct = 2.0f;
}
class Node;
class Odin {
public:
Odin();
long positions_calculated_{0};
std::shared_ptr<Node> start_node_;
inline void searchOn() {
searching_ = true;
setUpForCalculations();
}
inline void searchOff() {
searching_ = false;
}
void search();
void setPosition(const std::string& fen, const std::vector<std::string>& moves);
inline void setPosition(const Board& board) {
start_node_ =
std::make_shared<Node>(board,std::nullopt, std::nullopt, nullptr);
positions_calculated_ = 0;
}
static double evaluatePosition(const Board &board);
std::tuple<int, int, Figure> bestMove() const;
private:
std::thread computingThread_{};
bool searching_{false};
bool in_chess_{true};
void setUpForCalculations();
void computeNext();
};
/* //////////////////////////////////////////////////
* Move Generation
*///////////////////////////////////////////////////
Board makeMove(const Board &b, std::tuple<int, int, Figure>);
/*
* Checks if king could be "captured" in the next move and would therefore be in
* check.
*/
bool isCheck(const Board &board, Color to_be_checked);
//all moves disregardingCheck
void generateAllMoves(std::vector<std::tuple<int, int, Figure>>& moves, const Board& board);
//Checks if any move, can reach this field
bool hasMoveToField(std::vector<std::tuple<int, int, Figure>>& moves, const Board& board, int to_field);
//all moves regarding check
void generateAllLegalMoves(std::vector<std::tuple<int, int, Figure>> &moves, const Board& board);
/*
* filters all Moves which would be illegal, because color would check itself or
* not escape a check
*/
void extractLegalMoves(std::vector<std::tuple<int, int, Figure>>& moves, const Board & board,
std::function<void(std::vector<std::tuple<int, int, Figure>>&, const Board &)> generator);
namespace PAWNMOVES {
void generateAllPawnMovesWithWhite(std::vector<std::tuple<int, int, Figure>> &pawn_moves, const Board &board, int field_num);
void generateAllPawnMovesWithBlack(std::vector<std::tuple<int, int, Figure>> &pawn_moves, const Board &board, int field_num);
void generateAllPawnMoves(std::vector<std::tuple<int, int, Figure>>& , const Board& board);
}
namespace KNIGHTMOVES {
void addIfMoveable(std::vector<std::tuple<int, int, Figure>>& moves, const int fromi, const int fromj, const int toi, const int toj, const Board& b);
void generateAllKnightMoves(std::vector<std::tuple<int, int, Figure>> &, const Board &board);
}
namespace LONGRANGEPIECEMOVES {
template <int dX, int dY> void generateMoves(std::vector<std::tuple<int, int, Figure>>& moves, const Board& board, const int y, const int x);
void generateAllBishopMoves(std::vector<std::tuple<int, int, Figure>>& , const Board& board);
void generateAllRookMoves(std::vector<std::tuple<int, int, Figure>>& , const Board& board);
void generateAllQueenMoves(std::vector<std::tuple<int, int, Figure>>&, const Board& board);
}
namespace KINGMOVES {
/*
* Generates a king step in any direction. Where dX, dY is the directrion.
* For some weird reason, I do not fully understand: generateOneSteps needs to be header defined.
*/
template<int dX, int dY>
void generateOneSteps(const int j, const int i,
std::vector<std::tuple<int, int, Figure>> &moves,
const Board &board) {
int toi = i + dY;
int toj = j + dX;
if (!inBounds(toi, toj)) {
return;
}
if (board[toi][toj] == EMPTY.value()) {
moves.push_back(std::make_tuple((8 * i + j), (8 * toi + toj), EMPTY));
} else if (board[toi][toj] * (static_cast<int>(board.to_move_)) <=
EMPTY.value()) {
moves.push_back(std::make_tuple((8 * i + j), (8 * toi + toj), EMPTY));
}
}
void generateAllCastling(int i, int j, std::vector<std::tuple<int, int, Figure>> &moves, const Board &board);
void generateAllKingMoves(std::vector<std::tuple<int, int, Figure>>&, const Board& board);
}
inline bool hasNoFigure(const Board& board, const int rank, const int line) {
if (!inBounds(rank, line)) {
return false;
}
return board[rank][line] == 0;
}
inline bool hasBlackFigure(const Board& board, const int rank, const int line) {
if (!inBounds(rank, line)) {
return false;
}
return board[rank][line] < 0;
}
inline bool hasWhiteFigure(const Board& board, const int rank, const int line) {
if (!inBounds(rank, line)) {
return false;
}
return board[rank][line] > 0;
}
/*
* This method checks if the given move, does not make the king takeable, ensures the King is still protected.
*/
bool checkIfMoveIsIllegalDueCheck(const Board &b, std::tuple<int, int, Figure> move);
/*
* This currys checkIfMoveIsIllegalDueCheck(const Board &b, std::tuple<int, int, Figure> move);
*/
inline std::function<bool(std::tuple<int, int, Figure>)> checkIfMoveIsIllegalDueCheck(const Board& b){
return [b](std::tuple<int, int, Figure> move) -> bool {
return checkIfMoveIsIllegalDueCheck(b, move);
};
}
bool isCheckMate(const Board& b);
bool isStaleMate(const Board& b);
And here are additional files.
// Pawnlogic.cc
// Created by Niclas Schwalbe on 30.05.21.
//
#include "Odin.h"
inline void generatePawnPromotion(std::vector<std::tuple<int, int, Figure>> &seq,
const int field,
const int new_field) {
seq.push_back(std::make_tuple(field, new_field, BKNIGHT));
seq.push_back(std::make_tuple(field, new_field, BBISHOP));
seq.push_back(std::make_tuple(field, new_field, BROOK));
seq.push_back(std::make_tuple(field, new_field, BQUEEN));
}
void PAWNMOVES::generateAllPawnMovesWithWhite(std::vector<std::tuple<int, int, Figure>> &pawn_moves,
const Board &board,
int field_num) {
int rank = field_num / 8;
int line = field_num % 8;
if (hasNoFigure(board, rank + 1, line)) {
int new_field{field_num + 8};
if (rank == 6) {
generatePawnPromotion(pawn_moves, field_num, new_field);
} else {
pawn_moves.push_back(std::make_tuple(field_num, new_field, EMPTY));
}
}
if (rank == 1 && hasNoFigure(board, rank + 2, line) && hasNoFigure(board, rank + 1, line)) {
pawn_moves.push_back(std::make_tuple(field_num, field_num + 16, EMPTY));
}
int left = line - 1;
int right = line + 1;
if (0 <= left && left < 8 && hasBlackFigure(board, rank + 1, left)) {
int new_field{field_num + 7};
if (rank == 6) {
generatePawnPromotion(pawn_moves, field_num, new_field);
} else {
pawn_moves.push_back(std::make_tuple(field_num, new_field, EMPTY));
}
}
if (0 <= right && right < 8 && hasBlackFigure(board, rank + 1, right)) {
int new_field{field_num + 9};
if (rank == 6) {
generatePawnPromotion(pawn_moves, field_num, new_field);
} else {
pawn_moves.push_back(std::make_tuple(field_num, new_field, EMPTY));
}
}
}
void PAWNMOVES::generateAllPawnMovesWithBlack(std::vector<std::tuple<int, int, Figure>> &pawn_moves,
const Board &board,
int field_num) {
int rank = field_num / 8;
int line = field_num % 8;
if (hasNoFigure(board, rank - 1, line)) {
int new_field{field_num - 8};
if (rank == 1) {
generatePawnPromotion(pawn_moves, field_num, new_field);
} else {
pawn_moves.push_back(std::make_tuple(field_num, new_field, EMPTY));
}
}
if (rank == 6 && hasNoFigure(board, rank - 2, line) && hasNoFigure(board, rank - 1, line)) {
pawn_moves.push_back(std::make_tuple(field_num, field_num - 16, EMPTY));
}
int left = line - 1;
int right = line + 1;
if (0 <= left && left < 8 && hasWhiteFigure(board, rank - 1, left)) {
int new_field{field_num - 9};
if (rank == 1) {
generatePawnPromotion(pawn_moves, field_num, new_field);
} else {
pawn_moves.push_back(std::make_tuple(field_num, new_field, EMPTY));
}
}
if (0 <= right && right < 8 && hasWhiteFigure(board, rank - 1, right)) {
int new_field{field_num - 7};
if (rank == 1) {
generatePawnPromotion(pawn_moves, field_num, new_field);
} else {
pawn_moves.push_back(std::make_tuple(field_num, new_field, EMPTY));
}
}
}
void PAWNMOVES::generateAllPawnMoves(std::vector<std::tuple<int, int, Figure>> &moves, const Board &board) {
auto pawn = board.to_move_ == Color::WHITE ? WPAWN : BPAWN;
for(int fieldnum = 0; fieldnum < 64; fieldnum++){
if(board(fieldnum) == pawn.value()){
switch (board.to_move_) {
case Color::BLACK: generateAllPawnMovesWithBlack(moves, board, fieldnum);
break;
case Color::WHITE: generateAllPawnMovesWithWhite(moves, board, fieldnum);
break;
}
}
}
}
Movegeneration.cc
//
// Created by Niclas Schwalbe on 13.07.21.
//
#include "Odin.h"
Board makeMove(const Board &old_b, std::tuple<int, int, Figure> t) {
// copy Board
Board new_b{old_b};
const auto from_field{std::get<0>(t)};
const auto to_field{std::get<1>(t)};
const auto promotion{std::get<2>(t)};
// value of the piece to be moved.
int temp = new_b(from_field);
new_b(from_field) = EMPTY.value();
// is the move an en passant?
auto pawn = old_b.to_move_ == Color::WHITE ? WPAWN : BPAWN;
if (old_b(from_field) == pawn.value() &&
old_b.en_passant_field_ == to_field) {
if (old_b.to_move_ == Color::WHITE) {
new_b(to_field - 8) = EMPTY.value();
} else {
new_b(to_field + 8) = EMPTY.value();
}
}
// is it pawn move with 2 steps, if yes set en_passant
if (old_b(from_field) == pawn.value() && abs(from_field - to_field) == 16) {
new_b.en_passant_field_ =
new_b.to_move_ == Color::WHITE ? from_field + 8 : from_field - 8;
} else {
new_b.en_passant_field_ = -1;
}
// if move is castle, then set rook and remove castling rights
auto king = old_b.to_move_ == Color::WHITE ? WKING : BKING;
if (old_b(from_field) == king.value() && abs(from_field - to_field) == 2) {
if (to_field % 8 < 5) {
new_b((from_field / 8) * 8) = EMPTY.value();
new_b((from_field / 8) * 8 + 3) = king.color() * WROOK.value();
} else {
new_b((from_field / 8) * 8 + 7) = EMPTY.value();
new_b((from_field / 8) * 8 + 5) = king.color() * WROOK.value();
}
if (old_b.to_move_ == Color::WHITE) {
new_b.long_castle_white_ = false;
new_b.short_castle_white_ = false;
} else {
new_b.long_castle_black_ = false;
new_b.short_castle_black_ = false;
}
}
// Set new position, promote pawn if necessary
if (promotion.value() == 0) {
new_b(to_field) = temp;
} else {
new_b(to_field) = promotion.value();
}
// change color
new_b.to_move_ = old_b.to_move_ == Color::WHITE ? Color::BLACK : Color::WHITE;
return new_b;
}
void generateAllLegalMoves(std::vector<std::tuple<int, int, Figure>> &moves,
const Board &board) {
extractLegalMoves(moves, board, generateAllMoves);
}
void generateAllMoves(std::vector<std::tuple<int, int, Figure>> &moves,
const Board &board) {
bool colb = Color::WHITE == board.to_move_;
auto pawn = colb ? WPAWN : BPAWN;
auto knight = colb ? WKNIGHT : BKNIGHT;
auto bishop = colb ? WBISHOP : BBISHOP;
auto rook = colb ? WROOK : BROOK;
auto queen = colb ? WQUEEN : BQUEEN;
auto king = colb ? WKING : BKING;
for (int fieldnum = 0; fieldnum < 64; fieldnum++) {
auto val = board(fieldnum);
int y = fieldnum / 8;
int x = fieldnum % 8;
if (val == pawn.value()) {
switch (board.to_move_) {
case Color::WHITE: PAWNMOVES::generateAllPawnMovesWithWhite(moves, board, fieldnum);
break;
case Color::BLACK: PAWNMOVES::generateAllPawnMovesWithBlack(moves, board, fieldnum);
}
} else if (val == knight.value()) {
KNIGHTMOVES::addIfMoveable(moves, y, x, y - 2, x + 1, board);
KNIGHTMOVES::addIfMoveable(moves, y, x, y - 2, x - 1, board);
KNIGHTMOVES::addIfMoveable(moves, y, x, y + 2, x + 1, board);
KNIGHTMOVES::addIfMoveable(moves, y, x, y + 2, x - 1, board);
KNIGHTMOVES::addIfMoveable(moves, y, x, y - 1, x + 2, board);
KNIGHTMOVES::addIfMoveable(moves, y, x, y - 1, x - 2, board);
KNIGHTMOVES::addIfMoveable(moves, y, x, y + 1, x + 2, board);
KNIGHTMOVES::addIfMoveable(moves, y, x, y + 1, x - 2, board);
} else if (val == bishop.value()) {
LONGRANGEPIECEMOVES::generateMoves<1, 1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<1, -1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<-1, -1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<-1, 1>(moves, board, y, x);
} else if (val == rook.value()) {
LONGRANGEPIECEMOVES::generateMoves<1, 0>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<0, -1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<-1, 0>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<0, 1>(moves, board, y, x);
} else if (val == queen.value()) {
LONGRANGEPIECEMOVES::generateMoves<1, 1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<1, 0>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<1, -1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<-1, -1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<-1, 0>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<-1, 1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<0, -1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<0, 1>(moves, board, y, x);
} else if (val == king.value()) {
KINGMOVES::generateOneSteps<1, 0>(x, y, moves, board);
KINGMOVES::generateOneSteps<1, 1>(x, y, moves, board);
KINGMOVES::generateOneSteps<1, -1>(x, y, moves, board);
KINGMOVES::generateOneSteps<-1, 0>(x, y, moves, board);
KINGMOVES::generateOneSteps<-1, -1>(x, y, moves, board);
KINGMOVES::generateOneSteps<-1, 1>(x, y, moves, board);
KINGMOVES::generateOneSteps<0, -1>(x, y, moves, board);
KINGMOVES::generateOneSteps<0, 1>(x, y, moves, board);
KINGMOVES::generateAllCastling(x, y, moves, board);
}
}
}
void extractLegalMoves(
std::vector<std::tuple<int, int, Figure>> &moves, const Board &board,
std::function<void(std::vector<std::tuple<int, int, Figure>> &,
const Board &)>
generator) {
std::vector<std::tuple<int, int, Figure>> new_moves;
generator(new_moves, board);
for (auto &t : new_moves) {
if (!checkIfMoveIsIllegalDueCheck(board, t)) {
moves.push_back(t);
}
}
}
bool checkIfMoveIsIllegalDueCheck(const Board &b,
std::tuple<int, int, Figure> move) {
Board new_board = makeMove(b, move);
return isCheck(new_board, b.to_move_);
}
bool hasMoveToField(const Board &old_b, int to_field) {
auto color = old_b.to_move_ == Color::WHITE ? 1 : -1;
int x = to_field % 8;
int y = to_field / 8;
Figure pawn{WPAWN.value(), color};
Figure knight{WKNIGHT.value(), color};
Figure bishop{WBISHOP.value(), color};
Figure rook{WROOK.value(), color};
Figure queen{WQUEEN.value(), color};
Figure king{WKING.value(), color};
Board board{old_b};
board.to_move_ = (board.to_move_ == Color::WHITE) ? Color::BLACK : Color::WHITE;
std::vector<std::tuple<int, int, Figure>> knightmoves;
KNIGHTMOVES::addIfMoveable(knightmoves, y, x, y - 2, x + 1, board);
KNIGHTMOVES::addIfMoveable(knightmoves, y, x, y - 2, x - 1, board);
KNIGHTMOVES::addIfMoveable(knightmoves, y, x, y + 2, x + 1, board);
KNIGHTMOVES::addIfMoveable(knightmoves, y, x, y + 2, x - 1, board);
KNIGHTMOVES::addIfMoveable(knightmoves, y, x, y - 1, x + 2, board);
KNIGHTMOVES::addIfMoveable(knightmoves, y, x, y - 1, x - 2, board);
KNIGHTMOVES::addIfMoveable(knightmoves, y, x, y + 1, x + 2, board);
KNIGHTMOVES::addIfMoveable(knightmoves, y, x, y + 1, x - 2, board);
for(auto& [from, to, E] : knightmoves){
if(board(to) == knight.value()){
return true;
}
}
std::vector<std::tuple<int, int, Figure>> bishopmoves;
LONGRANGEPIECEMOVES::generateMoves<1, 1>(bishopmoves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<1, -1>(bishopmoves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<-1, -1>(bishopmoves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<-1, 1>(bishopmoves, board, y, x);
for(auto& [from, to, E] : bishopmoves){
if(board(to) == bishop.value() || board(to) == queen.value()){
return true;
}
}
std::vector<std::tuple<int, int, Figure>> rookmoves;
LONGRANGEPIECEMOVES::generateMoves<1, 0>(rookmoves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<0, -1>(rookmoves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<-1, 0>(rookmoves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<0, 1>(rookmoves, board, y, x);
for(auto& [from, to, E] : rookmoves){
if(board(to) == rook.value() || board(to) == queen.value()){
return true;
}
}
std::vector<std::tuple<int, int, Figure>> kingmoves;
KINGMOVES::generateOneSteps<1, 0>(x, y, kingmoves, board);
KINGMOVES::generateOneSteps<1, 1>(x, y, kingmoves, board);
KINGMOVES::generateOneSteps<1, -1>(x, y, kingmoves, board);
KINGMOVES::generateOneSteps<-1, 0>(x, y, kingmoves, board);
KINGMOVES::generateOneSteps<-1, -1>(x, y, kingmoves, board);
KINGMOVES::generateOneSteps<-1, 1>(x, y, kingmoves, board);
KINGMOVES::generateOneSteps<0, -1>(x, y, kingmoves, board);
KINGMOVES::generateOneSteps<0, 1>(x, y, kingmoves, board);
for(auto& [from, to, E] : kingmoves){
if(board(to) == king.value()){
return true;
}
}
switch (old_b.to_move_) {
case Color::WHITE: return (inBounds(x-1,y-1) && board[y-1][x-1] == pawn.value()) || (inBounds(x+1,y-1) && board[y-1][x+1] == pawn.value());
case Color::BLACK: return (inBounds(x-1,y+1) && board[y+1][x-1] == pawn.value()) || (inBounds(x+1,y+1) && board[y+1][x+1] == pawn.value());
}
}
bool isCheck(const Board &b, Color color_to_be_checked) {
std::vector<std::tuple<int, int, Figure>> moves;
if (b.to_move_ == color_to_be_checked) {
Board passTurn{b};
passTurn.to_move_ =
b.to_move_ == Color::WHITE ? Color::BLACK : Color::WHITE;
return isCheck(passTurn, color_to_be_checked);
} else {
int field_num{0};
auto figure = b.to_move_ == Color::WHITE ? BKING : WKING;
for (auto p : b) {
if (p == figure.value()) {
break;
}
field_num++;
}
return hasMoveToField(b, field_num);
}
}
bool isCheckMate(const Board &board) {
if (!isCheck(board, board.to_move_)) {
return false;
}
//smarter way
if (board.is_end_position.has_value()) {
return board.is_end_position.value();
}
//else Bruteforce
std::vector<std::tuple<int, int, Figure>> vec{};
generateAllLegalMoves(vec, board);
return vec.size() == 0;
}
bool isStaleMate(const Board &board) {
if (isCheck(board, board.to_move_)) {
return false;
}
//smarter way
if (board.is_end_position.has_value()) {
return board.is_end_position.value();
}
//else Brutforce
std::vector<std::tuple<int, int, Figure>> vec{};
generateAllLegalMoves(vec, board);
return vec.size() == 0;
}
Longrangepiecelogic.cc
#include "Odin.h"
/*
* Generates long range piece moves. Where dX, dY is the direction the piece is going to.
* A rook and a rook need 4 directions
* A queen needs 6 directions
*/
template <int dX, int dY>
void LONGRANGEPIECEMOVES::generateMoves(std::vector<std::tuple<int, int, Figure>>& moves,
const Board& board, const int y, const int x) {
int tox = x;
int toy = y;
for (int i = 0; i < 8 && inBounds(tox + dX, toy + dY); i++) {
tox += dX;
toy += dY;
if (board[toy][tox] == EMPTY.value()) {
moves.push_back(std::make_tuple((8 * y + x), (8 * toy + tox), EMPTY));
continue;
} else if (board[toy][tox] * (static_cast<int>(board.to_move_)) <=
EMPTY.value()) {
moves.push_back(std::make_tuple((8 * y + x), (8 * toy + tox), EMPTY));
break;
}
break;
}
}
void LONGRANGEPIECEMOVES::generateAllBishopMoves(std::vector<std::tuple<int, int, Figure>>& moves,
const Board& board) {
auto piece = board.to_move_ == Color::WHITE ? WBISHOP : BBISHOP;
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
if (board[y][x] == piece.value()) {
LONGRANGEPIECEMOVES::generateMoves<1, 1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<1, -1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<-1, -1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<-1, 1>(moves, board, y, x);
}
}
}
}
void LONGRANGEPIECEMOVES::generateAllRookMoves(std::vector<std::tuple<int, int, Figure>>& moves,
const Board& board) {
auto piece = board.to_move_ == Color::WHITE ? WROOK : BROOK;
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
if (board[y][x] == piece.value()) {
LONGRANGEPIECEMOVES::generateMoves<1, 0>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<0, -1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<-1, 0>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<0, 1>(moves, board, y, x);
}
}
}
}
void LONGRANGEPIECEMOVES::generateAllQueenMoves(std::vector<std::tuple<int, int, Figure>>& moves,
const Board& board) {
auto piece = board.to_move_ == Color::WHITE ? WQUEEN : BQUEEN;
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
if (board[y][x] == piece.value()) {
LONGRANGEPIECEMOVES::generateMoves<1, 1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<1, -1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<-1, -1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<-1, 1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<1, 0>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<0, -1>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<-1, 0>(moves, board, y, x);
LONGRANGEPIECEMOVES::generateMoves<0, 1>(moves, board, y, x);
}
}
}
}
Kinglogic.cc
#include "Odin.h"
/*
* Generates all castling moves. If the king is in check after the castling, the move will be generated and can be filtered out later.
* However, if the king would be in check while crossing, the move would not be added.
*/
void KINGMOVES::generateAllCastling(int j, int i,
std::vector<std::tuple<int, int, Figure>> &moves,
const Board &board) {
//check if King is at original position.
if (!((board.to_move_ == Color::BLACK && i == 7 && j == 4) || (board.to_move_ == Color::WHITE && i == 0 && j == 4))) {
return;
}
switch (board.to_move_) {
/*
* If the side still has castling sides, the space between king and rook is free, the rook still is at original position
* and the king could not be captured while moving to its desired position.
*/
//case WHITE
case Color::WHITE:
if (board.long_castle_white_ &&
!checkIfMoveIsIllegalDueCheck(board, std::make_tuple(4, 3, EMPTY)) &&
board[0][1] == EMPTY.value() &&
board[0][2] == EMPTY.value() &&
board[0][3] == EMPTY.value() &&
board[0][0] == WROOK.value()) {
moves.push_back(std::make_tuple(4, 2, EMPTY));
}
if (board.short_castle_white_ &&
!checkIfMoveIsIllegalDueCheck(board, std::make_tuple(4, 5, EMPTY)) &&
board[0][5] == EMPTY.value() &&
board[0][6] == EMPTY.value() &&
board[0][7] == WROOK.value()) {
moves.push_back(std::make_tuple(4, 6, EMPTY));
}
break;
//case BLACK
default:
if (board.long_castle_black_ &&
!checkIfMoveIsIllegalDueCheck(board, std::make_tuple(60, 59, EMPTY)) &&
board[7][1] == EMPTY.value() &&
board[7][2] == EMPTY.value() &&
board[7][3] == EMPTY.value() &&
board[7][0] == BROOK.value()) {
moves.push_back(std::make_tuple(60, 58, EMPTY));
}
if (board.short_castle_black_ &&
!checkIfMoveIsIllegalDueCheck(board, std::make_tuple(60, 61, EMPTY)) &&
board[7][5] == EMPTY.value() &&
board[7][6] == EMPTY.value() &&
board[7][7] == BROOK.value()) {
moves.push_back(std::make_tuple(60, 62, EMPTY));
}
}
}
void KINGMOVES::generateAllKingMoves(std::vector<std::tuple<int, int, Figure>> &moves,
const Board &board) {
auto piece = board.to_move_ == Color::WHITE ? WKING : BKING;
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (board[i][j] == piece.value()) {
KINGMOVES::generateOneSteps<1, 0>(j, i, moves, board);
KINGMOVES::generateOneSteps<1, 1>(j, i, moves, board);
KINGMOVES::generateOneSteps<1, -1>(j, i, moves, board);
KINGMOVES::generateOneSteps<-1, 0>(j, i, moves, board);
KINGMOVES::generateOneSteps<-1, -1>(j, i, moves, board);
KINGMOVES::generateOneSteps<-1, 1>(j, i, moves, board);
KINGMOVES::generateOneSteps<0, -1>(j, i, moves, board);
KINGMOVES::generateOneSteps<0, 1>(j, i, moves, board);
KINGMOVES::generateAllCastling(j, i, moves, board);
return;
}
}
}
}
Knightlogic.cc
#include <vector>
#include <tuple>
#include "Odin.h"
//adds Knight move if moveable
inline void KNIGHTMOVES::addIfMoveable(std::vector<std::tuple<int, int, Figure>>& moves, const int fromi,
const int fromj, const int toi, const int toj, const Board& b) {
int opposite = b.to_move_ == Color::WHITE ? -1 : 1;
if (!inBounds(toi, toj)) {
return;
}
if (opposite * b[toi][toj] >= 0) {
int old_field = fromi * 8 + fromj;
int new_field = toi * 8 + toj;
moves.push_back(std::make_tuple(old_field, new_field, EMPTY));
}
return;
}
void KNIGHTMOVES::generateAllKnightMoves(std::vector<std::tuple<int, int, Figure>>& moves,
const Board& board) {
auto piece = board.to_move_ == Color::WHITE ? WKNIGHT : BKNIGHT;
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (board[i][j] == piece.value()) {
KNIGHTMOVES::addIfMoveable(moves, i, j, i - 2, j + 1, board);
KNIGHTMOVES::addIfMoveable(moves, i, j, i - 2, j - 1, board);
KNIGHTMOVES::addIfMoveable(moves, i, j, i + 2, j + 1, board);
KNIGHTMOVES::addIfMoveable(moves, i, j, i + 2, j - 1, board);
KNIGHTMOVES::addIfMoveable(moves, i, j, i - 1, j + 2, board);
KNIGHTMOVES::addIfMoveable(moves, i, j, i - 1, j - 2, board);
KNIGHTMOVES::addIfMoveable(moves, i, j, i + 1, j + 2, board);
KNIGHTMOVES::addIfMoveable(moves, i, j, i + 1, j - 2, board);
}
}
}
}
I have some general questions:
As you see in the Odin.h file, namespaces are declared. However, for some weird reasons, I had to include the
KINGMOVES::generateOneStep
into the header file and could not define it in the kinglogic.cc, while this was possible with the similargeneratesMoves
from the longrangepiecelogic.cc. How to fix that?How could a better structure of this move generation look like?
There are some functions, where White and Black do different things (Pawnlogic.cc) This make it really clumsy. Any idea on how to fix that?
What else would you improve?
EMPTY
defined? \$\endgroup\$