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
Prev Previous commit
Next Next commit
Rewrite how the UI processes positions
  • Loading branch information
razvanfilea committed Nov 30, 2019
commit f922ceb93302194e662a55d320cbb5e30103addd
66 changes: 40 additions & 26 deletions ChessAndroid/app/src/main/cpp/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,14 @@
#include "chess/BoardManager.h"
#include "chess/Stats.h"
#include "chess/data/Board.h"
#include "chess/algorithm/Evaluation.h"
#include "chess/persistence/MovesPersistence.h"
#include "chess/algorithm/NegaMax.h"
#include "chess/algorithm/PieceAttacks.h"

JavaVM *jvm = nullptr;
jobject gameManagerInstance;

const BoardManager::PieceChangeListener listener = [](State state, bool shouldRedraw,
const std::vector<std::pair<byte, byte>> &moved)
const std::vector<std::pair<byte, byte>> &moved)
{
JNIEnv *env;
int getEnvStat = jvm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
Expand All @@ -30,24 +28,25 @@ const BoardManager::PieceChangeListener listener = [](State state, bool shouldRe

env->ExceptionClear();

jobjectArray result = env->NewObjectArray(static_cast<jsize>(moved.size()), Cache::posPairClass, nullptr);
jobjectArray result = env->NewObjectArray(static_cast<jsize>(moved.size()), Cache::posPairClass,
nullptr);

const static auto constructorId = env->GetMethodID(Cache::posPairClass, "<init>", "(BBBB)V");
const static auto constructorId = env->GetMethodID(Cache::posPairClass, "<init>", "(IIII)V");

for (unsigned i = 0; i < moved.size(); ++i)
{
const Pos &startPos = Pos(moved[i].first);
const Pos &destPos = Pos(moved[i].second);
jobject obj = env->NewObject(Cache::posPairClass, constructorId,
startPos.x, startPos.y, destPos.x, destPos.y);
startPos.x, startPos.y, destPos.x, destPos.y);

env->SetObjectArrayElement(result, i, obj);
}

const static auto callbackId = env->GetMethodID(Cache::gameManagerClass, "callback",
"(IZ[Lnet/theluckycoder/chess/PosPair;)V");
env->CallVoidMethod(gameManagerInstance, callbackId,
static_cast<jint>(state), static_cast<jboolean>(shouldRedraw), result);
static_cast<jint>(state), static_cast<jboolean>(shouldRedraw), result);

if (getEnvStat == JNI_EDETACHED)
{
Expand All @@ -61,7 +60,7 @@ external JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void */*reserved*/)
LOGI("ChessCpp", "JNI_OnLoad");

JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK)
return -1;

jvm = vm;
Expand All @@ -87,7 +86,8 @@ external JNIEXPORT void JNI_OnUnload(JavaVM *vm, void */*reserved*/)

