Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6c0054b
Rewrite how the board is stored and moves are made
razvanfilea Nov 20, 2019
1c41cd3
Remove 'ToList' MoveGen template arg, make some fixes
razvanfilea Nov 22, 2019
2bfdff3
Fix a critical error with Board::doMove()
razvanfilea Nov 24, 2019
9db8df9
Fix getPhase always returning ENDGAME
razvanfilea Nov 24, 2019
8fd4e8c
Improve the Evaluation function performance
razvanfilea Nov 24, 2019
bee2ee1
Fix Board::hasValidState and update Pawn PSQT
razvanfilea Nov 26, 2019
68d9853
Switch to the previous Multi Threading model
razvanfilea Nov 28, 2019
7154003
Remove KING_DANGER MoveGen
razvanfilea Nov 28, 2019
eb1963c
Add age attribute to the TranspositionTable
razvanfilea Nov 29, 2019
2da9711
Fix crash when generating legal moves
razvanfilea Nov 30, 2019
f922ceb
Rewrite how the UI processes positions
razvanfilea Nov 30, 2019
220b4d7
Rename NegaMax class to Search
razvanfilea Nov 30, 2019
ee721fd
Work on the ThreadPool
razvanfilea Nov 30, 2019
9a9e5ac
Solve a bug with Castling and disable EnPassant for now
razvanfilea Nov 30, 2019
faba2c2
Add Generated Nodes to Stats
razvanfilea Dec 1, 2019
d73bb5c
Remake the Settings Screen
razvanfilea Dec 1, 2019
9b46c3e
Clean ThreadSafeQueue and ThreadPool
razvanfilea Dec 1, 2019
d5d5ae0
Revert to the old Pawn PSQT
razvanfilea Dec 8, 2019
1b60b38
Re-enable EnPassant
razvanfilea Dec 9, 2019
c0be29b
Add Platform Specific Implementations for BSF and BSR
razvanfilea Dec 10, 2019
8a64bbc
Remove StackVector
razvanfilea Dec 10, 2019
7889cfa
Fix compile errors
razvanfilea Dec 10, 2019
e35ab03
Remake the Pawn Evaluation add Connected Bonus
razvanfilea Dec 11, 2019
459400d
Merge Rays.h into Bitboard.h
razvanfilea Dec 11, 2019
9be2e38
Fix Pawn Move Generation from last commit
razvanfilea Dec 12, 2019
9a37891
Remove Light Theme, add Rook on Queen File Evaluation
razvanfilea Dec 15, 2019
b81a38c
Rewrite Piece class to only use 1 byte
razvanfilea Dec 15, 2019
3a122a9
New icon
razvanfilea Dec 15, 2019
99a93b5
Release 1.0
razvanfilea Dec 15, 2019
b8651a6
Fix issues with the Pull Request
razvanfilea Dec 15, 2019
8c8a8d9
Delete MoveOrdering.h
razvanfilea Dec 15, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Rewrite how the board is stored and moves are made
  • Loading branch information
razvanfilea committed Nov 20, 2019
commit 6c0054b8d09aaaf71243c31402436b717e15cb0f
9 changes: 0 additions & 9 deletions ChessAndroid/app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -g -O0 -std=c++17 -Wall -Wextra -pedantic")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -O3 -std=c++17 -Wall -Wextra -pedantic -DNDEBUG")

Expand Down Expand Up @@ -37,10 +32,6 @@ find_library( # Sets the name of the path variable.
# you want CMake to locate.
log)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
chess

Expand Down
4 changes: 2 additions & 2 deletions ChessAndroid/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ android {
applicationId "net.theluckycoder.chess"
minSdkVersion 21
targetSdkVersion 29
versionCode 26
versionName "0.26"
versionCode 100
versionName "1.0-alpha"
}

buildTypes {
Expand Down
2 changes: 1 addition & 1 deletion ChessAndroid/app/src/main/cpp/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ Java_net_theluckycoder_chess_Native_perft(JNIEnv */*pEnv*/, jclass /*type*/, jin
const double timeNeeded = std::chrono::duration<double, std::milli>(currentTime - startTime).count();

LOGV("Perft Test", "Time needed: %lf", timeNeeded);
LOGV("Perft Test", "Nodes count: %llu", nodesCount);
LOGV("Perft Test", "Nodes count: %llu/%llu", nodesCount, perftResults[i]);
if (nodesCount != perftResults[i])
LOGE("Perft Test Error", "Nodes count do not match at depth %d", i);
}
Expand Down
269 changes: 20 additions & 249 deletions ChessAndroid/app/src/main/cpp/chess/BoardManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ void BoardManager::loadGame(const std::vector<PosPair> &moves, const bool isPlay

for (const PosPair &move : moves)
{
movePieceInternal(move.first, move.second, s_Board);
s_Board.doMove(move.first.toSquare(), move.second.toSquare());
s_Board.score = Evaluation::evaluate(s_Board);
s_MovesHistory.emplace_back(move.first, move.second, s_Board);
}
Expand All @@ -57,16 +57,16 @@ Piece::MaxMovesVector BoardManager::getPossibleMoves(const Pos &selectedPos)
Piece::MaxMovesVector moves;

const Piece &piece = s_Board[selectedPos];
const auto possibleMoves = piece.getPossibleMoves(selectedPos, s_Board);
const auto possibleMoves = piece.getPossibleMoves(selectedPos.toSquare(), s_Board);

for (const Pos &destPos : possibleMoves)
{
const Piece &destPiece = s_Board[destPos];
if (destPiece.type == Type::KING)
if (destPiece.type == PieceType::KING)
continue;

Board board = s_Board;
BoardManager::movePieceInternal(selectedPos, destPos, board);
board.doMove(selectedPos.toSquare(), destPos.toSquare());

if (board.state == State::INVALID)
continue;
Expand All @@ -77,12 +77,14 @@ Piece::MaxMovesVector BoardManager::getPossibleMoves(const Pos &selectedPos)

int count = 1;

for (const auto &game : BoardManager::getMovesHistory()) {
if (board.whiteToMove == game.board.whiteToMove &&
for (const auto &game : BoardManager::getMovesHistory())
{
if (board.colorToMove == game.board.colorToMove &&
board.state == game.board.state &&
board.key == game.board.key)
board.zKey == game.board.zKey)
count++;

// TODO: Fix this
if (count == 3)
{
board.score = 0;
Expand All @@ -100,86 +102,16 @@ Piece::MaxMovesVector BoardManager::getPossibleMoves(const Pos &selectedPos)
void BoardManager::movePiece(const Pos &selectedPos, const Pos &destPos, const bool movedByPlayer)
{
assert(selectedPos.isValid() && destPos.isValid());
s_Board.whiteToMove = !s_Board.whiteToMove;
s_Board.isPromotion = s_Board.isCapture = false;
bool shouldRedraw = false;

const Pos enPassantPos = s_Board.enPassantPos;
s_Board.enPassantPos = Pos();
Piece &selectedPiece = s_Board[selectedPos];

StackVector<PosPair, 2> piecesMoved{ {selectedPos, destPos} };

const U64 selectedPosBitboard = selectedPos.toBitboard();
const U64 destPosBitboard = destPos.toBitboard();
const bool selectedPieceColor = selectedPiece.isWhite;

switch (selectedPiece.type)
{
case PAWN:
shouldRedraw = movePawn(s_Board, selectedPos, destPos, enPassantPos);
s_Board.pawns[selectedPieceColor] &= ~selectedPosBitboard;
s_Board.pawns[selectedPieceColor] |= destPosBitboard;
break;
case KNIGHT:
s_Board.knights[selectedPieceColor] &= ~selectedPosBitboard;
s_Board.knights[selectedPieceColor] |= destPosBitboard;
break;
case BISHOP:
s_Board.bishops[selectedPieceColor] &= ~selectedPosBitboard;
s_Board.bishops[selectedPieceColor] |= destPosBitboard;
break;
case ROOK:
s_Board.rooks[selectedPieceColor] &= ~selectedPosBitboard;
s_Board.rooks[selectedPieceColor] |= destPosBitboard;
break;
case QUEEN:
s_Board.queens[selectedPieceColor] &= ~selectedPosBitboard;
s_Board.queens[selectedPieceColor] |= destPosBitboard;
break;
case KING:
{
s_Board.kingSquare[selectedPiece.isWhite] = destPos.toSquare();

if (!selectedPiece.moved)
{
const PosPair &posPair = piecesMoved.emplace_back(moveKing(selectedPiece, selectedPos, destPos, s_Board));
if (posPair.first.isValid())
{
if (selectedPiece.isWhite)
s_Board.whiteCastled = true;
else
s_Board.blackCastled = true;

U64 &pieces = s_Board.allPieces[selectedPiece.isWhite];
pieces &= ~posPair.first.toBitboard();
pieces |= posPair.second.toBitboard();
}
}
break;
}
case NONE:
break;
}

s_Board.allPieces[selectedPiece.isWhite] &= ~selectedPosBitboard; // Remove selected Piece
s_Board.allPieces[selectedPiece.isWhite] |= destPosBitboard; // Add the selected Piece to destination

selectedPiece.moved = true;

if (const Piece &destPiece = s_Board[destPos]; destPiece)
{
s_Board.allPieces[destPiece.isWhite] &= ~destPos.toBitboard(); // Remove destination Piece
s_Board.npm -= Evaluation::getPieceValue(destPiece.type);
s_Board.isCapture = true;
}

s_Board[destPos] = selectedPiece;
s_Board[selectedPos] = Piece();
const byte castledBefore = (s_Board.castlingRights & CASTLED_WHITE) | (s_Board.castlingRights & CASTLED_BLACK);
s_Board.doMove(selectedPos.toSquare(), destPos.toSquare());
const byte castledAfter = (s_Board.castlingRights & CASTLED_WHITE) | (s_Board.castlingRights & CASTLED_BLACK);

s_Board.key = Hash::compute(s_Board);
s_Board.updateState();
s_Board.score = Evaluation::evaluate(s_Board);
s_Board.zKey = Hash::compute(s_Board);

const StackVector<PosPair, 2> piecesMoved{ {selectedPos, destPos} };
const bool shouldRedraw = s_Board.isPromotion || (castledBefore != castledAfter);

s_MovesHistory.emplace_back(selectedPos, destPos, s_Board);
s_Listener(s_Board.state, shouldRedraw, piecesMoved);
Expand All @@ -188,102 +120,15 @@ void BoardManager::movePiece(const Pos &selectedPos, const Pos &destPos, const b
s_WorkerThread = std::thread(moveComputerPlayer, s_Settings);
}

void BoardManager::movePieceInternal(const Pos &selectedPos, const Pos &destPos, Board &board, const bool updateState)
{
board.whiteToMove = !board.whiteToMove;
Piece &selectedPiece = board[selectedPos];
Piece &destPiece = board[destPos];
bool hashHandled = false;
board.isPromotion = board.isCapture = false;

const Pos enPassantPos = board.enPassantPos;
board.enPassantPos = Pos();

const U64 selectedPosBitboard = selectedPos.toBitboard();
const U64 destPosBitboard = destPos.toBitboard();
const bool selectedPieceColor = selectedPiece.isWhite;

switch (selectedPiece.type)
{
case PAWN:
hashHandled = movePawn(board, selectedPos, destPos, enPassantPos);
board.pawns[selectedPieceColor] &= ~selectedPosBitboard;
board.pawns[selectedPieceColor] |= destPosBitboard;
break;
case KNIGHT:
board.knights[selectedPieceColor] &= ~selectedPosBitboard;
board.knights[selectedPieceColor] |= destPosBitboard;
break;
case BISHOP:
board.bishops[selectedPieceColor] &= ~selectedPosBitboard;
board.bishops[selectedPieceColor] |= destPosBitboard;
break;
case ROOK:
board.rooks[selectedPieceColor] &= ~selectedPosBitboard;
board.rooks[selectedPieceColor] |= destPosBitboard;
break;
case QUEEN:
board.queens[selectedPieceColor] &= ~selectedPosBitboard;
board.queens[selectedPieceColor] |= destPosBitboard;
break;
case KING:
{
board.kingSquare[selectedPieceColor] = destPos.toSquare();

if (!selectedPiece.moved)
{
const PosPair posPair = moveKing(selectedPiece, selectedPos, destPos, board);
if (posPair.first.isValid()) // Castling
{
if (selectedPiece.isWhite)
board.whiteCastled = true;
else
board.blackCastled = true;

Hash::makeMove(board.key, posPair.first, posPair.second, Piece(Type::ROOK, selectedPieceColor));

U64 &pieces = board.allPieces[selectedPieceColor];
pieces &= ~posPair.first.toBitboard();
pieces |= posPair.second.toBitboard();
}
}
break;
}
case NONE:
break;
}

board.allPieces[selectedPieceColor] &= ~selectedPosBitboard; // Remove selected Piece
board.allPieces[selectedPieceColor] |= destPosBitboard; // Add the selected Piece to destination

Hash::flipSide(board.key);
if (!hashHandled)
Hash::makeMove(board.key, selectedPos, destPos, selectedPiece, destPiece);

if (destPiece)
{
board.allPieces[destPiece.isWhite] &= ~destPos.toBitboard(); // Remove destination Piece
board.npm -= Evaluation::getPieceValue(destPiece.type);
board.isCapture = true;
}

selectedPiece.moved = true;
destPiece = selectedPiece;
board[selectedPos] = Piece();

if (updateState)
board.updateState();
}

// This function should only be called through the Worker Thread
void BoardManager::moveComputerPlayer(const Settings &settings)
{
s_IsWorking = true;
Stats::resetStats();
Stats::startTimer();

assert(s_Board.whiteToMove != s_IsPlayerWhite);
const RootMove bestMove = NegaMax::getBestMove(s_Board, settings);
assert(s_Board.colorToMove != toColor(s_IsPlayerWhite));
const RootMove bestMove = NegaMax::findBestMove(s_Board, settings);

Stats::stopTimer();
s_IsWorking = false;
Expand All @@ -292,80 +137,6 @@ void BoardManager::moveComputerPlayer(const Settings &settings)
s_WorkerThread.detach();
}

bool BoardManager::movePawn(Board &board, const Pos &startPos, const Pos &destPos, const Pos &enPassantPos)
{
Piece &pawn = board[startPos];

if (pawn.moved) {
if (destPos.y == 0 || destPos.y == 7)
{
pawn.type = Type::QUEEN;
board.isPromotion = true;

Hash::promotePawn(board.key, startPos, destPos, pawn.isWhite, Type::QUEEN);
return true;
} else if (destPos == enPassantPos) {
board.isCapture = true;

const Pos capturedPos(enPassantPos.x, enPassantPos.y + static_cast<byte>(pawn.isWhite ? -1 : 1));
Piece &capturedPiece = board[capturedPos];

// Remove the captured Pawn
Hash::xorPiece(board.key, capturedPos, capturedPiece);
board.allPieces[!pawn.isWhite] = ~capturedPos.toBitboard();
board.npm -= Evaluation::getPieceValue(Type::PAWN);
capturedPiece = Piece();
return true;
}
} else {
const int distance = static_cast<int>(destPos.y) - static_cast<int>(startPos.y);
if (distance == 2 || distance == -2)
board.enPassantPos = Pos(destPos.x, destPos.y - static_cast<byte>(distance / 2));
}

return false;
}

PosPair BoardManager::moveKing(Piece &king, const Pos &selectedPos, const Pos &destPos, Board &board)
{
if (destPos.x == 6u)
{
constexpr byte startX = 7u;
const byte y = selectedPos.y;

Piece &rook = board.getPiece(startX, y);
if (rook.type == Type::ROOK && king.isSameColor(rook) && !rook.moved)
{
rook.moved = true;

constexpr byte destX = 5;
board.getPiece(destX, y) = rook;
board.getPiece(startX, y) = Piece::EMPTY;

return std::make_pair(Pos(startX, y), Pos(destX, y));
}
}
else if (destPos.x == 2u)
{
constexpr byte startX = 0u;
const byte y = selectedPos.y;

Piece &rook = board.getPiece(startX, y);
if (rook.type == Type::ROOK && king.isSameColor(rook) && !rook.moved)
{
rook.moved = true;

constexpr byte destX = 3u;
board.getPiece(destX, y) = rook;
board.getPiece(startX, y) = Piece::EMPTY;

return std::make_pair(Pos(startX, y), Pos(destX, y));
}
}

return std::make_pair(Pos(), Pos());
}

void BoardManager::undoLastMoves()
{
if (isWorking() || s_MovesHistory.size() < 3) return;
Expand All @@ -386,8 +157,8 @@ void BoardManager::undoLastMoves()
// Redraw if a Promotion or castling happened in the last three moves
const bool shouldRedraw = engineBoard.isPromotion || engineBoard.isCapture ||
playerBoard.isPromotion || playerBoard.isCapture ||
engineBoard.whiteCastled != previousBoard.whiteCastled ||
engineBoard.blackCastled != previousBoard.whiteCastled;
engineBoard.isCastled(WHITE) != previousBoard.isCastled(WHITE) ||
engineBoard.isCastled(BLACK) != previousBoard.isCastled(BLACK);

s_Board = previousBoard;
s_Listener(previousBoard.state, shouldRedraw,
Expand Down
3 changes: 0 additions & 3 deletions ChessAndroid/app/src/main/cpp/chess/BoardManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,8 @@ class BoardManager final
static bool isPlayerWhite() { return s_IsPlayerWhite; }
static Piece::MaxMovesVector getPossibleMoves(const Pos &selectedPos);
static void movePiece(const Pos &selectedPos, const Pos &destPos, bool movedByPlayer = true);
static void movePieceInternal(const Pos &selectedPos, const Pos &destPos, Board &board, bool updateState = true);
static void setSettings(const Settings &settings) { s_Settings = settings; }

private:
static void moveComputerPlayer(const Settings &settings);
static bool movePawn(Board &board, const Pos &startPos, const Pos &destPos, const Pos &enPassantPos);
static PosPair moveKing(Piece &king, const Pos &selectedPos, const Pos &destPos, Board &board);
};
Loading