external JNIEXPORT void JNICALL
Java_net_theluckycoder_chess_GameManager_initBoardNative(JNIEnv *pEnv, jobject instance,
jboolean restartGame, jboolean isPlayerWhite)
jboolean restartGame,
jboolean isPlayerWhite)
{
static bool boardManagerInitialized = false;
pEnv->ExceptionClear();
Expand All @@ -96,7 +96,8 @@ Java_net_theluckycoder_chess_GameManager_initBoardNative(JNIEnv *pEnv, jobject i
{
pEnv->DeleteGlobalRef(gameManagerInstance);
gameManagerInstance = pEnv->NewGlobalRef(instance);
Cache::gameManagerClass = Cache::cacheClass(pEnv, pEnv->GetObjectClass(gameManagerInstance));
Cache::gameManagerClass = Cache::cacheClass(pEnv,
pEnv->GetObjectClass(gameManagerInstance));

boardManagerInitialized = true;
BoardManager::initBoardManager(listener);
Expand Down Expand Up @@ -138,10 +139,11 @@ Java_net_theluckycoder_chess_Native_getPieces(JNIEnv *pEnv, jclass /*type*/)
{
pEnv->ExceptionClear();

const static auto constructorId = pEnv->GetMethodID(Cache::pieceClass, "<init>", "(BBB)V");
const static auto constructorId = pEnv->GetMethodID(Cache::pieceClass, "<init>", "(IIB)V");

const auto pieces = BoardManager::getBoard().getAllPieces();
auto *array = pEnv->NewObjectArray(static_cast<jsize>(pieces.size()), Cache::pieceClass, nullptr);
auto *array = pEnv->NewObjectArray(static_cast<jsize>(pieces.size()), Cache::pieceClass,
nullptr);

jsize i = 0;
for (const auto &it : pieces)
Expand All @@ -165,15 +167,20 @@ Java_net_theluckycoder_chess_Native_getPossibleMoves(JNIEnv *pEnv, jclass /*type
{
pEnv->ExceptionClear();

const static auto constructorId = pEnv->GetMethodID(Cache::posClass, "<init>", "(BB)V");
const static auto constructorId = pEnv->GetMethodID(Cache::posClass, "<init>", "(II)V");

const Pos pos(getByte(pEnv, Cache::posClass, dest, "x"), getByte(pEnv, Cache::posClass, dest, "y"));
const Pos pos(getInt(pEnv, Cache::posClass, dest, "x"),
getInt(pEnv, Cache::posClass, dest, "y"));
const auto possibleMoves = BoardManager::getPossibleMoves(pos);
auto *result = pEnv->NewObjectArray(static_cast<jsize>(possibleMoves.size()), Cache::posClass, nullptr);
auto *result = pEnv->NewObjectArray(static_cast<jsize>(possibleMoves.size()), Cache::posClass,
nullptr);

for (unsigned i = 0; i < possibleMoves.size(); ++i)
{
jobject obj = pEnv->NewObject(Cache::posClass, constructorId, possibleMoves[i].x, possibleMoves[i].y);
jobject obj = pEnv->NewObject(Cache::posClass,
constructorId,
static_cast<int>(possibleMoves[i].x),
static_cast<int>(possibleMoves[i].y));
pEnv->SetObjectArrayElement(result, i, obj);
}

Expand All @@ -188,8 +195,10 @@ Java_net_theluckycoder_chess_Native_enableStats(JNIEnv */*pEnv*/, jclass /*type*
}

external JNIEXPORT void JNICALL
Java_net_theluckycoder_chess_Native_setSettings(JNIEnv */*pEnv*/, jclass /*type*/, jint baseSearchDepth,
jint threadCount, jint cacheSizeMb, jboolean performQuiescenceSearch)
Java_net_theluckycoder_chess_Native_setSettings(JNIEnv */*pEnv*/, jclass /*type*/,
jint baseSearchDepth,
jint threadCount, jint cacheSizeMb,
jboolean performQuiescenceSearch)
{
BoardManager::setSettings(Settings(static_cast<short>(baseSearchDepth),
static_cast<unsigned int>(threadCount),
Expand All @@ -200,10 +209,12 @@ Java_net_theluckycoder_chess_Native_setSettings(JNIEnv */*pEnv*/, jclass /*type*

external JNIEXPORT void JNICALL
Java_net_theluckycoder_chess_Native_movePiece(JNIEnv */*pEnv*/, jclass /*type*/,
jbyte selectedX, jbyte selectedY, jbyte destX, jbyte destY)
jbyte selectedX, jbyte selectedY, jbyte destX,
jbyte destY)
{
BoardManager::movePiece(Pos(static_cast<byte>(selectedX), static_cast<byte>(selectedY)).toSquare(),
Pos(static_cast<byte>(destX), static_cast<byte>(destY)).toSquare());
BoardManager::movePiece(
Pos(static_cast<byte>(selectedX), static_cast<byte>(selectedY)).toSquare(),
Pos(static_cast<byte>(destX), static_cast<byte>(destY)).toSquare());
}

external JNIEXPORT jint JNICALL
Expand Down Expand Up @@ -233,7 +244,8 @@ Java_net_theluckycoder_chess_Native_loadMoves(JNIEnv *pEnv, jclass /*type*/, jst
external JNIEXPORT jstring JNICALL
Java_net_theluckycoder_chess_Native_saveMoves(JNIEnv *pEnv, jclass /*type*/)
{
const std::string string = MovesPersistence::saveToString(BoardManager::getMovesHistory(), BoardManager::isPlayerWhite());
const std::string string = MovesPersistence::saveToString(BoardManager::getMovesHistory(),
BoardManager::isPlayerWhite());
return pEnv->NewStringUTF(string.c_str());
}

Expand All @@ -256,7 +268,9 @@ static U64 perft(const Board &board, int depth)
external JNIEXPORT void JNICALL
Java_net_theluckycoder_chess_Native_perft(JNIEnv */*pEnv*/, jclass /*type*/, jint depth)
{
constexpr std::array<U64, 7> perftResults {
using namespace std::chrono;

constexpr std::array<U64, 7> perftResults{
1, 20, 400, 8902, 197281, 4865609, 119060324
};
constexpr int maxSize = static_cast<int>(perftResults.size());
Expand All @@ -271,11 +285,11 @@ Java_net_theluckycoder_chess_Native_perft(JNIEnv */*pEnv*/, jclass /*type*/, jin
{
LOGV("Perft Test", "Starting Depth %d Test", i);

const auto startTime = std::chrono::high_resolution_clock::now();
const auto startTime = high_resolution_clock::now();
const U64 nodesCount = perft(board, i);
const auto endTime = high_resolution_clock::now();

const auto currentTime = std::chrono::high_resolution_clock::now();
const double timeNeeded = std::chrono::duration<double, std::milli>(currentTime - startTime).count();
const auto timeNeeded = duration<double, std::milli>(endTime - startTime).count();

LOGV("Perft Test", "Time needed: %lf", timeNeeded);
LOGV("Perft Test", "Nodes count: %llu/%llu", nodesCount, perftResults[i]);
Expand Down
6 changes: 2 additions & 4 deletions ChessAndroid/app/src/main/cpp/external.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

#define external extern "C"

using byte = unsigned char;

byte getByte(JNIEnv *env, jclass type, jobject obj, const char *name)
int getInt(JNIEnv *env, jclass type, jobject obj, const char *name)
{
return static_cast<byte>(env->GetByteField(obj, env->GetFieldID(type, name, "B")));
return static_cast<int>(env->GetIntField(obj, env->GetFieldID(type, name, "I")));
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class ChessActivity : AppCompatActivity(), CustomView.ClickListener, GameManager
preferences.firstStart = false
// Set Default Settings
preferences.settings =
Settings(4, Runtime.getRuntime().availableProcessors() - 1, 200, true)
Settings(4, Runtime.getRuntime().availableProcessors() - 1, 100, true)
}

gameManager.initBoard(false)
Expand Down Expand Up @@ -121,15 +121,14 @@ class ChessActivity : AppCompatActivity(), CustomView.ClickListener, GameManager

pieces.forEach {
if (it.value.res == drawable)
it.value.isInChess = true
it.value.isInCheck = true
}
}

private fun movePiece(view: TileView) {
if (!selectedPos.isValid || !canMove) return

val startPos = selectedPos
gameManager.makeMove(startPos, view.pos)
gameManager.makeMove(selectedPos, view.pos)
clearTiles(true)
selectedPos = Pos()

Expand All @@ -154,8 +153,12 @@ class ChessActivity : AppCompatActivity(), CustomView.ClickListener, GameManager

if (gameManager.statsEnabled) {
tv_debug.visibility = View.VISIBLE
tv_debug.text =
getString(R.string.stats, Native.getStats(), Native.getBoardValue(), Native.getBestMoveFound())
tv_debug.text = getString(
R.string.stats,
Native.getStats(),
Native.getBoardValue(),
Native.getBestMoveFound()
)
} else {
tv_debug.visibility = View.GONE
}
Expand Down Expand Up @@ -185,8 +188,8 @@ class ChessActivity : AppCompatActivity(), CustomView.ClickListener, GameManager
setKingInChess(false)
} else {
pieces.forEach {
if (it.value.isInChess)
it.value.isInChess = false
if (it.value.isInCheck)
it.value.isInCheck = false
}
}
}
Expand Down Expand Up @@ -221,23 +224,21 @@ class ChessActivity : AppCompatActivity(), CustomView.ClickListener, GameManager
}
tiles.clear()

for (i in 0..7) {
for (j in 0..7) {
val pos = Pos(invertIf(!isPlayerWhite, i), invertIf(isPlayerWhite, j))
val isWhite = (i + j) % 2 == 0
for (i in 0 until 64) {
val pos = Pos(i % 8, i / 8)
val isWhite = (pos.x + pos.y) % 2 == 0

val xSize = invertIf(!isPlayerWhite, pos.x.toInt()) * viewSize
val ySize = invertIf(isPlayerWhite, pos.y.toInt()) * viewSize
val xSize = pos.x * viewSize
val ySize = invertIf(isPlayerWhite, pos.y) * viewSize

val tileView = TileView(this, isWhite, pos, this).apply {
layoutParams = FrameLayout.LayoutParams(viewSize, viewSize)
x = xSize.toFloat()
y = ySize.toFloat()
}

tiles[pos] = tileView
layout_board.addView(tileView)
val tileView = TileView(this, isWhite, pos, this).apply {
layoutParams = FrameLayout.LayoutParams(viewSize, viewSize)
x = xSize.toFloat()
y = ySize.toFloat()
}

tiles[pos] = tileView
layout_board.addView(tileView)
}
}

Expand All @@ -251,11 +252,12 @@ class ChessActivity : AppCompatActivity(), CustomView.ClickListener, GameManager
val isWhite = it.type in 1..6
val resource = PieceResourceManager.piecesResources[it.type.toInt() - 1]
val clickable = isWhite == isPlayerWhite
val xSize = invertIf(!isPlayerWhite, it.x.toInt()) * viewSize
val ySize = invertIf(isPlayerWhite, it.y.toInt()) * viewSize

val pos = Pos(it.x, it.y)

val xSize = pos.x * viewSize
val ySize = invertIf(isPlayerWhite, pos.y) * viewSize

val pieceView = PieceView(this, clickable, resource, pos, this).apply {
layoutParams = FrameLayout.LayoutParams(viewSize, viewSize)
x = xSize.toFloat()
Expand All @@ -280,8 +282,8 @@ class ChessActivity : AppCompatActivity(), CustomView.ClickListener, GameManager
it.pos = destPos

// Calculate the new View Position
val xPos = invertIf(!isPlayerWhite, destPos.x.toInt()) * viewSize
val yPos = invertIf(isPlayerWhite, destPos.y.toInt()) * viewSize
val xPos = destPos.x * viewSize
val yPos = invertIf(isPlayerWhite, destPos.y) * viewSize

it.animate()
.x(xPos.toFloat())
Expand All @@ -295,9 +297,13 @@ class ChessActivity : AppCompatActivity(), CustomView.ClickListener, GameManager
layout_board.removeView(destView)

val type = PieceResourceManager.piecesResources.indexOf(destView.res) + 1
val piece = Piece(destView.pos.x, destView.pos.y, type.toByte())
val piece =
Piece(destView.pos.x, invertIf(isPlayerWhite, destView.pos.y), type.toByte())

if (isPlayerWhite) capturedPieces.addBlackPiece(piece) else capturedPieces.addWhitePiece(piece)
if (isPlayerWhite)
capturedPieces.addBlackPiece(piece)
else
capturedPieces.addWhitePiece(piece)
}

pieceView?.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.app.Application

@Suppress("unused")
class ChessApp : Application() {

companion object {
init {
System.loadLibrary("chess")
Expand Down
20 changes: 11 additions & 9 deletions ChessAndroid/app/src/main/java/net/theluckycoder/chess/Data.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ enum class State {

data class Piece(
@JvmField
val x: Byte,
val x: Int,
@JvmField
val y: Byte,
val y: Int,
@JvmField
val type: Byte
) {
Expand All @@ -30,27 +30,29 @@ data class Piece(

data class Pos(
@JvmField
val x: Byte,
val x: Int,
@JvmField
val y: Byte
val y: Int
) {

constructor() : this(8, 8)
constructor(x: Int, y: Int) : this(x.toByte(), y.toByte())
constructor(square: Byte) : this(square % 8, square / 8)

fun invertIf(invert: Boolean) = Pos(x, if (invert) 7 - y else y)

val isValid
get() = (x in 0..7 && y in 0..7)
}

data class PosPair(
@JvmField
val startX: Byte,
val startX: Int,
@JvmField
val startY: Byte,
val startY: Int,
@JvmField
val destX: Byte,
val destX: Int,
@JvmField
val destY: Byte
val destY: Int
)

class Settings(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@ class GameManager(

fun makeMove(startPos: Pos, destPos: Pos) {
Native.enableStats(statsEnabled)
Native.movePiece(startPos.x, startPos.y, destPos.x, destPos.y)
Native.movePiece(
startPos.x.toByte(),
startPos.y.toByte(),
destPos.x.toByte(),
destPos.y.toByte()
)
}

private fun getPiecesList() = Native.getPieces().filter { it.type.toInt() != 0 }
Expand Down
Loading