diff --git a/DroidFish/jni/Android.mk b/DroidFish/jni/Android.mk
index c484a0e..d3d5dec 100644
--- a/DroidFish/jni/Android.mk
+++ b/DroidFish/jni/Android.mk
@@ -10,3 +10,5 @@ include $(BUILD_SHARED_LIBRARY)
include jni/stockfish/Android.mk
include jni/gtb/Android.mk
+
+include jni/rtb/Android.mk
diff --git a/DroidFish/jni/Application.mk b/DroidFish/jni/Application.mk
index f4f67cf..1a8fc48 100644
--- a/DroidFish/jni/Application.mk
+++ b/DroidFish/jni/Application.mk
@@ -1,3 +1,4 @@
APP_ABI := armeabi armeabi-v7a x86 mips
APP_STL := gnustl_static
APP_OPTIM := release
+NDK_TOOLCHAIN_VERSION := 4.8
diff --git a/DroidFish/jni/rtb/Android.mk b/DroidFish/jni/rtb/Android.mk
new file mode 100644
index 0000000..7fce3d7
--- /dev/null
+++ b/DroidFish/jni/rtb/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+
+LOCAL_MODULE := rtb
+LOCAL_SRC_FILES := \
+ bitBoard.cpp material.cpp moveGen.cpp position.cpp rtb-probe.cpp tbprobe.cpp \
+ RtbProbe.cpp
+
+LOCAL_CFLAGS := --std=c++11 \
+ -I $(LOCAL_PATH)/sysport/ -I -DNDEBUG -Wall \
+ -mandroid -DTARGET_OS=android -D__ANDROID__ \
+ -D__STDC_INT64__ -D_GLIBCXX_USE_C99_STDINT_TR1 \
+ -D_GLIBCXX_HAS_GTHREADS -D_GLIBCXX__PTHREADS
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/DroidFish/jni/rtb/RtbProbe.cpp b/DroidFish/jni/rtb/RtbProbe.cpp
new file mode 100644
index 0000000..6768211
--- /dev/null
+++ b/DroidFish/jni/rtb/RtbProbe.cpp
@@ -0,0 +1,82 @@
+/*
+ DroidFish - An Android chess program.
+ Copyright (C) 2011-2012 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+#include "RtbProbe.h"
+#include "tbprobe.hpp"
+#include
+
+
+static bool initOk = false;
+
+JNIEXPORT jboolean
+JNICALL Java_org_petero_droidfish_gtb_RtbProbe_init(
+ JNIEnv* env, jclass cls, jstring jTbPath)
+{
+ initOk = false;
+ const char* tbPath = (*env).GetStringUTFChars(jTbPath, NULL);
+ if (!tbPath)
+ return false;
+ std::string rtbPath(tbPath);
+ (*env).ReleaseStringUTFChars(jTbPath, tbPath);
+
+ TBProbe::initialize(rtbPath);
+ initOk = true;
+ return true;
+}
+
+JNIEXPORT void
+JNICALL Java_org_petero_droidfish_gtb_RtbProbe_probe(
+ JNIEnv* env, jobject ths, jbyteArray jSquares, jboolean wtm,
+ jint epSq, jint castleMask,
+ jint halfMoveClock, jint fullMoveCounter,
+ jintArray result)
+{
+ if ((*env).GetArrayLength(result) < 2)
+ return;
+
+ jint res[2];
+ res[0] = 1000;
+ res[1] = 1000;
+ (*env).SetIntArrayRegion(result, 0, 2, res);
+
+ if (!initOk)
+ return;
+
+ const int len = (*env).GetArrayLength(jSquares);
+ if (len != 64)
+ return;
+
+ Position pos;
+ jbyte* jbPtr = (*env).GetByteArrayElements(jSquares, NULL);
+ for (int i = 0; i < 64; i++)
+ pos.setPiece(i, jbPtr[i]);
+ (*env).ReleaseByteArrayElements(jSquares, jbPtr, 0);
+
+ pos.setWhiteMove(wtm);
+ pos.setEpSquare(epSq);
+ pos.setCastleMask(castleMask);
+ pos.setHalfMoveClock(halfMoveClock);
+ pos.setFullMoveCounter(fullMoveCounter);
+
+ int score;
+ if (TBProbe::rtbProbeWDL(pos, score))
+ res[0] = score;
+ if (TBProbe::rtbProbeDTZ(pos, score))
+ res[1] = score;
+
+ (*env).SetIntArrayRegion(result, 0, 2, res);
+}
diff --git a/DroidFish/jni/rtb/RtbProbe.h b/DroidFish/jni/rtb/RtbProbe.h
new file mode 100644
index 0000000..01b3bd3
--- /dev/null
+++ b/DroidFish/jni/rtb/RtbProbe.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include
+/* Header for class org_petero_droidfish_gtb_RtbProbe */
+
+#ifndef _Included_org_petero_droidfish_gtb_RtbProbe
+#define _Included_org_petero_droidfish_gtb_RtbProbe
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_petero_droidfish_gtb_RtbProbe
+ * Method: probe
+ * Signature: ([BZIIII[I)V
+ */
+JNIEXPORT void JNICALL Java_org_petero_droidfish_gtb_RtbProbe_probe
+ (JNIEnv *, jobject, jbyteArray, jboolean, jint, jint, jint, jint, jintArray);
+
+/*
+ * Class: org_petero_droidfish_gtb_RtbProbe
+ * Method: init
+ * Signature: (Ljava/lang/String;)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_petero_droidfish_gtb_RtbProbe_init
+ (JNIEnv *, jclass, jstring);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/DroidFish/jni/rtb/bitBoard.cpp b/DroidFish/jni/rtb/bitBoard.cpp
new file mode 100644
index 0000000..61cddf9
--- /dev/null
+++ b/DroidFish/jni/rtb/bitBoard.cpp
@@ -0,0 +1,364 @@
+/*
+ Texel - A UCI chess engine.
+ Copyright (C) 2012-2014 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*
+ * bitBoard.cpp
+ *
+ * Created on: Feb 25, 2012
+ * Author: petero
+ */
+
+#include "bitBoard.hpp"
+#include "position.hpp"
+#include
+#include
+
+U64 BitBoard::kingAttacks[64];
+U64 BitBoard::knightAttacks[64];
+U64 BitBoard::wPawnAttacks[64];
+U64 BitBoard::bPawnAttacks[64];
+
+const U64 BitBoard::maskFile[8] = {
+ 0x0101010101010101ULL,
+ 0x0202020202020202ULL,
+ 0x0404040404040404ULL,
+ 0x0808080808080808ULL,
+ 0x1010101010101010ULL,
+ 0x2020202020202020ULL,
+ 0x4040404040404040ULL,
+ 0x8080808080808080ULL
+};
+
+U64 BitBoard::epMaskW[8];
+U64 BitBoard::epMaskB[8];
+
+const U64 BitBoard::maskRow1;
+const U64 BitBoard::maskRow2;
+const U64 BitBoard::maskRow3;
+const U64 BitBoard::maskRow4;
+const U64 BitBoard::maskRow5;
+const U64 BitBoard::maskRow6;
+const U64 BitBoard::maskRow7;
+const U64 BitBoard::maskRow8;
+const U64 BitBoard::maskRow1Row8;
+const U64 BitBoard::maskDarkSq;
+const U64 BitBoard::maskLightSq;
+const U64 BitBoard::maskCorners;
+
+U64* BitBoard::rTables[64];
+U64 BitBoard::rMasks[64];
+int BitBoard::rBits[64] = { 12, 11, 11, 11, 11, 11, 11, 12,
+ 11, 10, 10, 11, 10, 10, 10, 11,
+ 11, 10, 10, 10, 10, 10, 10, 11,
+ 11, 10, 10, 10, 10, 10, 10, 11,
+ 11, 10, 10, 10, 10, 10, 10, 11,
+ 11, 10, 10, 11, 10, 10, 10, 11,
+ 10, 9, 9, 9, 9, 9, 10, 10,
+ 11, 10, 10, 10, 10, 11, 10, 11 };
+const U64 BitBoard::rMagics[64] = {
+ 0x19a80065ff2bffffULL, 0x3fd80075ffebffffULL, 0x4010000df6f6fffeULL, 0x0050001faffaffffULL,
+ 0x0050028004ffffb0ULL, 0x7f600280089ffff1ULL, 0x7f5000b0029ffffcULL, 0x5b58004848a7fffaULL,
+ 0x002a90005547ffffULL, 0x000050007f13ffffULL, 0x007fa0006013ffffULL, 0x006a9005656fffffULL,
+ 0x007f600f600affffULL, 0x007ec007e6bfffe2ULL, 0x007ec003eebffffbULL, 0x0071d002382fffdaULL,
+ 0x009f803000e7fffaULL, 0x00680030008bffffULL, 0x00606060004f3ffcULL, 0x001a00600bff9ffdULL,
+ 0x000d006005ff9fffULL, 0x0001806003005fffULL, 0x00000300040bfffaULL, 0x000192500065ffeaULL,
+ 0x00fff112d0006800ULL, 0x007ff037d000c004ULL, 0x003fd062001a3ff8ULL, 0x00087000600e1ffcULL,
+ 0x000fff0100100804ULL, 0x0007ff0100080402ULL, 0x0003ffe0c0060003ULL, 0x0001ffd53000d300ULL,
+ 0x00fffd3000600061ULL, 0x007fff7f95900040ULL, 0x003fff8c00600060ULL, 0x001ffe2587a01860ULL,
+ 0x000fff3fbf40180cULL, 0x0007ffc73f400c06ULL, 0x0003ff86d2c01405ULL, 0x0001fffeaa700100ULL,
+ 0x00fffdfdd8005000ULL, 0x007fff80ebffb000ULL, 0x003fffdf603f6000ULL, 0x001fffe050405000ULL,
+ 0x000fff400700c00cULL, 0x0007ff6007bf600aULL, 0x0003ffeebffec005ULL, 0x0001fffdf3feb001ULL,
+ 0x00ffff39ff484a00ULL, 0x007fff3fff486300ULL, 0x003fff99ffac2e00ULL, 0x001fff31ff2a6a00ULL,
+ 0x000fff19ff15b600ULL, 0x0007fff5fff28600ULL, 0x0003fffddffbfee0ULL, 0x0001fff5f63c96a0ULL,
+ 0x00ffff5dff65cfb6ULL, 0x007fffbaffd1c5aeULL, 0x003fff71ff6cbceaULL, 0x001fffd9ffd4756eULL,
+ 0x000ffff5fff338e6ULL, 0x0007fffdfffe24f6ULL, 0x0003ffef27eebe74ULL, 0x0001ffff23ff605eULL
+};
+
+U64* BitBoard::bTables[64];
+U64 BitBoard::bMasks[64];
+const int BitBoard::bBits[64] = { 5, 4, 5, 5, 5, 5, 4, 5,
+ 4, 4, 5, 5, 5, 5, 4, 4,
+ 4, 4, 7, 7, 7, 7, 4, 4,
+ 5, 5, 7, 9, 9, 7, 5, 5,
+ 5, 5, 7, 9, 9, 7, 5, 5,
+ 4, 4, 7, 7, 7, 7, 4, 4,
+ 4, 4, 5, 5, 5, 5, 4, 4,
+ 5, 4, 5, 5, 5, 5, 4, 5 };
+const U64 BitBoard::bMagics[64] = {
+ 0x0006eff5367ff600ULL, 0x00345835ba77ff2bULL, 0x00145f68a3f5dab6ULL, 0x003a1863fb56f21dULL,
+ 0x0012eb6bfe9d93cdULL, 0x000d82827f3420d6ULL, 0x00074bcd9c7fec97ULL, 0x000034fe99f9ffffULL,
+ 0x0000746f8d6717f6ULL, 0x00003acb32e1a3f7ULL, 0x0000185daf1ffb8aULL, 0x00003a1867f17067ULL,
+ 0x0000038ee0ccf92eULL, 0x000002a2b7ff926eULL, 0x000006c9aa93ff14ULL, 0x00000399b5e5bf87ULL,
+ 0x00400f342c951ffcULL, 0x0020230579ed8ff0ULL, 0x007b008a0077dbfdULL, 0x001d00010c13fd46ULL,
+ 0x00040022031c1ffbULL, 0x000fa00fd1cbff79ULL, 0x000400a4bc9affdfULL, 0x000200085e9cffdaULL,
+ 0x002a14560a3dbfbdULL, 0x000a0a157b9eafd1ULL, 0x00060600fd002ffaULL, 0x004006000c009010ULL,
+ 0x001a002042008040ULL, 0x001a00600fd1ffc0ULL, 0x000d0ace50bf3f8dULL, 0x000183a48434efd1ULL,
+ 0x001fbd7670982a0dULL, 0x000fe24301d81a0fULL, 0x0007fbf82f040041ULL, 0x000040c800008200ULL,
+ 0x007fe17018086006ULL, 0x003b7ddf0ffe1effULL, 0x001f92f861df4a0aULL, 0x000fd713ad98a289ULL,
+ 0x000fd6aa751e400cULL, 0x0007f2a63ae9600cULL, 0x0003ff7dfe0e3f00ULL, 0x000003fd2704ce04ULL,
+ 0x00007fc421601d40ULL, 0x007fff5f70900120ULL, 0x003fa66283556403ULL, 0x001fe31969aec201ULL,
+ 0x0007fdfc18ac14bbULL, 0x0003fb96fb568a47ULL, 0x000003f72ea4954dULL, 0x00000003f8dc0383ULL,
+ 0x0000007f3a814490ULL, 0x00007dc5c9cf62a6ULL, 0x007f23d3342897acULL, 0x003fee36eee1565cULL,
+ 0x0003ff3e99fcccc7ULL, 0x000003ecfcfac5feULL, 0x00000003f97f7453ULL, 0x0000000003f8dc03ULL,
+ 0x000000007efa8146ULL, 0x0000007ed3e2ef60ULL, 0x00007f47243adcd6ULL, 0x007fb65afabfb3b5ULL
+};
+
+std::vector BitBoard::tableData;
+
+const S8 BitBoard::dirTable[] = {
+ -9, 0, 0, 0, 0, 0, 0, -8, 0, 0, 0, 0, 0, 0, -7,
+ 0, 0, -9, 0, 0, 0, 0, 0, -8, 0, 0, 0, 0, 0, -7, 0,
+ 0, 0, 0, -9, 0, 0, 0, 0, -8, 0, 0, 0, 0, -7, 0, 0,
+ 0, 0, 0, 0, -9, 0, 0, 0, -8, 0, 0, 0, -7, 0, 0, 0,
+ 0, 0, 0, 0, 0, -9, 0, 0, -8, 0, 0, -7, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -9,-17, -8,-15, -7, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,-10, -9, -8, -7, -6, 0, 0, 0, 0, 0,
+ 0, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 7, 15, 8, 17, 9, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7, 0, 0, 8, 0, 0, 9, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0,
+ 0, 0, 0, 7, 0, 0, 0, 0, 8, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 7, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 9, 0,
+ 0, 7, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9
+};
+
+const S8 BitBoard::kingDistTable[] = {
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 0, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
+ 0, 7, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7,
+ 0, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 6, 7,
+ 0, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7,
+ 0, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7,
+ 0, 7, 6, 5, 4, 3, 2, 1, 1, 1, 2, 3, 4, 5, 6, 7,
+ 0, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7,
+ 0, 7, 6, 5, 4, 3, 2, 1, 1, 1, 2, 3, 4, 5, 6, 7,
+ 0, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7,
+ 0, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7,
+ 0, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 6, 7,
+ 0, 7, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7,
+ 0, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+};
+
+const S8 BitBoard::taxiDistTable[] = {
+ 14,13,12,11,10, 9, 8, 7, 8, 9,10,11,12,13,14,
+ 0,13,12,11,10, 9, 8, 7, 6, 7, 8, 9,10,11,12,13,
+ 0,12,11,10, 9, 8, 7, 6, 5, 6, 7, 8, 9,10,11,12,
+ 0,11,10, 9, 8, 7, 6, 5, 4, 5, 6, 7, 8, 9,10,11,
+ 0,10, 9, 8, 7, 6, 5, 4, 3, 4, 5, 6, 7, 8, 9,10,
+ 0, 9, 8, 7, 6, 5, 4, 3, 2, 3, 4, 5, 6, 7, 8, 9,
+ 0, 8, 7, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6, 7, 8,
+ 0, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7,
+ 0, 8, 7, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6, 7, 8,
+ 0, 9, 8, 7, 6, 5, 4, 3, 2, 3, 4, 5, 6, 7, 8, 9,
+ 0,10, 9, 8, 7, 6, 5, 4, 3, 4, 5, 6, 7, 8, 9,10,
+ 0,11,10, 9, 8, 7, 6, 5, 4, 5, 6, 7, 8, 9,10,11,
+ 0,12,11,10, 9, 8, 7, 6, 5, 6, 7, 8, 9,10,11,12,
+ 0,13,12,11,10, 9, 8, 7, 6, 7, 8, 9,10,11,12,13,
+ 0,14,13,12,11,10, 9, 8, 7, 8, 9,10,11,12,13,14
+};
+
+const int BitBoard::trailingZ[64] = {
+ 63, 0, 58, 1, 59, 47, 53, 2,
+ 60, 39, 48, 27, 54, 33, 42, 3,
+ 61, 51, 37, 40, 49, 18, 28, 20,
+ 55, 30, 34, 11, 43, 14, 22, 4,
+ 62, 57, 46, 52, 38, 26, 32, 41,
+ 50, 36, 17, 19, 29, 10, 13, 21,
+ 56, 45, 25, 31, 35, 16, 9, 12,
+ 44, 24, 15, 8, 23, 7, 6, 5
+};
+
+U64 BitBoard::squaresBetween[64][64];
+
+static U64 createPattern(int i, U64 mask) {
+ U64 ret = 0ULL;
+ for (int j = 0; ; j++) {
+ U64 nextMask = mask & (mask - 1);
+ U64 bit = mask ^ nextMask;
+ if ((i & (1ULL << j)) != 0)
+ ret |= bit;
+ mask = nextMask;
+ if (mask == 0)
+ break;
+ }
+ return ret;
+}
+
+static U64 addRay(U64 mask, int x, int y, int dx, int dy,
+ U64 occupied, bool inner) {
+ int lo = inner ? 1 : 0;
+ int hi = inner ? 6 : 7;
+ while (true) {
+ if (dx != 0) {
+ x += dx; if ((x < lo) || (x > hi)) break;
+ }
+ if (dy != 0) {
+ y += dy; if ((y < lo) || (y > hi)) break;
+ }
+ int sq = Position::getSquare(x, y);
+ mask |= 1ULL << sq;
+ if ((occupied & (1ULL << sq)) != 0)
+ break;
+ }
+ return mask;
+}
+
+static U64 addRookRays(int x, int y, U64 occupied, bool inner) {
+ U64 mask = 0;
+ mask = addRay(mask, x, y, 1, 0, occupied, inner);
+ mask = addRay(mask, x, y, -1, 0, occupied, inner);
+ mask = addRay(mask, x, y, 0, 1, occupied, inner);
+ mask = addRay(mask, x, y, 0, -1, occupied, inner);
+ return mask;
+}
+
+static U64 addBishopRays(int x, int y, U64 occupied, bool inner) {
+ U64 mask = 0;
+ mask = addRay(mask, x, y, 1, 1, occupied, inner);
+ mask = addRay(mask, x, y, -1, -1, occupied, inner);
+ mask = addRay(mask, x, y, 1, -1, occupied, inner);
+ mask = addRay(mask, x, y, -1, 1, occupied, inner);
+ return mask;
+}
+
+static StaticInitializer bbInit;
+
+void
+BitBoard::staticInitialize() {
+ for (int f = 0; f < 8; f++) {
+ U64 m = 0;
+ if (f > 0) m |= 1ULL << Position::getSquare(f-1, 3);
+ if (f < 7) m |= 1ULL << Position::getSquare(f+1, 3);
+ epMaskW[f] = m;
+
+ m = 0;
+ if (f > 0) m |= 1ULL << Position::getSquare(f-1, 4);
+ if (f < 7) m |= 1ULL << Position::getSquare(f+1, 4);
+ epMaskB[f] = m;
+ }
+
+ // Compute king attacks
+ for (int sq = 0; sq < 64; sq++) {
+ U64 m = 1ULL << sq;
+ U64 mask = (((m >> 1) | (m << 7) | (m >> 9)) & maskAToGFiles) |
+ (((m << 1) | (m << 9) | (m >> 7)) & maskBToHFiles) |
+ (m << 8) | (m >> 8);
+ kingAttacks[sq] = mask;
+ }
+
+ // Compute knight attacks
+ for (int sq = 0; sq < 64; sq++) {
+ U64 m = 1ULL << sq;
+ U64 mask = (((m << 6) | (m >> 10)) & maskAToFFiles) |
+ (((m << 15) | (m >> 17)) & maskAToGFiles) |
+ (((m << 17) | (m >> 15)) & maskBToHFiles) |
+ (((m << 10) | (m >> 6)) & maskCToHFiles);
+ knightAttacks[sq] = mask;
+ }
+
+ // Compute pawn attacks
+ for (int sq = 0; sq < 64; sq++) {
+ U64 m = 1ULL << sq;
+ U64 mask = ((m << 7) & maskAToGFiles) | ((m << 9) & maskBToHFiles);
+ wPawnAttacks[sq] = mask;
+ mask = ((m >> 9) & maskAToGFiles) | ((m >> 7) & maskBToHFiles);
+ bPawnAttacks[sq] = mask;
+ }
+
+ // Rook magics
+ int rTableSize = 0;
+ for (int sq = 0; sq < 64; sq++)
+ rTableSize += 1 << rBits[sq];
+ int bTableSize = 0;
+ for (int sq = 0; sq < 64; sq++)
+ bTableSize += 1 << bBits[sq];
+ tableData.resize(rTableSize + bTableSize);
+
+ int tableUsed = 0;
+ for (int sq = 0; sq < 64; sq++) {
+ int x = Position::getX(sq);
+ int y = Position::getY(sq);
+ rMasks[sq] = addRookRays(x, y, 0ULL, true);
+ int tableSize = 1 << rBits[sq];
+ U64* table = &tableData[tableUsed];
+ tableUsed += tableSize;
+ const U64 unInit = 0xffffffffffffffffULL;
+ for (int i = 0; i < tableSize; i++) table[i] = unInit;
+ int nPatterns = 1 << BitBoard::bitCount(rMasks[sq]);
+ for (int i = 0; i < nPatterns; i++) {
+ U64 p = createPattern(i, rMasks[sq]);
+ int entry = (int)((p * rMagics[sq]) >> (64 - rBits[sq]));
+ U64 atks = addRookRays(x, y, p, false);
+ if (table[entry] == unInit) {
+ table[entry] = atks;
+ } else {
+ assert(table[entry] == atks);
+ }
+ }
+ rTables[sq] = table;
+ }
+
+ // Bishop magics
+ for (int sq = 0; sq < 64; sq++) {
+ int x = Position::getX(sq);
+ int y = Position::getY(sq);
+ bMasks[sq] = addBishopRays(x, y, 0ULL, true);
+ int tableSize = 1 << bBits[sq];
+ U64* table = &tableData[tableUsed];
+ tableUsed += tableSize;
+ const U64 unInit = 0xffffffffffffffffULL;
+ for (int i = 0; i < tableSize; i++) table[i] = unInit;
+ int nPatterns = 1 << BitBoard::bitCount(bMasks[sq]);
+ for (int i = 0; i < nPatterns; i++) {
+ U64 p = createPattern(i, bMasks[sq]);
+ int entry = (int)((p * bMagics[sq]) >> (64 - bBits[sq]));
+ U64 atks = addBishopRays(x, y, p, false);
+ if (table[entry] == unInit) {
+ table[entry] = atks;
+ } else {
+ assert(table[entry] == atks);
+ }
+ }
+ bTables[sq] = table;
+ }
+
+ // squaresBetween
+ for (int sq1 = 0; sq1 < 64; sq1++) {
+ for (int j = 0; j < 64; j++)
+ squaresBetween[sq1][j] = 0;
+ for (int dx = -1; dx <= 1; dx++) {
+ for (int dy = -1; dy <= 1; dy++) {
+ if ((dx == 0) && (dy == 0))
+ continue;
+ U64 m = 0;
+ int x = Position::getX(sq1);
+ int y = Position::getY(sq1);
+ while (true) {
+ x += dx; y += dy;
+ if ((x < 0) || (x > 7) || (y < 0) || (y > 7))
+ break;
+ int sq2 = Position::getSquare(x, y);
+ squaresBetween[sq1][sq2] = m;
+ m |= 1ULL << sq2;
+ }
+ }
+ }
+ }
+}
diff --git a/DroidFish/jni/rtb/bitBoard.hpp b/DroidFish/jni/rtb/bitBoard.hpp
new file mode 100644
index 0000000..b803280
--- /dev/null
+++ b/DroidFish/jni/rtb/bitBoard.hpp
@@ -0,0 +1,132 @@
+/*
+ Texel - A UCI chess engine.
+ Copyright (C) 2012-2014 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*
+ * bitBoard.hpp
+ *
+ * Created on: Feb 25, 2012
+ * Author: petero
+ */
+
+#ifndef BITBOARD_HPP_
+#define BITBOARD_HPP_
+
+#include "util.hpp"
+#include
+
+enum Square {
+ A1, B1, C1, D1, E1, F1, G1, H1,
+ A2, B2, C2, D2, E2, F2, G2, H2,
+ A3, B3, C3, D3, E3, F3, G3, H3,
+ A4, B4, C4, D4, E4, F4, G4, H4,
+ A5, B5, C5, D5, E5, F5, G5, H5,
+ A6, B6, C6, D6, E6, F6, G6, H6,
+ A7, B7, C7, D7, E7, F7, G7, H7,
+ A8, B8, C8, D8, E8, F8, G8, H8
+};
+
+class BitBoard {
+public:
+ /** Squares attacked by a king on a given square. */
+ static U64 kingAttacks[64];
+ static U64 knightAttacks[64];
+ static U64 wPawnAttacks[64], bPawnAttacks[64];
+
+ static const U64 maskAToGFiles = 0x7F7F7F7F7F7F7F7FULL;
+ static const U64 maskBToHFiles = 0xFEFEFEFEFEFEFEFEULL;
+ static const U64 maskAToFFiles = 0x3F3F3F3F3F3F3F3FULL;
+ static const U64 maskCToHFiles = 0xFCFCFCFCFCFCFCFCULL;
+
+ static const U64 maskFile[8];
+
+ // Masks for squares where enemy pawn can capture en passant, indexed by file
+ static U64 epMaskW[8], epMaskB[8];
+
+ static const U64 maskRow1 = 0x00000000000000FFULL;
+ static const U64 maskRow2 = 0x000000000000FF00ULL;
+ static const U64 maskRow3 = 0x0000000000FF0000ULL;
+ static const U64 maskRow4 = 0x00000000FF000000ULL;
+ static const U64 maskRow5 = 0x000000FF00000000ULL;
+ static const U64 maskRow6 = 0x0000FF0000000000ULL;
+ static const U64 maskRow7 = 0x00FF000000000000ULL;
+ static const U64 maskRow8 = 0xFF00000000000000ULL;
+ static const U64 maskRow1Row8 = 0xFF000000000000FFULL;
+
+ static const U64 maskDarkSq = 0xAA55AA55AA55AA55ULL;
+ static const U64 maskLightSq = 0x55AA55AA55AA55AAULL;
+
+ static const U64 maskCorners = 0x8100000000000081ULL;
+
+
+ static U64 bishopAttacks(int sq, U64 occupied) {
+ return bTables[sq][(int)(((occupied & bMasks[sq]) * bMagics[sq]) >> (64 - bBits[sq]))];
+ }
+
+ static U64 rookAttacks(int sq, U64 occupied) {
+ return rTables[sq][(int)(((occupied & rMasks[sq]) * rMagics[sq]) >> (64 - rBits[sq]))];
+ }
+
+ static U64 squaresBetween[64][64];
+
+ static int getDirection(int from, int to) {
+ int offs = to + (to|7) - from - (from|7) + 0x77;
+ return dirTable[offs];
+ }
+
+ static int numberOfTrailingZeros(U64 mask) {
+ return trailingZ[(int)(((mask & -mask) * 0x07EDD5E59A4E28C2ULL) >> 58)];
+ }
+
+ /** Return number of 1 bits in mask. */
+ static int bitCount(U64 mask) {
+ const U64 k1 = 0x5555555555555555ULL;
+ const U64 k2 = 0x3333333333333333ULL;
+ const U64 k4 = 0x0f0f0f0f0f0f0f0fULL;
+ const U64 kf = 0x0101010101010101ULL;
+ U64 t = mask;
+ t -= (t >> 1) & k1;
+ t = (t & k2) + ((t >> 2) & k2);
+ t = (t + (t >> 4)) & k4;
+ t = (t * kf) >> 56;
+ return (int)t;
+ }
+
+ /** Initialize static data. */
+ static void staticInitialize();
+
+private:
+ static U64* rTables[64];
+ static U64 rMasks[64];
+ static int rBits[64];
+ static const U64 rMagics[64];
+
+ static U64* bTables[64];
+ static U64 bMasks[64];
+ static const int bBits[64];
+ static const U64 bMagics[64];
+
+ static std::vector tableData;
+
+ static const S8 dirTable[];
+ static const S8 kingDistTable[];
+ static const S8 taxiDistTable[];
+ static const int trailingZ[64];
+};
+
+
+#endif /* BITBOARD_HPP_ */
diff --git a/DroidFish/jni/rtb/material.cpp b/DroidFish/jni/rtb/material.cpp
new file mode 100644
index 0000000..9fe5bb4
--- /dev/null
+++ b/DroidFish/jni/rtb/material.cpp
@@ -0,0 +1,32 @@
+/*
+ Texel - A UCI chess engine.
+ Copyright (C) 2013 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*
+ * material.cpp
+ *
+ * Created on: May 1, 2013
+ * Author: petero
+ */
+
+#include "material.hpp"
+
+const int MatId::materialId[] = {
+ 0,
+ 0, WQ, WR, WB, WN, WP,
+ 0, BQ, BR, BB, BN, BP
+};
diff --git a/DroidFish/jni/rtb/material.hpp b/DroidFish/jni/rtb/material.hpp
new file mode 100644
index 0000000..205a39e
--- /dev/null
+++ b/DroidFish/jni/rtb/material.hpp
@@ -0,0 +1,97 @@
+/*
+ Texel - A UCI chess engine.
+ Copyright (C) 2013-2014 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*
+ * material.hpp
+ *
+ * Created on: May 1, 2013
+ * Author: petero
+ */
+
+#ifndef MATERIAL_HPP_
+#define MATERIAL_HPP_
+
+#include "piece.hpp"
+
+/**
+ * An incrementally updated material identifier.
+ * For each legal piece configuration, a unique identifier is computed.
+ */
+class MatId {
+public:
+ static const int WP = 1;
+ static const int WR = 9;
+ static const int WN = 91;
+ static const int WB = 767;
+ static const int WQ = 5903;
+
+ static const int BP = 1 << 16;
+ static const int BR = 9 << 16;
+ static const int BN = 91 << 16;
+ static const int BB = 767 << 16;
+ static const int BQ = 5903 << 16;
+
+ MatId() : hash(0) {}
+
+ /** Add a piece to the material configuration. */
+ void addPiece(int pType);
+
+ /** Remove a piece from the material configuration. */
+ void removePiece(int pType);
+
+ /** Add cnt pieces of tyep ptype to the material configuration. */
+ void addPieceCnt(int pType, int cnt);
+
+ /** Get the material configuration identifier. */
+ int operator()() const;
+
+ /** Get ID for black/white mirror position. */
+ static int mirror(int id);
+
+private:
+ int hash;
+ static const int materialId[Piece::nPieceTypes];
+};
+
+inline void
+MatId::addPiece(int pType) {
+ hash += materialId[pType];
+}
+
+inline void
+MatId::removePiece(int pType) {
+ hash -= materialId[pType];
+}
+
+inline void
+MatId::addPieceCnt(int pType, int cnt) {
+ hash += materialId[pType] * cnt;
+}
+
+inline int
+MatId::operator()() const {
+ return hash;
+}
+
+inline int
+MatId::mirror(int h) {
+ unsigned int ret = h;
+ return (ret >> 16) | ((ret & 0xffff) << 16);
+}
+
+#endif /* MATERIAL_HPP_ */
diff --git a/DroidFish/jni/rtb/move.hpp b/DroidFish/jni/rtb/move.hpp
new file mode 100644
index 0000000..02e2542
--- /dev/null
+++ b/DroidFish/jni/rtb/move.hpp
@@ -0,0 +1,90 @@
+/*
+ Texel - A UCI chess engine.
+ Copyright (C) 2012-2014 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*
+ * move.hpp
+ *
+ * Created on: Feb 25, 2012
+ * Author: petero
+ */
+
+#ifndef MOVE_HPP_
+#define MOVE_HPP_
+
+#include "util.hpp"
+
+/** Represents a chess move. */
+class Move {
+public:
+ /** Create empty move object. */
+ Move() : from_(0), to_(0), promoteTo_(0) { }
+
+ /** Create a move object. */
+ Move(int from, int to, int promoteTo);
+
+ /** Copy constructor. */
+ Move(const Move& m);
+
+ int from() const;
+ int to() const;
+ int promoteTo() const;
+
+ /** Not declared "nothrow". Avoids nullptr check in generated assembly code when using placement new. */
+ void* operator new (std::size_t size, void* ptr) { return ptr; }
+
+private:
+ /** From square, 0-63. */
+ int from_;
+
+ /** To square, 0-63. */
+ int to_;
+
+ /** Promotion piece. */
+ int promoteTo_;
+};
+
+inline
+Move::Move(int from, int to, int promoteTo) {
+ from_ = from;
+ to_ = to;
+ promoteTo_ = promoteTo;
+}
+
+inline
+Move::Move(const Move& m) {
+ from_ = m.from_;
+ to_ = m.to_;
+ promoteTo_ = m.promoteTo_;
+}
+
+inline int
+Move::from() const {
+ return from_;
+}
+
+inline int
+Move::to() const {
+ return to_;
+}
+
+inline int
+Move::promoteTo() const {
+ return promoteTo_;
+}
+
+#endif /* MOVE_HPP_ */
diff --git a/DroidFish/jni/rtb/moveGen.cpp b/DroidFish/jni/rtb/moveGen.cpp
new file mode 100644
index 0000000..f603fae
--- /dev/null
+++ b/DroidFish/jni/rtb/moveGen.cpp
@@ -0,0 +1,344 @@
+/*
+ Texel - A UCI chess engine.
+ Copyright (C) 2012-2014 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*
+ * moveGen.cpp
+ *
+ * Created on: Feb 25, 2012
+ * Author: petero
+ */
+
+#include "moveGen.hpp"
+
+
+template void MoveGen::pseudoLegalMoves(const Position& pos, MoveList& moveList);
+template void MoveGen::pseudoLegalMoves(const Position& pos, MoveList& moveList);
+
+template
+void
+MoveGen::pseudoLegalMoves(const Position& pos, MoveList& moveList) {
+ typedef ColorTraits MyColor;
+ const U64 occupied = pos.occupiedBB();
+
+ // Queen moves
+ U64 squares = pos.pieceTypeBB(MyColor::QUEEN);
+ while (squares != 0) {
+ int sq = BitBoard::numberOfTrailingZeros(squares);
+ U64 m = (BitBoard::rookAttacks(sq, occupied) | BitBoard::bishopAttacks(sq, occupied)) & ~pos.colorBB(wtm);
+ addMovesByMask(moveList, sq, m);
+ squares &= squares-1;
+ }
+
+ // Rook moves
+ squares = pos.pieceTypeBB(MyColor::ROOK);
+ while (squares != 0) {
+ int sq = BitBoard::numberOfTrailingZeros(squares);
+ U64 m = BitBoard::rookAttacks(sq, occupied) & ~pos.colorBB(wtm);
+ addMovesByMask(moveList, sq, m);
+ squares &= squares-1;
+ }
+
+ // Bishop moves
+ squares = pos.pieceTypeBB(MyColor::BISHOP);
+ while (squares != 0) {
+ int sq = BitBoard::numberOfTrailingZeros(squares);
+ U64 m = BitBoard::bishopAttacks(sq, occupied) & ~pos.colorBB(wtm);
+ addMovesByMask(moveList, sq, m);
+ squares &= squares-1;
+ }
+
+ // King moves
+ {
+ int sq = pos.getKingSq(wtm);
+ U64 m = BitBoard::kingAttacks[sq] & ~pos.colorBB(wtm);
+ addMovesByMask(moveList, sq, m);
+ const int k0 = wtm ? 4 : 60;
+ if (sq == k0) {
+ const U64 OO_SQ = wtm ? 0x60ULL : 0x6000000000000000ULL;
+ const U64 OOO_SQ = wtm ? 0xEULL : 0xE00000000000000ULL;
+ const int hCastle = wtm ? Position::H1_CASTLE : Position::H8_CASTLE;
+ const int aCastle = wtm ? Position::A1_CASTLE : Position::A8_CASTLE;
+ if (((pos.getCastleMask() & (1 << hCastle)) != 0) &&
+ ((OO_SQ & occupied) == 0) &&
+ (pos.getPiece(k0 + 3) == MyColor::ROOK) &&
+ !sqAttacked(pos, k0) &&
+ !sqAttacked(pos, k0 + 1)) {
+ moveList.addMove(k0, k0 + 2, Piece::EMPTY);
+ }
+ if (((pos.getCastleMask() & (1 << aCastle)) != 0) &&
+ ((OOO_SQ & occupied) == 0) &&
+ (pos.getPiece(k0 - 4) == MyColor::ROOK) &&
+ !sqAttacked(pos, k0) &&
+ !sqAttacked(pos, k0 - 1)) {
+ moveList.addMove(k0, k0 - 2, Piece::EMPTY);
+ }
+ }
+ }
+
+ // Knight moves
+ U64 knights = pos.pieceTypeBB(MyColor::KNIGHT);
+ while (knights != 0) {
+ int sq = BitBoard::numberOfTrailingZeros(knights);
+ U64 m = BitBoard::knightAttacks[sq] & ~pos.colorBB(wtm);
+ addMovesByMask(moveList, sq, m);
+ knights &= knights-1;
+ }
+
+ // Pawn moves
+ const U64 pawns = pos.pieceTypeBB(MyColor::PAWN);
+ const int epSquare = pos.getEpSquare();
+ const U64 epMask = (epSquare >= 0) ? (1ULL << epSquare) : 0ULL;
+ if (wtm) {
+ U64 m = (pawns << 8) & ~occupied;
+ addPawnMovesByMask(moveList, m, -8, true);
+ m = ((m & BitBoard::maskRow3) << 8) & ~occupied;
+ addPawnDoubleMovesByMask(moveList, m, -16);
+
+ m = (pawns << 7) & BitBoard::maskAToGFiles & (pos.colorBB(!wtm) | epMask);
+ addPawnMovesByMask(moveList, m, -7, true);
+
+ m = (pawns << 9) & BitBoard::maskBToHFiles & (pos.colorBB(!wtm) | epMask);
+ addPawnMovesByMask(moveList, m, -9, true);
+ } else {
+ U64 m = (pawns >> 8) & ~occupied;
+ addPawnMovesByMask(moveList, m, 8, true);
+ m = ((m & BitBoard::maskRow6) >> 8) & ~occupied;
+ addPawnDoubleMovesByMask(moveList, m, 16);
+
+ m = (pawns >> 9) & BitBoard::maskAToGFiles & (pos.colorBB(!wtm) | epMask);
+ addPawnMovesByMask(moveList, m, 9, true);
+
+ m = (pawns >> 7) & BitBoard::maskBToHFiles & (pos.colorBB(!wtm) | epMask);
+ addPawnMovesByMask(moveList, m, 7, true);
+ }
+}
+
+template void MoveGen::checkEvasions(const Position& pos, MoveList& moveList);
+template void MoveGen::checkEvasions(const Position& pos, MoveList& moveList);
+
+template
+void
+MoveGen::checkEvasions(const Position& pos, MoveList& moveList) {
+ typedef ColorTraits MyColor;
+ typedef ColorTraits OtherColor;
+ const U64 occupied = pos.occupiedBB();
+
+ const int kingSq = pos.getKingSq(wtm);
+ U64 kingThreats = pos.pieceTypeBB(OtherColor::KNIGHT) & BitBoard::knightAttacks[kingSq];
+ U64 rookPieces = pos.pieceTypeBB(OtherColor::ROOK, OtherColor::QUEEN);
+ if (rookPieces != 0)
+ kingThreats |= rookPieces & BitBoard::rookAttacks(kingSq, occupied);
+ U64 bishPieces = pos.pieceTypeBB(OtherColor::BISHOP, OtherColor::QUEEN);
+ if (bishPieces != 0)
+ kingThreats |= bishPieces & BitBoard::bishopAttacks(kingSq, occupied);
+ const U64 myPawnAttacks = wtm ? BitBoard::wPawnAttacks[kingSq] : BitBoard::bPawnAttacks[kingSq];
+ kingThreats |= pos.pieceTypeBB(OtherColor::PAWN) & myPawnAttacks;
+ U64 validTargets = 0;
+ if ((kingThreats != 0) && ((kingThreats & (kingThreats-1)) == 0)) { // Exactly one attacking piece
+ int threatSq = BitBoard::numberOfTrailingZeros(kingThreats);
+ validTargets = kingThreats | BitBoard::squaresBetween[kingSq][threatSq];
+ }
+ validTargets |= pos.pieceTypeBB(OtherColor::KING);
+ // Queen moves
+ U64 squares = pos.pieceTypeBB(MyColor::QUEEN);
+ while (squares != 0) {
+ int sq = BitBoard::numberOfTrailingZeros(squares);
+ U64 m = (BitBoard::rookAttacks(sq, occupied) | BitBoard::bishopAttacks(sq, occupied)) &
+ ~pos.colorBB(wtm) & validTargets;
+ addMovesByMask(moveList, sq, m);
+ squares &= squares-1;
+ }
+
+ // Rook moves
+ squares = pos.pieceTypeBB(MyColor::ROOK);
+ while (squares != 0) {
+ int sq = BitBoard::numberOfTrailingZeros(squares);
+ U64 m = BitBoard::rookAttacks(sq, occupied) & ~pos.colorBB(wtm) & validTargets;
+ addMovesByMask(moveList, sq, m);
+ squares &= squares-1;
+ }
+
+ // Bishop moves
+ squares = pos.pieceTypeBB(MyColor::BISHOP);
+ while (squares != 0) {
+ int sq = BitBoard::numberOfTrailingZeros(squares);
+ U64 m = BitBoard::bishopAttacks(sq, occupied) & ~pos.colorBB(wtm) & validTargets;
+ addMovesByMask(moveList, sq, m);
+ squares &= squares-1;
+ }
+
+ // King moves
+ {
+ int sq = pos.getKingSq(wtm);
+ U64 m = BitBoard::kingAttacks[sq] & ~pos.colorBB(wtm);
+ addMovesByMask(moveList, sq, m);
+ }
+
+ // Knight moves
+ U64 knights = pos.pieceTypeBB(MyColor::KNIGHT);
+ while (knights != 0) {
+ int sq = BitBoard::numberOfTrailingZeros(knights);
+ U64 m = BitBoard::knightAttacks[sq] & ~pos.colorBB(wtm) & validTargets;
+ addMovesByMask(moveList, sq, m);
+ knights &= knights-1;
+ }
+
+ // Pawn moves
+ const U64 pawns = pos.pieceTypeBB(MyColor::PAWN);
+ const int epSquare = pos.getEpSquare();
+ const U64 epMask = (epSquare >= 0) ? (1ULL << epSquare) : 0ULL;
+ if (wtm) {
+ U64 m = (pawns << 8) & ~occupied;
+ addPawnMovesByMask(moveList, m & validTargets, -8, true);
+ m = ((m & BitBoard::maskRow3) << 8) & ~occupied;
+ addPawnDoubleMovesByMask(moveList, m & validTargets, -16);
+
+ m = (pawns << 7) & BitBoard::maskAToGFiles & ((pos.colorBB(!wtm) & validTargets) | epMask);
+ addPawnMovesByMask(moveList, m, -7, true);
+
+ m = (pawns << 9) & BitBoard::maskBToHFiles & ((pos.colorBB(!wtm) & validTargets) | epMask);
+ addPawnMovesByMask(moveList, m, -9, true);
+ } else {
+ U64 m = (pawns >> 8) & ~occupied;
+ addPawnMovesByMask(moveList, m & validTargets, 8, true);
+ m = ((m & BitBoard::maskRow6) >> 8) & ~occupied;
+ addPawnDoubleMovesByMask(moveList, m & validTargets, 16);
+
+ m = (pawns >> 9) & BitBoard::maskAToGFiles & ((pos.colorBB(!wtm) & validTargets) | epMask);
+ addPawnMovesByMask(moveList, m, 9, true);
+
+ m = (pawns >> 7) & BitBoard::maskBToHFiles & ((pos.colorBB(!wtm) & validTargets) | epMask);
+ addPawnMovesByMask(moveList, m, 7, true);
+ }
+}
+
+template void MoveGen::pseudoLegalCaptures(const Position& pos, MoveList& moveList);
+template void MoveGen::pseudoLegalCaptures(const Position& pos, MoveList& moveList);
+
+template
+void
+MoveGen::pseudoLegalCaptures(const Position& pos, MoveList& moveList) {
+ typedef ColorTraits MyColor;
+ const U64 occupied = pos.occupiedBB();
+
+ // Queen moves
+ U64 squares = pos.pieceTypeBB(MyColor::QUEEN);
+ while (squares != 0) {
+ int sq = BitBoard::numberOfTrailingZeros(squares);
+ U64 m = (BitBoard::rookAttacks(sq, occupied) | BitBoard::bishopAttacks(sq, occupied)) & pos.colorBB(!wtm);
+ addMovesByMask(moveList, sq, m);
+ squares &= squares-1;
+ }
+
+ // Rook moves
+ squares = pos.pieceTypeBB(MyColor::ROOK);
+ while (squares != 0) {
+ int sq = BitBoard::numberOfTrailingZeros(squares);
+ U64 m = BitBoard::rookAttacks(sq, occupied) & pos.colorBB(!wtm);
+ addMovesByMask(moveList, sq, m);
+ squares &= squares-1;
+ }
+
+ // Bishop moves
+ squares = pos.pieceTypeBB(MyColor::BISHOP);
+ while (squares != 0) {
+ int sq = BitBoard::numberOfTrailingZeros(squares);
+ U64 m = BitBoard::bishopAttacks(sq, occupied) & pos.colorBB(!wtm);
+ addMovesByMask(moveList, sq, m);
+ squares &= squares-1;
+ }
+
+ // Knight moves
+ U64 knights = pos.pieceTypeBB(MyColor::KNIGHT);
+ while (knights != 0) {
+ int sq = BitBoard::numberOfTrailingZeros(knights);
+ U64 m = BitBoard::knightAttacks[sq] & pos.colorBB(!wtm);
+ addMovesByMask(moveList, sq, m);
+ knights &= knights-1;
+ }
+
+ // King moves
+ int sq = pos.getKingSq(wtm);
+ U64 m = BitBoard::kingAttacks[sq] & pos.colorBB(!wtm);
+ addMovesByMask(moveList, sq, m);
+
+ // Pawn moves
+ const U64 pawns = pos.pieceTypeBB(MyColor::PAWN);
+ const int epSquare = pos.getEpSquare();
+ const U64 epMask = (epSquare >= 0) ? (1ULL << epSquare) : 0ULL;
+ if (wtm) {
+ m = (pawns << 8) & ~occupied;
+ m &= BitBoard::maskRow8;
+ addPawnMovesByMask(moveList, m, -8, false);
+
+ m = (pawns << 7) & BitBoard::maskAToGFiles & (pos.colorBB(!wtm) | epMask);
+ addPawnMovesByMask(moveList, m, -7, false);
+ m = (pawns << 9) & BitBoard::maskBToHFiles & (pos.colorBB(!wtm) | epMask);
+ addPawnMovesByMask(moveList, m, -9, false);
+ } else {
+ m = (pawns >> 8) & ~occupied;
+ m &= BitBoard::maskRow1;
+ addPawnMovesByMask(moveList, m, 8, false);
+
+ m = (pawns >> 9) & BitBoard::maskAToGFiles & (pos.colorBB(!wtm) | epMask);
+ addPawnMovesByMask(moveList, m, 9, false);
+ m = (pawns >> 7) & BitBoard::maskBToHFiles & (pos.colorBB(!wtm) | epMask);
+ addPawnMovesByMask(moveList, m, 7, false);
+ }
+}
+
+bool
+MoveGen::isLegal(Position& pos, const Move& m, bool isInCheck) {
+ UndoInfo ui;
+ int kSq = pos.getKingSq(pos.isWhiteMove());
+ const int epSquare = pos.getEpSquare();
+ if (isInCheck) {
+ if ((m.from() != kSq) && (m.to() != epSquare)) {
+ U64 occupied = pos.occupiedBB();
+ U64 toMask = 1ULL << m.to();
+ Piece::Type knight = pos.isWhiteMove() ? Piece::BKNIGHT : Piece::WKNIGHT;
+ if (((BitBoard::rookAttacks(kSq, occupied) & toMask) == 0) &&
+ ((BitBoard::bishopAttacks(kSq, occupied) & toMask) == 0) &&
+ ((BitBoard::knightAttacks[kSq] & pos.pieceTypeBB(knight) & toMask) == 0))
+ return false;
+ }
+ pos.makeMove(m, ui);
+ bool legal = !canTakeKing(pos);
+ pos.unMakeMove(m, ui);
+ return legal;
+ } else {
+ if (m.from() == kSq) {
+ U64 occupied = pos.occupiedBB() & ~(1ULL<.
+*/
+
+/*
+ * moveGen.hpp
+ *
+ * Created on: Feb 25, 2012
+ * Author: petero
+ */
+
+#ifndef MOVEGEN_HPP_
+#define MOVEGEN_HPP_
+
+#include "move.hpp"
+#include "position.hpp"
+#include "util.hpp"
+
+#include
+
+//#define MOVELIST_DEBUG
+
+#ifdef MOVELIST_DEBUG
+# include
+# include "textio.hpp"
+#endif
+
+/**
+ * Generates move lists (pseudo-legal, legal, check evasions, captures).
+ */
+class MoveGen {
+private:
+ static const int MAX_MOVES = 256;
+
+public:
+
+ /** A stack-allocated move list object. */
+ class MoveList {
+ private:
+ int buf[sizeof(Move[MAX_MOVES])/sizeof(int)];
+ public:
+ int size;
+
+ MoveList() : size(0) { }
+ void clear() { size = 0; }
+
+ Move& operator[](int i) { return ((Move*)&buf[0])[i]; }
+ const Move& operator[](int i) const { return ((Move*)&buf[0])[i]; }
+
+ void addMove(int from, int to, int promoteTo) {
+ Move& m = (*this)[size++];
+ new (&m) Move(from, to, promoteTo);
+ }
+ };
+
+ /**
+ * Generate and return a list of pseudo-legal moves.
+ * Pseudo-legal means that the moves don't necessarily defend from check threats.
+ */
+ template
+ static void pseudoLegalMoves(const Position& pos, MoveList& moveList);
+ static void pseudoLegalMoves(const Position& pos, MoveList& moveList);
+
+ /**
+ * Generate and return a list of pseudo-legal check evasion moves.
+ * Pseudo-legal means that the moves don't necessarily defend from check threats.
+ */
+ template
+ static void checkEvasions(const Position& pos, MoveList& moveList);
+ static void checkEvasions(const Position& pos, MoveList& moveList);
+
+ /** Generate and return a list of pseudo-legal capture moves. */
+ template
+ static void pseudoLegalCaptures(const Position& pos, MoveList& moveList);
+ static void pseudoLegalCaptures(const Position& pos, MoveList& moveList);
+
+ /**
+ * Return true if the side to move is in check.
+ */
+ static bool inCheck(const Position& pos) {
+ int kingSq = pos.getKingSq(pos.isWhiteMove());
+ return sqAttacked(pos, kingSq);
+ }
+
+ /**
+ * Return true if the side to move can take the opponents king.
+ */
+ static bool canTakeKing(Position& pos) {
+ pos.setWhiteMove(!pos.isWhiteMove());
+ bool ret = inCheck(pos);
+ pos.setWhiteMove(!pos.isWhiteMove());
+ return ret;
+ }
+
+ /**
+ * Return true if a square is attacked by the opposite side.
+ */
+ static bool sqAttacked(const Position& pos, int sq) {
+ const U64 occupied = pos.occupiedBB();
+ return sqAttacked(pos, sq, occupied);
+ }
+ static bool sqAttacked(const Position& pos, int sq, U64 occupied) {
+ return pos.isWhiteMove() ? sqAttacked(pos, sq, occupied)
+ : sqAttacked(pos, sq, occupied);
+ }
+ template
+ static bool sqAttacked(const Position& pos, int sq, U64 occupied) {
+ typedef ColorTraits OtherColor;
+ if ((BitBoard::knightAttacks[sq] & pos.pieceTypeBB(OtherColor::KNIGHT)) != 0)
+ return true;
+ if ((BitBoard::kingAttacks[sq] & pos.pieceTypeBB(OtherColor::KING)) != 0)
+ return true;
+ if (wtm) {
+ if ((BitBoard::wPawnAttacks[sq] & pos.pieceTypeBB(OtherColor::PAWN)) != 0)
+ return true;
+ } else {
+ if ((BitBoard::bPawnAttacks[sq] & pos.pieceTypeBB(OtherColor::PAWN)) != 0)
+ return true;
+ }
+ U64 bbQueen = pos.pieceTypeBB(OtherColor::QUEEN);
+ if ((BitBoard::bishopAttacks(sq, occupied) & (pos.pieceTypeBB(OtherColor::BISHOP) | bbQueen)) != 0)
+ return true;
+ if ((BitBoard::rookAttacks(sq, occupied) & (pos.pieceTypeBB(OtherColor::ROOK) | bbQueen)) != 0)
+ return true;
+ return false;
+ }
+
+ /** Return true if the pseudo-legal move "move" is legal is position "pos".
+ * isInCheck must be equal to inCheck(pos). */
+ static bool isLegal(Position& pos, const Move& move, bool isInCheck);
+
+private:
+ template
+ static void addPawnMovesByMask(MoveList& moveList, U64 mask, int delta, bool allPromotions) {
+ typedef ColorTraits MyColor;
+ if (mask == 0)
+ return;
+ U64 promMask = mask & BitBoard::maskRow1Row8;
+ mask &= ~promMask;
+ while (promMask != 0) {
+ int sq = BitBoard::numberOfTrailingZeros(promMask);
+ int sq0 = sq + delta;
+ moveList.addMove(sq0, sq, MyColor::QUEEN);
+ moveList.addMove(sq0, sq, MyColor::KNIGHT);
+ if (allPromotions) {
+ moveList.addMove(sq0, sq, MyColor::ROOK);
+ moveList.addMove(sq0, sq, MyColor::BISHOP);
+ }
+ promMask &= (promMask - 1);
+ }
+ while (mask != 0) {
+ int sq = BitBoard::numberOfTrailingZeros(mask);
+ moveList.addMove(sq + delta, sq, Piece::EMPTY);
+ mask &= (mask - 1);
+ }
+ }
+
+ static void addPawnDoubleMovesByMask(MoveList& moveList, U64 mask, int delta) {
+ while (mask != 0) {
+ int sq = BitBoard::numberOfTrailingZeros(mask);
+ moveList.addMove(sq + delta, sq, Piece::EMPTY);
+ mask &= (mask - 1);
+ }
+ }
+
+ static void addMovesByMask(MoveList& moveList, int sq0, U64 mask) {
+ while (mask != 0) {
+ int sq = BitBoard::numberOfTrailingZeros(mask);
+ moveList.addMove(sq0, sq, Piece::EMPTY);
+ mask &= (mask - 1);
+ }
+ }
+
+ MoveGen() = delete;
+};
+
+
+inline void
+MoveGen::pseudoLegalMoves(const Position& pos, MoveList& moveList) {
+ if (pos.isWhiteMove())
+ pseudoLegalMoves(pos, moveList);
+ else
+ pseudoLegalMoves(pos, moveList);
+}
+
+inline void
+MoveGen::checkEvasions(const Position& pos, MoveList& moveList) {
+ if (pos.isWhiteMove())
+ checkEvasions(pos, moveList);
+ else
+ checkEvasions(pos, moveList);
+}
+
+inline void
+MoveGen::pseudoLegalCaptures(const Position& pos, MoveList& moveList) {
+ if (pos.isWhiteMove())
+ pseudoLegalCaptures(pos, moveList);
+ else
+ pseudoLegalCaptures(pos, moveList);
+}
+
+#endif /* MOVEGEN_HPP_ */
diff --git a/DroidFish/jni/rtb/piece.hpp b/DroidFish/jni/rtb/piece.hpp
new file mode 100644
index 0000000..26e10f8
--- /dev/null
+++ b/DroidFish/jni/rtb/piece.hpp
@@ -0,0 +1,86 @@
+/*
+ Texel - A UCI chess engine.
+ Copyright (C) 2012-2013 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*
+ * piece.hpp
+ *
+ * Created on: Feb 25, 2012
+ * Author: petero
+ */
+
+#ifndef PIECE_HPP_
+#define PIECE_HPP_
+
+/**
+ * Constants for different piece types.
+ */
+class Piece {
+public:
+ enum Type {
+ EMPTY = 0,
+ WKING = 1,
+ WQUEEN = 2,
+ WROOK = 3,
+ WBISHOP = 4,
+ WKNIGHT = 5,
+ WPAWN = 6,
+
+ BKING = 7,
+ BQUEEN = 8,
+ BROOK = 9,
+ BBISHOP = 10,
+ BKNIGHT = 11,
+ BPAWN = 12,
+
+ nPieceTypes = 13
+ };
+
+ /**
+ * Return true if p is a white piece, false otherwise.
+ * Note that if p is EMPTY, an unspecified value is returned.
+ */
+ static bool isWhite(int pType);
+};
+
+template struct ColorTraits {
+};
+
+template<> struct ColorTraits {
+ static const Piece::Type KING = Piece::WKING;
+ static const Piece::Type QUEEN = Piece::WQUEEN;
+ static const Piece::Type ROOK = Piece::WROOK;
+ static const Piece::Type BISHOP = Piece::WBISHOP;
+ static const Piece::Type KNIGHT = Piece::WKNIGHT;
+ static const Piece::Type PAWN = Piece::WPAWN;
+};
+
+template<> struct ColorTraits {
+ static const Piece::Type KING = Piece::BKING;
+ static const Piece::Type QUEEN = Piece::BQUEEN;
+ static const Piece::Type ROOK = Piece::BROOK;
+ static const Piece::Type BISHOP = Piece::BBISHOP;
+ static const Piece::Type KNIGHT = Piece::BKNIGHT;
+ static const Piece::Type PAWN = Piece::BPAWN;
+};
+
+inline bool
+Piece::isWhite(int pType) {
+ return pType < BKING;
+}
+
+#endif /* PIECE_HPP_ */
diff --git a/DroidFish/jni/rtb/position.cpp b/DroidFish/jni/rtb/position.cpp
new file mode 100644
index 0000000..3708a83
--- /dev/null
+++ b/DroidFish/jni/rtb/position.cpp
@@ -0,0 +1,211 @@
+/*
+ Texel - A UCI chess engine.
+ Copyright (C) 2012-2014 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*
+ * position.cpp
+ *
+ * Created on: Feb 25, 2012
+ * Author: petero
+ */
+
+#include "position.hpp"
+
+
+Position::Position() {
+ for (int i = 0; i < 64; i++)
+ squares[i] = Piece::EMPTY;
+ for (int i = 0; i < Piece::nPieceTypes; i++)
+ pieceTypeBB_[i] = 0;
+ whiteBB_ = blackBB_ = 0;
+ whiteMove = true;
+ castleMask = 0;
+ epSquare = -1;
+ halfMoveClock = 0;
+ fullMoveCounter = 1;
+ matId = {};
+ for (int sq = 0; sq < 64; sq++) {
+ int p = squares[sq];
+ matId.addPiece(p);
+ }
+ wKingSq_ = bKingSq_ = -1;
+}
+
+void
+Position::setPiece(int square, int piece) {
+ int removedPiece = squares[square];
+ squares[square] = piece;
+
+ // Update material identifier
+ matId.removePiece(removedPiece);
+ matId.addPiece(piece);
+
+ // Update bitboards
+ const U64 sqMask = 1ULL << square;
+ pieceTypeBB_[removedPiece] &= ~sqMask;
+ pieceTypeBB_[piece] |= sqMask;
+
+ if (removedPiece != Piece::EMPTY) {
+ if (Piece::isWhite(removedPiece)) {
+ whiteBB_ &= ~sqMask;
+ } else {
+ blackBB_ &= ~sqMask;
+ }
+ }
+
+ if (piece != Piece::EMPTY) {
+ if (Piece::isWhite(piece)) {
+ whiteBB_ |= sqMask;
+ if (piece == Piece::WKING)
+ wKingSq_ = square;
+ } else {
+ blackBB_ |= sqMask;
+ if (piece == Piece::BKING)
+ bKingSq_ = square;
+ }
+ }
+}
+
+void
+Position::makeMove(const Move& move, UndoInfo& ui) {
+ ui.capturedPiece = squares[move.to()];
+ ui.castleMask = castleMask;
+ ui.epSquare = epSquare;
+ ui.halfMoveClock = halfMoveClock;
+ bool wtm = whiteMove;
+
+ const int p = squares[move.from()];
+ int capP = squares[move.to()];
+ U64 fromMask = 1ULL << move.from();
+
+ int prevEpSquare = epSquare;
+ setEpSquare(-1);
+
+ if ((capP != Piece::EMPTY) || ((pieceTypeBB(Piece::WPAWN, Piece::BPAWN) & fromMask) != 0)) {
+ halfMoveClock = 0;
+
+ // Handle en passant and epSquare
+ if (p == Piece::WPAWN) {
+ if (move.to() - move.from() == 2 * 8) {
+ int x = getX(move.to());
+ if (BitBoard::epMaskW[x] & pieceTypeBB(Piece::BPAWN))
+ setEpSquare(move.from() + 8);
+ } else if (move.to() == prevEpSquare) {
+ setPiece(move.to() - 8, Piece::EMPTY);
+ }
+ } else if (p == Piece::BPAWN) {
+ if (move.to() - move.from() == -2 * 8) {
+ int x = getX(move.to());
+ if (BitBoard::epMaskB[x] & pieceTypeBB(Piece::WPAWN))
+ setEpSquare(move.from() - 8);
+ } else if (move.to() == prevEpSquare) {
+ setPiece(move.to() + 8, Piece::EMPTY);
+ }
+ }
+
+ if ((pieceTypeBB(Piece::WKING, Piece::BKING) & fromMask) != 0) {
+ if (wtm) {
+ setCastleMask(castleMask & ~(1 << A1_CASTLE));
+ setCastleMask(castleMask & ~(1 << H1_CASTLE));
+ } else {
+ setCastleMask(castleMask & ~(1 << A8_CASTLE));
+ setCastleMask(castleMask & ~(1 << H8_CASTLE));
+ }
+ }
+
+ // Perform move
+ setPiece(move.from(), Piece::EMPTY);
+ // Handle promotion
+ if (move.promoteTo() != Piece::EMPTY) {
+ setPiece(move.to(), move.promoteTo());
+ } else {
+ setPiece(move.to(), p);
+ }
+ } else {
+ halfMoveClock++;
+
+ // Handle castling
+ if ((pieceTypeBB(Piece::WKING, Piece::BKING) & fromMask) != 0) {
+ int k0 = move.from();
+ if (move.to() == k0 + 2) { // O-O
+ movePieceNotPawn(k0 + 3, k0 + 1);
+ } else if (move.to() == k0 - 2) { // O-O-O
+ movePieceNotPawn(k0 - 4, k0 - 1);
+ }
+ if (wtm) {
+ setCastleMask(castleMask & ~(1 << A1_CASTLE));
+ setCastleMask(castleMask & ~(1 << H1_CASTLE));
+ } else {
+ setCastleMask(castleMask & ~(1 << A8_CASTLE));
+ setCastleMask(castleMask & ~(1 << H8_CASTLE));
+ }
+ }
+
+ // Perform move
+ movePieceNotPawn(move.from(), move.to());
+ }
+ if (wtm) {
+ // Update castling rights when rook moves
+ if ((BitBoard::maskCorners & fromMask) != 0) {
+ if (p == Piece::WROOK)
+ removeCastleRights(move.from());
+ }
+ if ((BitBoard::maskCorners & (1ULL << move.to())) != 0) {
+ if (capP == Piece::BROOK)
+ removeCastleRights(move.to());
+ }
+ } else {
+ fullMoveCounter++;
+ // Update castling rights when rook moves
+ if ((BitBoard::maskCorners & fromMask) != 0) {
+ if (p == Piece::BROOK)
+ removeCastleRights(move.from());
+ }
+ if ((BitBoard::maskCorners & (1ULL << move.to())) != 0) {
+ if (capP == Piece::WROOK)
+ removeCastleRights(move.to());
+ }
+ }
+
+ whiteMove = !wtm;
+}
+
+void
+Position::movePieceNotPawn(int from, int to) {
+ const int piece = squares[from];
+
+ squares[from] = Piece::EMPTY;
+ squares[to] = piece;
+
+ const U64 sqMaskF = 1ULL << from;
+ const U64 sqMaskT = 1ULL << to;
+ pieceTypeBB_[piece] &= ~sqMaskF;
+ pieceTypeBB_[piece] |= sqMaskT;
+ if (Piece::isWhite(piece)) {
+ whiteBB_ &= ~sqMaskF;
+ whiteBB_ |= sqMaskT;
+ if (piece == Piece::WKING)
+ wKingSq_ = to;
+ } else {
+ blackBB_ &= ~sqMaskF;
+ blackBB_ |= sqMaskT;
+ if (piece == Piece::BKING)
+ bKingSq_ = to;
+ }
+}
+
+
diff --git a/DroidFish/jni/rtb/position.hpp b/DroidFish/jni/rtb/position.hpp
new file mode 100644
index 0000000..3253f13
--- /dev/null
+++ b/DroidFish/jni/rtb/position.hpp
@@ -0,0 +1,318 @@
+/*
+ Texel - A UCI chess engine.
+ Copyright (C) 2012-2014 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*
+ * position.hpp
+ *
+ * Created on: Feb 25, 2012
+ * Author: petero
+ */
+
+#ifndef POSITION_HPP_
+#define POSITION_HPP_
+
+#include "move.hpp"
+#include "undoInfo.hpp"
+#include "bitBoard.hpp"
+#include "piece.hpp"
+#include "material.hpp"
+#include
+#include
+
+/**
+ * Stores the state of a chess position.
+ * All required state is stored, except for all previous positions
+ * since the last capture or pawn move. That state is only needed
+ * for three-fold repetition draw detection, and is better stored
+ * in a separate hash table.
+ */
+class Position {
+public:
+ /** Bit definitions for the castleMask bit mask. */
+ static const int A1_CASTLE = 0; /** White long castle. */
+ static const int H1_CASTLE = 1; /** White short castle. */
+ static const int A8_CASTLE = 2; /** Black long castle. */
+ static const int H8_CASTLE = 3; /** Black short castle. */
+
+ /** Initialize board to empty position. */
+ Position();
+
+ /** Return the material identifier. */
+ int materialId() const;
+
+ bool isWhiteMove() const;
+ void setWhiteMove(bool whiteMove);
+
+ /** Return piece occupying a square. */
+ int getPiece(int square) const;
+
+ /** Set a square to a piece value. */
+ void setPiece(int square, int piece);
+
+ /** Bitmask describing castling rights. */
+ int getCastleMask() const;
+ void setCastleMask(int castleMask);
+
+ /** En passant square, or -1 if no en passant possible. */
+ int getEpSquare() const;
+
+ void setEpSquare(int epSquare);
+
+ int getKingSq(bool white) const;
+
+ /** Apply a move to the current position. */
+ void makeMove(const Move& move, UndoInfo& ui);
+
+ void unMakeMove(const Move& move, const UndoInfo& ui);
+
+ int getFullMoveCounter() const;
+ void setFullMoveCounter(int fm);
+ int getHalfMoveClock() const;
+ void setHalfMoveClock(int hm);
+
+ /** BitBoard for all squares occupied by a piece type. */
+ U64 pieceTypeBB(Piece::Type piece) const;
+ /** BitBoard for all squares occupied by several piece types. */
+ template U64 pieceTypeBB(Piece0 piece0, Pieces... pieces) const;
+
+ /** BitBoard for all squares occupied by white pieces. */
+ U64 whiteBB() const;
+ /** BitBoard for all squares occupied by black pieces. */
+ U64 blackBB() const;
+ /** BitBoard for all squares occupied by white or black pieces. */
+ U64 colorBB(int wtm) const;
+
+ /** BitBoard for all squares occupied by white and black pieces. */
+ U64 occupiedBB() const;
+
+ int wKingSq() const;
+ int bKingSq() const;
+
+ /** Return index in squares[] vector corresponding to (x,y). */
+ static int getSquare(int x, int y);
+
+ /** Return x position (file) corresponding to a square. */
+ static int getX(int square);
+
+ /** Return y position (rank) corresponding to a square. */
+ static int getY(int square);
+
+ /** Return true if (x,y) is a dark square. */
+ static bool darkSquare(int x, int y);
+
+ /** Initialize static data. */
+ static void staticInitialize();
+
+private:
+ /** Move a non-pawn piece to an empty square. */
+ void movePieceNotPawn(int from, int to);
+
+ void removeCastleRights(int square);
+
+
+ int wKingSq_, bKingSq_; // Cached king positions
+
+ int squares[64];
+
+ // Bitboards
+ U64 pieceTypeBB_[Piece::nPieceTypes];
+ U64 whiteBB_, blackBB_;
+
+ bool whiteMove;
+
+ /** Number of half-moves since last 50-move reset. */
+ int halfMoveClock;
+
+ /** Game move number, starting from 1. */
+ int fullMoveCounter;
+
+ int castleMask;
+ int epSquare;
+
+ MatId matId; // Cached material identifier
+};
+
+inline int
+Position::materialId() const {
+ return matId();
+}
+
+inline bool
+Position::isWhiteMove() const {
+ return whiteMove;
+}
+
+inline void
+Position::setWhiteMove(bool whiteMove) {
+ this->whiteMove = whiteMove;
+}
+
+inline int
+Position::getPiece(int square) const {
+ return squares[square];
+}
+
+inline int
+Position::getCastleMask() const {
+ return castleMask;
+}
+
+inline void
+Position::setCastleMask(int castleMask) {
+ this->castleMask = castleMask;
+}
+
+inline int
+Position::getEpSquare() const {
+ return epSquare;
+}
+
+inline void
+Position::setEpSquare(int epSquare) {
+ this->epSquare = epSquare;
+}
+
+inline int
+Position::getKingSq(bool white) const {
+ return white ? wKingSq() : bKingSq();
+}
+
+inline void
+Position::unMakeMove(const Move& move, const UndoInfo& ui) {
+ whiteMove = !whiteMove;
+ int p = squares[move.to()];
+ setPiece(move.from(), p);
+ setPiece(move.to(), ui.capturedPiece);
+ setCastleMask(ui.castleMask);
+ setEpSquare(ui.epSquare);
+ halfMoveClock = ui.halfMoveClock;
+ bool wtm = whiteMove;
+ if (move.promoteTo() != Piece::EMPTY) {
+ p = wtm ? Piece::WPAWN : Piece::BPAWN;
+ setPiece(move.from(), p);
+ }
+ if (!wtm)
+ fullMoveCounter--;
+
+ // Handle castling
+ int king = wtm ? Piece::WKING : Piece::BKING;
+ if (p == king) {
+ int k0 = move.from();
+ if (move.to() == k0 + 2) { // O-O
+ movePieceNotPawn(k0 + 1, k0 + 3);
+ } else if (move.to() == k0 - 2) { // O-O-O
+ movePieceNotPawn(k0 - 1, k0 - 4);
+ }
+ }
+
+ // Handle en passant
+ if (move.to() == epSquare) {
+ if (p == Piece::WPAWN) {
+ setPiece(move.to() - 8, Piece::BPAWN);
+ } else if (p == Piece::BPAWN) {
+ setPiece(move.to() + 8, Piece::WPAWN);
+ }
+ }
+}
+
+inline int
+Position::getSquare(int x, int y) {
+ return y * 8 + x;
+}
+
+/** Return x position (file) corresponding to a square. */
+inline int
+Position::getX(int square) {
+ return square & 7;
+}
+
+/** Return y position (rank) corresponding to a square. */
+inline int
+Position::getY(int square) {
+ return square >> 3;
+}
+
+/** Return true if (x,y) is a dark square. */
+inline bool
+Position::darkSquare(int x, int y) {
+ return (x & 1) == (y & 1);
+}
+
+inline void
+Position::removeCastleRights(int square) {
+ if (square == getSquare(0, 0)) {
+ setCastleMask(castleMask & ~(1 << A1_CASTLE));
+ } else if (square == getSquare(7, 0)) {
+ setCastleMask(castleMask & ~(1 << H1_CASTLE));
+ } else if (square == getSquare(0, 7)) {
+ setCastleMask(castleMask & ~(1 << A8_CASTLE));
+ } else if (square == getSquare(7, 7)) {
+ setCastleMask(castleMask & ~(1 << H8_CASTLE));
+ }
+}
+
+inline int Position::getFullMoveCounter() const {
+ return fullMoveCounter;
+}
+
+inline void Position::setFullMoveCounter(int fm) {
+ fullMoveCounter = fm;
+}
+
+inline int Position::getHalfMoveClock() const {
+ return halfMoveClock;
+}
+
+inline void Position::setHalfMoveClock(int hm) {
+ halfMoveClock = hm;
+}
+
+inline U64 Position::pieceTypeBB(Piece::Type piece) const {
+ return pieceTypeBB_[piece];
+}
+
+template
+inline U64 Position::pieceTypeBB(Piece0 piece0, Pieces... pieces) const {
+ return pieceTypeBB(piece0) | pieceTypeBB(pieces...);
+}
+
+inline U64 Position::whiteBB() const {
+ return whiteBB_;
+}
+
+inline U64 Position::blackBB() const {
+ return blackBB_;
+};
+
+inline U64 Position::colorBB(int wtm) const {
+ return wtm ? whiteBB_ : blackBB_;
+}
+
+inline U64 Position::occupiedBB() const {
+ return whiteBB() | blackBB();
+}
+
+inline int Position::wKingSq() const {
+ return wKingSq_;
+}
+
+inline int Position::bKingSq() const {
+ return bKingSq_;
+}
+
+#endif /* POSITION_HPP_ */
diff --git a/DroidFish/jni/rtb/rtb-core-impl.hpp b/DroidFish/jni/rtb/rtb-core-impl.hpp
new file mode 100644
index 0000000..243b7bb
--- /dev/null
+++ b/DroidFish/jni/rtb/rtb-core-impl.hpp
@@ -0,0 +1,1619 @@
+/*
+ Copyright (c) 2011-2013 Ronald de Man
+ This file may be redistributed and/or modified without restrictions.
+
+ tbcore.c contains engine-independent routines of the tablebase probing code.
+ This file should not need to much adaptation to add tablebase probing to
+ a particular engine, provided the engine is written in C or C++.
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#ifndef __WIN32__
+#include
+#endif
+#include "rtb-core.hpp"
+
+
+#define TBMAX_PIECE 254
+#define TBMAX_PAWN 256
+#define HSHMAX 4
+
+// for variants where kings can connect and/or captured
+// #define CONNECTED_KINGS
+
+#define Swap(a,b) {int tmp=a;a=b;b=tmp;}
+
+#define TB_PAWN 1
+#define TB_KNIGHT 2
+#define TB_BISHOP 3
+#define TB_ROOK 4
+#define TB_QUEEN 5
+#define TB_KING 6
+
+#define TB_WPAWN TB_PAWN
+#define TB_BPAWN (TB_PAWN | 8)
+
+static std::mutex TB_mutex;
+
+static bool initialized = false;
+static int num_paths = 0;
+static char *path_string = NULL;
+static char **paths = NULL;
+
+static int TBnum_piece, TBnum_pawn;
+static struct TBEntry_piece TB_piece[TBMAX_PIECE];
+static struct TBEntry_pawn TB_pawn[TBMAX_PAWN];
+
+static struct TBHashEntry WDL_hash[1 << TBHASHBITS][HSHMAX];
+static struct DTZTableEntry DTZ_hash[1 << TBHASHBITS][HSHMAX];
+
+static void init_indices(void);
+static uint64_t calc_key_from_pcs(const int *pcs, bool mirror);
+static void free_wdl_entry(struct TBEntry *entry);
+static void free_dtz_entry(struct TBEntry *entry);
+
+static FD open_tb(const char *str, const char *suffix)
+{
+ for (int i = 0; i < num_paths; i++) {
+ std::string file(paths[i]);
+ file += '/';
+ file += str;
+ file += suffix;
+#ifndef __WIN32__
+ FD fd = open(file.c_str(), O_RDONLY);
+#else
+ FD fd = CreateFile(file.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
+ if (fd != FD_ERR) return fd;
+ }
+ return FD_ERR;
+}
+
+static void close_tb(FD fd)
+{
+#ifndef __WIN32__
+ close(fd);
+#else
+ CloseHandle(fd);
+#endif
+}
+
+static char *map_file(const char *name, const char *suffix, uint64_t *mapping)
+{
+ FD fd = open_tb(name, suffix);
+ if (fd == FD_ERR)
+ return NULL;
+#ifndef __WIN32__
+ struct stat statbuf;
+ fstat(fd, &statbuf);
+ *mapping = statbuf.st_size;
+ char *data = (char *)mmap(NULL, statbuf.st_size, PROT_READ,
+ MAP_SHARED, fd, 0);
+ if (data == (char *)(-1)) {
+ std::cout << "Could not mmap() " << name << std::endl;
+ close_tb(fd);
+ return NULL;
+ }
+#else
+ DWORD size_low, size_high;
+ size_low = GetFileSize(fd, &size_high);
+ // *size = ((uint64_t)size_high) << 32 | ((uint64_t)size_low);
+ HANDLE map = CreateFileMapping(fd, NULL, PAGE_READONLY, size_high, size_low,
+ NULL);
+ if (map == NULL) {
+ std::cout << "CreateFileMapping() failed" << std::endl;
+ close_tb(fd);
+ return NULL;
+ }
+ *mapping = (uint64_t)map;
+ char *data = (char *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
+ if (data == NULL) {
+ std::cout << "MapViewOfFile() failed, name = " << name << suffix << ", error = " << GetLastError() << std::endl;
+ close_tb(fd);
+ return NULL;
+ }
+#endif
+ close_tb(fd);
+ return data;
+}
+
+#ifndef __WIN32__
+static void unmap_file(char *data, uint64_t size)
+{
+ if (!data) return;
+ munmap(data, size);
+}
+#else
+static void unmap_file(char *data, uint64_t mapping)
+{
+ if (!data) return;
+ UnmapViewOfFile(data);
+ CloseHandle((HANDLE)mapping);
+}
+#endif
+
+static void add_to_hash(struct TBEntry *ptr, uint64_t key)
+{
+ int i, hshidx;
+
+ hshidx = key >> (64 - TBHASHBITS);
+ i = 0;
+ while (i < HSHMAX && WDL_hash[hshidx][i].ptr)
+ i++;
+ assert(i < HSHMAX);
+ WDL_hash[hshidx][i].key = key;
+ WDL_hash[hshidx][i].ptr = ptr;
+}
+
+static void add_to_dtz_hash(uint64_t key1, uint64_t key2)
+{
+ int i, hshidx;
+
+ hshidx = key1 >> (64 - TBHASHBITS);
+ i = 0;
+ while (i < HSHMAX && DTZ_hash[hshidx][i].key1)
+ i++;
+ assert(i < HSHMAX);
+ DTZ_hash[hshidx][i].key1 = key1;
+ DTZ_hash[hshidx][i].key2 = key2;
+ DTZ_hash[hshidx][i].entry = NULL;
+}
+
+static char pchr[] = {'K', 'Q', 'R', 'B', 'N', 'P'};
+
+static void init_tb(char *str)
+{
+ FD fd;
+ struct TBEntry *entry;
+ int i, j, pcs[16];
+ uint64_t key, key2;
+ int color;
+ char *s;
+
+ fd = open_tb(str, WDLSUFFIX);
+ if (fd == FD_ERR) return;
+ close_tb(fd);
+
+ for (i = 0; i < 16; i++)
+ pcs[i] = 0;
+ color = 0;
+ for (s = str; *s; s++)
+ switch (*s) {
+ case 'P':
+ pcs[TB_PAWN | color]++;
+ break;
+ case 'N':
+ pcs[TB_KNIGHT | color]++;
+ break;
+ case 'B':
+ pcs[TB_BISHOP | color]++;
+ break;
+ case 'R':
+ pcs[TB_ROOK | color]++;
+ break;
+ case 'Q':
+ pcs[TB_QUEEN | color]++;
+ break;
+ case 'K':
+ pcs[TB_KING | color]++;
+ break;
+ case 'v':
+ color = 0x08;
+ break;
+ }
+ for (i = 0; i < 8; i++)
+ if (pcs[i] != pcs[i+8])
+ break;
+ key = calc_key_from_pcs(pcs, false);
+ key2 = calc_key_from_pcs(pcs, true);
+ if (pcs[TB_WPAWN] + pcs[TB_BPAWN] == 0) {
+ assert(TBnum_piece < TBMAX_PIECE);
+ entry = (struct TBEntry *)&TB_piece[TBnum_piece++];
+ } else {
+ assert(TBnum_pawn < TBMAX_PAWN);
+ entry = (struct TBEntry *)&TB_pawn[TBnum_pawn++];
+ }
+ entry->key = key;
+ entry->ready = 0;
+ entry->num = 0;
+ for (i = 0; i < 16; i++)
+ entry->num += pcs[i];
+ entry->symmetric = (key == key2);
+ entry->has_pawns = (pcs[TB_WPAWN] + pcs[TB_BPAWN] > 0);
+ if (entry->num > Syzygy::TBLargest)
+ Syzygy::TBLargest = entry->num;
+
+ if (entry->has_pawns) {
+ struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry;
+ ptr->pawns[0] = pcs[TB_WPAWN];
+ ptr->pawns[1] = pcs[TB_BPAWN];
+ if (pcs[TB_BPAWN] > 0
+ && (pcs[TB_WPAWN] == 0 || pcs[TB_BPAWN] < pcs[TB_WPAWN])) {
+ ptr->pawns[0] = pcs[TB_BPAWN];
+ ptr->pawns[1] = pcs[TB_WPAWN];
+ }
+ } else {
+ struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry;
+ for (i = 0, j = 0; i < 16; i++)
+ if (pcs[i] == 1) j++;
+ if (j >= 3) ptr->enc_type = 0;
+ else if (j == 2) ptr->enc_type = 2;
+ else { /* only for suicide */
+ j = 16;
+ for (i = 0; i < 16; i++) {
+ if (pcs[i] < j && pcs[i] > 1) j = pcs[i];
+ ptr->enc_type = 1 + j;
+ }
+ }
+ }
+ add_to_hash(entry, key);
+ if (key2 != key) add_to_hash(entry, key2);
+ add_to_dtz_hash(key, key2);
+}
+
+void Syzygy::init(const std::string& path)
+{
+ char str[16];
+ int i, j, k, l;
+
+ { // The probing code currently expects a little-endian architecture
+ static_assert(sizeof(uint32_t) == 4, "Unsupported architecture");
+ uint32_t test = 0x01020304;
+ unsigned char* p = (unsigned char*)&test;
+ if (p[0] != 4 || p[1] != 3 || p[2] != 2 || p[3] != 1)
+ return;
+ }
+
+ if (initialized) {
+ free(path_string); path_string = NULL;
+ free(paths); paths = NULL;
+ struct TBEntry *entry;
+ for (i = 0; i < TBnum_piece; i++) {
+ entry = (struct TBEntry *)&TB_piece[i];
+ free_wdl_entry(entry);
+ }
+ for (i = 0; i < TBnum_pawn; i++) {
+ entry = (struct TBEntry *)&TB_pawn[i];
+ free_wdl_entry(entry);
+ }
+ for (i = 0; i < (1 << TBHASHBITS); i++)
+ for (j = 0; j < HSHMAX; j++) {
+ if (DTZ_hash[i][j].entry) {
+ free_dtz_entry(DTZ_hash[i][j].entry);
+ DTZ_hash[i][j].entry = NULL;
+ }
+ }
+ TBnum_piece = TBnum_pawn = 0;
+ TBLargest = 0;
+ } else {
+ init_indices();
+ initialized = true;
+ }
+
+ const char *p = path.c_str();
+ if (strlen(p) == 0)
+ return;
+ path_string = (char *)malloc(strlen(p) + 1);
+ strcpy(path_string, p);
+ num_paths = 0;
+ for (i = 0;; i++) {
+ if (path_string[i] && path_string[i] != SEP_CHAR)
+ num_paths++;
+ while (path_string[i] && path_string[i] != SEP_CHAR)
+ i++;
+ if (!path_string[i]) break;
+ path_string[i] = 0;
+ }
+ paths = (char **)malloc(num_paths * sizeof(char *));
+ for (i = j = 0; i < num_paths; i++) {
+ while (!path_string[j]) j++;
+ paths[i] = &path_string[j];
+ while (path_string[j]) j++;
+ }
+
+ for (i = 0; i < (1 << TBHASHBITS); i++)
+ for (j = 0; j < HSHMAX; j++) {
+ WDL_hash[i][j].key = 0ULL;
+ WDL_hash[i][j].ptr = NULL;
+ }
+
+ for (i = 0; i < (1 << TBHASHBITS); i++)
+ for (j = 0; j < HSHMAX; j++) {
+ DTZ_hash[i][j].key1 = 0ULL;
+ DTZ_hash[i][j].key2 = 0ULL;
+ DTZ_hash[i][j].entry = NULL;
+ }
+
+ for (i = 1; i < 6; i++) {
+ sprintf(str, "K%cvK", pchr[i]);
+ init_tb(str);
+ }
+
+ for (i = 1; i < 6; i++)
+ for (j = i; j < 6; j++) {
+ sprintf(str, "K%cvK%c", pchr[i], pchr[j]);
+ init_tb(str);
+ }
+
+ for (i = 1; i < 6; i++)
+ for (j = i; j < 6; j++) {
+ sprintf(str, "K%c%cvK", pchr[i], pchr[j]);
+ init_tb(str);
+ }
+
+ for (i = 1; i < 6; i++)
+ for (j = i; j < 6; j++)
+ for (k = 1; k < 6; k++) {
+ sprintf(str, "K%c%cvK%c", pchr[i], pchr[j], pchr[k]);
+ init_tb(str);
+ }
+
+ for (i = 1; i < 6; i++)
+ for (j = i; j < 6; j++)
+ for (k = j; k < 6; k++) {
+ sprintf(str, "K%c%c%cvK", pchr[i], pchr[j], pchr[k]);
+ init_tb(str);
+ }
+
+ // 6-piece tables are only supported for 64-bit, because tables are mmap()ed into memory
+ if (sizeof(char*) >= 8) {
+ for (i = 1; i < 6; i++)
+ for (j = i; j < 6; j++)
+ for (k = i; k < 6; k++)
+ for (l = (i == k) ? j : k; l < 6; l++) {
+ sprintf(str, "K%c%cvK%c%c", pchr[i], pchr[j], pchr[k], pchr[l]);
+ init_tb(str);
+ }
+
+ for (i = 1; i < 6; i++)
+ for (j = i; j < 6; j++)
+ for (k = j; k < 6; k++)
+ for (l = 1; l < 6; l++) {
+ sprintf(str, "K%c%c%cvK%c", pchr[i], pchr[j], pchr[k], pchr[l]);
+ init_tb(str);
+ }
+
+ for (i = 1; i < 6; i++)
+ for (j = i; j < 6; j++)
+ for (k = j; k < 6; k++)
+ for (l = k; l < 6; l++) {
+ sprintf(str, "K%c%c%c%cvK", pchr[i], pchr[j], pchr[k], pchr[l]);
+ init_tb(str);
+ }
+ }
+
+ std::cout << "info string Found " << (TBnum_piece + TBnum_pawn) << " syzygy tablebases" << std::endl;
+}
+
+static const signed char offdiag[] = {
+ 0,-1,-1,-1,-1,-1,-1,-1,
+ 1, 0,-1,-1,-1,-1,-1,-1,
+ 1, 1, 0,-1,-1,-1,-1,-1,
+ 1, 1, 1, 0,-1,-1,-1,-1,
+ 1, 1, 1, 1, 0,-1,-1,-1,
+ 1, 1, 1, 1, 1, 0,-1,-1,
+ 1, 1, 1, 1, 1, 1, 0,-1,
+ 1, 1, 1, 1, 1, 1, 1, 0
+};
+
+static const ubyte triangle[] = {
+ 6, 0, 1, 2, 2, 1, 0, 6,
+ 0, 7, 3, 4, 4, 3, 7, 0,
+ 1, 3, 8, 5, 5, 8, 3, 1,
+ 2, 4, 5, 9, 9, 5, 4, 2,
+ 2, 4, 5, 9, 9, 5, 4, 2,
+ 1, 3, 8, 5, 5, 8, 3, 1,
+ 0, 7, 3, 4, 4, 3, 7, 0,
+ 6, 0, 1, 2, 2, 1, 0, 6
+};
+
+static const ubyte invtriangle[] = {
+ 1, 2, 3, 10, 11, 19, 0, 9, 18, 27
+};
+
+static const ubyte invdiag[] = {
+ 0, 9, 18, 27, 36, 45, 54, 63,
+ 7, 14, 21, 28, 35, 42, 49, 56
+};
+
+static const ubyte flipdiag[] = {
+ 0, 8, 16, 24, 32, 40, 48, 56,
+ 1, 9, 17, 25, 33, 41, 49, 57,
+ 2, 10, 18, 26, 34, 42, 50, 58,
+ 3, 11, 19, 27, 35, 43, 51, 59,
+ 4, 12, 20, 28, 36, 44, 52, 60,
+ 5, 13, 21, 29, 37, 45, 53, 61,
+ 6, 14, 22, 30, 38, 46, 54, 62,
+ 7, 15, 23, 31, 39, 47, 55, 63
+};
+
+static const ubyte lower[] = {
+ 28, 0, 1, 2, 3, 4, 5, 6,
+ 0, 29, 7, 8, 9, 10, 11, 12,
+ 1, 7, 30, 13, 14, 15, 16, 17,
+ 2, 8, 13, 31, 18, 19, 20, 21,
+ 3, 9, 14, 18, 32, 22, 23, 24,
+ 4, 10, 15, 19, 22, 33, 25, 26,
+ 5, 11, 16, 20, 23, 25, 34, 27,
+ 6, 12, 17, 21, 24, 26, 27, 35
+};
+
+static const ubyte diag[] = {
+ 0, 0, 0, 0, 0, 0, 0, 8,
+ 0, 1, 0, 0, 0, 0, 9, 0,
+ 0, 0, 2, 0, 0, 10, 0, 0,
+ 0, 0, 0, 3, 11, 0, 0, 0,
+ 0, 0, 0, 12, 4, 0, 0, 0,
+ 0, 0, 13, 0, 0, 5, 0, 0,
+ 0, 14, 0, 0, 0, 0, 6, 0,
+ 15, 0, 0, 0, 0, 0, 0, 7
+};
+
+static const ubyte flap[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 18, 18, 12, 6, 0,
+ 1, 7, 13, 19, 19, 13, 7, 1,
+ 2, 8, 14, 20, 20, 14, 8, 2,
+ 3, 9, 15, 21, 21, 15, 9, 3,
+ 4, 10, 16, 22, 22, 16, 10, 4,
+ 5, 11, 17, 23, 23, 17, 11, 5,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const ubyte ptwist[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 47, 35, 23, 11, 10, 22, 34, 46,
+ 45, 33, 21, 9, 8, 20, 32, 44,
+ 43, 31, 19, 7, 6, 18, 30, 42,
+ 41, 29, 17, 5, 4, 16, 28, 40,
+ 39, 27, 15, 3, 2, 14, 26, 38,
+ 37, 25, 13, 1, 0, 12, 24, 36,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const ubyte invflap[] = {
+ 8, 16, 24, 32, 40, 48,
+ 9, 17, 25, 33, 41, 49,
+ 10, 18, 26, 34, 42, 50,
+ 11, 19, 27, 35, 43, 51
+};
+
+static const ubyte invptwist[] = {
+ 52, 51, 44, 43, 36, 35, 28, 27, 20, 19, 12, 11,
+ 53, 50, 45, 42, 37, 34, 29, 26, 21, 18, 13, 10,
+ 54, 49, 46, 41, 38, 33, 30, 25, 22, 17, 14, 9,
+ 55, 48, 47, 40, 39, 32, 31, 24, 23, 16, 15, 8
+};
+
+static const ubyte file_to_file[] = {
+ 0, 1, 2, 3, 3, 2, 1, 0
+};
+
+#ifndef CONNECTED_KINGS
+static const short KK_idx[10][64] = {
+ { -1, -1, -1, 0, 1, 2, 3, 4,
+ -1, -1, -1, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57 },
+ { 58, -1, -1, -1, 59, 60, 61, 62,
+ 63, -1, -1, -1, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 77, 78, 79, 80, 81, 82, 83,
+ 84, 85, 86, 87, 88, 89, 90, 91,
+ 92, 93, 94, 95, 96, 97, 98, 99,
+ 100,101,102,103,104,105,106,107,
+ 108,109,110,111,112,113,114,115},
+ {116,117, -1, -1, -1,118,119,120,
+ 121,122, -1, -1, -1,123,124,125,
+ 126,127,128,129,130,131,132,133,
+ 134,135,136,137,138,139,140,141,
+ 142,143,144,145,146,147,148,149,
+ 150,151,152,153,154,155,156,157,
+ 158,159,160,161,162,163,164,165,
+ 166,167,168,169,170,171,172,173 },
+ {174, -1, -1, -1,175,176,177,178,
+ 179, -1, -1, -1,180,181,182,183,
+ 184, -1, -1, -1,185,186,187,188,
+ 189,190,191,192,193,194,195,196,
+ 197,198,199,200,201,202,203,204,
+ 205,206,207,208,209,210,211,212,
+ 213,214,215,216,217,218,219,220,
+ 221,222,223,224,225,226,227,228 },
+ {229,230, -1, -1, -1,231,232,233,
+ 234,235, -1, -1, -1,236,237,238,
+ 239,240, -1, -1, -1,241,242,243,
+ 244,245,246,247,248,249,250,251,
+ 252,253,254,255,256,257,258,259,
+ 260,261,262,263,264,265,266,267,
+ 268,269,270,271,272,273,274,275,
+ 276,277,278,279,280,281,282,283 },
+ {284,285,286,287,288,289,290,291,
+ 292,293, -1, -1, -1,294,295,296,
+ 297,298, -1, -1, -1,299,300,301,
+ 302,303, -1, -1, -1,304,305,306,
+ 307,308,309,310,311,312,313,314,
+ 315,316,317,318,319,320,321,322,
+ 323,324,325,326,327,328,329,330,
+ 331,332,333,334,335,336,337,338 },
+ { -1, -1,339,340,341,342,343,344,
+ -1, -1,345,346,347,348,349,350,
+ -1, -1,441,351,352,353,354,355,
+ -1, -1, -1,442,356,357,358,359,
+ -1, -1, -1, -1,443,360,361,362,
+ -1, -1, -1, -1, -1,444,363,364,
+ -1, -1, -1, -1, -1, -1,445,365,
+ -1, -1, -1, -1, -1, -1, -1,446 },
+ { -1, -1, -1,366,367,368,369,370,
+ -1, -1, -1,371,372,373,374,375,
+ -1, -1, -1,376,377,378,379,380,
+ -1, -1, -1,447,381,382,383,384,
+ -1, -1, -1, -1,448,385,386,387,
+ -1, -1, -1, -1, -1,449,388,389,
+ -1, -1, -1, -1, -1, -1,450,390,
+ -1, -1, -1, -1, -1, -1, -1,451 },
+ {452,391,392,393,394,395,396,397,
+ -1, -1, -1, -1,398,399,400,401,
+ -1, -1, -1, -1,402,403,404,405,
+ -1, -1, -1, -1,406,407,408,409,
+ -1, -1, -1, -1,453,410,411,412,
+ -1, -1, -1, -1, -1,454,413,414,
+ -1, -1, -1, -1, -1, -1,455,415,
+ -1, -1, -1, -1, -1, -1, -1,456 },
+ {457,416,417,418,419,420,421,422,
+ -1,458,423,424,425,426,427,428,
+ -1, -1, -1, -1, -1,429,430,431,
+ -1, -1, -1, -1, -1,432,433,434,
+ -1, -1, -1, -1, -1,435,436,437,
+ -1, -1, -1, -1, -1,459,438,439,
+ -1, -1, -1, -1, -1, -1,460,440,
+ -1, -1, -1, -1, -1, -1, -1,461 }
+};
+#else
+static const short PP_idx[10][64] = {
+ { 0, -1, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46,
+ -1, 47, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61 },
+ { 62, -1, -1, 63, 64, 65, -1, 66,
+ -1, 67, 68, 69, 70, 71, 72, -1,
+ 73, 74, 75, 76, 77, 78, 79, 80,
+ 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96,
+ -1, 97, 98, 99,100,101,102,103,
+ -1,104,105,106,107,108,109, -1,
+ 110, -1,111,112,113,114, -1,115 },
+ {116, -1, -1, -1,117, -1, -1,118,
+ -1,119,120,121,122,123,124, -1,
+ -1,125,126,127,128,129,130, -1,
+ 131,132,133,134,135,136,137,138,
+ -1,139,140,141,142,143,144,145,
+ -1,146,147,148,149,150,151, -1,
+ -1,152,153,154,155,156,157, -1,
+ 158, -1, -1,159,160, -1, -1,161 },
+ {162, -1, -1, -1, -1, -1, -1,163,
+ -1,164, -1,165,166,167,168, -1,
+ -1,169,170,171,172,173,174, -1,
+ -1,175,176,177,178,179,180, -1,
+ -1,181,182,183,184,185,186, -1,
+ -1, -1,187,188,189,190,191, -1,
+ -1,192,193,194,195,196,197, -1,
+ 198, -1, -1, -1, -1, -1, -1,199 },
+ {200, -1, -1, -1, -1, -1, -1,201,
+ -1,202, -1, -1,203, -1,204, -1,
+ -1, -1,205,206,207,208, -1, -1,
+ -1,209,210,211,212,213,214, -1,
+ -1, -1,215,216,217,218,219, -1,
+ -1, -1,220,221,222,223, -1, -1,
+ -1,224, -1,225,226, -1,227, -1,
+ 228, -1, -1, -1, -1, -1, -1,229 },
+ {230, -1, -1, -1, -1, -1, -1,231,
+ -1,232, -1, -1, -1, -1,233, -1,
+ -1, -1,234, -1,235,236, -1, -1,
+ -1, -1,237,238,239,240, -1, -1,
+ -1, -1, -1,241,242,243, -1, -1,
+ -1, -1,244,245,246,247, -1, -1,
+ -1,248, -1, -1, -1, -1,249, -1,
+ 250, -1, -1, -1, -1, -1, -1,251 },
+ { -1, -1, -1, -1, -1, -1, -1,259,
+ -1,252, -1, -1, -1, -1,260, -1,
+ -1, -1,253, -1, -1,261, -1, -1,
+ -1, -1, -1,254,262, -1, -1, -1,
+ -1, -1, -1, -1,255, -1, -1, -1,
+ -1, -1, -1, -1, -1,256, -1, -1,
+ -1, -1, -1, -1, -1, -1,257, -1,
+ -1, -1, -1, -1, -1, -1, -1,258 },
+ { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,268, -1,
+ -1, -1,263, -1, -1,269, -1, -1,
+ -1, -1, -1,264,270, -1, -1, -1,
+ -1, -1, -1, -1,265, -1, -1, -1,
+ -1, -1, -1, -1, -1,266, -1, -1,
+ -1, -1, -1, -1, -1, -1,267, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+ { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1,274, -1, -1,
+ -1, -1, -1,271,275, -1, -1, -1,
+ -1, -1, -1, -1,272, -1, -1, -1,
+ -1, -1, -1, -1, -1,273, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+ { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1,277, -1, -1, -1,
+ -1, -1, -1, -1,276, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 }
+};
+
+static const ubyte test45[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 0, 0, 0, 0, 0,
+ 1, 1, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const ubyte mtwist[] = {
+ 15, 63, 55, 47, 40, 48, 56, 12,
+ 62, 11, 39, 31, 24, 32, 8, 57,
+ 54, 38, 7, 23, 16, 4, 33, 49,
+ 46, 30, 22, 3, 0, 17, 25, 41,
+ 45, 29, 21, 2, 1, 18, 26, 42,
+ 53, 37, 6, 20, 19, 5, 34, 50,
+ 61, 10, 36, 28, 27, 35, 9, 58,
+ 14, 60, 52, 44, 43, 51, 59, 13
+};
+#endif
+
+static int binomial[5][64];
+static int pawnidx[5][24];
+static int pfactor[5][4];
+#ifdef CONNECTED_KINGS
+static int multidx[5][10];
+static int mfactor[5];
+#endif
+
+static void init_indices(void)
+{
+ int i, j, k;
+
+ // binomial[k-1][n] = Bin(n, k)
+ for (i = 0; i < 5; i++)
+ for (j = 0; j < 64; j++) {
+ int f = j;
+ int l = 1;
+ for (k = 1; k <= i; k++) {
+ f *= (j - k);
+ l *= (k + 1);
+ }
+ binomial[i][j] = f / l;
+ }
+
+ for (i = 0; i < 5; i++) {
+ int s = 0;
+ for (j = 0; j < 6; j++) {
+ pawnidx[i][j] = s;
+ s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]];
+ }
+ pfactor[i][0] = s;
+ s = 0;
+ for (; j < 12; j++) {
+ pawnidx[i][j] = s;
+ s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]];
+ }
+ pfactor[i][1] = s;
+ s = 0;
+ for (; j < 18; j++) {
+ pawnidx[i][j] = s;
+ s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]];
+ }
+ pfactor[i][2] = s;
+ s = 0;
+ for (; j < 24; j++) {
+ pawnidx[i][j] = s;
+ s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]];
+ }
+ pfactor[i][3] = s;
+ }
+
+#ifdef CONNECTED_KINGS
+ for (i = 0; i < 5; i++) {
+ int s = 0;
+ for (j = 0; j < 10; j++) {
+ multidx[i][j] = s;
+ s += (i == 0) ? 1 : binomial[i - 1][mtwist[invtriangle[j]]];
+ }
+ mfactor[i] = s;
+ }
+#endif
+}
+
+#ifndef CONNECTED_KINGS
+static uint64_t encode_piece(struct TBEntry_piece *ptr, ubyte *norm, int *pos, int *factor)
+{
+ uint64_t idx = 0;
+ int i, j, k, m, l, p;
+ int n = ptr->num;
+
+ if (pos[0] & 0x04) {
+ for (i = 0; i < n; i++)
+ pos[i] ^= 0x07;
+ }
+ if (pos[0] & 0x20) {
+ for (i = 0; i < n; i++)
+ pos[i] ^= 0x38;
+ }
+
+ for (i = 0; i < n; i++)
+ if (offdiag[pos[i]]) break;
+ if (i < (ptr->enc_type == 0 ? 3 : 2) && offdiag[pos[i]] > 0)
+ for (i = 0; i < n; i++)
+ pos[i] = flipdiag[pos[i]];
+
+ switch (ptr->enc_type) {
+
+ case 0: /* 111 */
+ i = (pos[1] > pos[0]);
+ j = (pos[2] > pos[0]) + (pos[2] > pos[1]);
+
+ if (offdiag[pos[0]])
+ idx = triangle[pos[0]] * 63*62 + (pos[1] - i) * 62 + (pos[2] - j);
+ else if (offdiag[pos[1]])
+ idx = 6*63*62 + diag[pos[0]] * 28*62 + lower[pos[1]] * 62 + pos[2] - j;
+ else if (offdiag[pos[2]])
+ idx = 6*63*62 + 4*28*62 + (diag[pos[0]]) * 7*28 + (diag[pos[1]] - i) * 28 + lower[pos[2]];
+ else
+ idx = 6*63*62 + 4*28*62 + 4*7*28 + (diag[pos[0]] * 7*6) + (diag[pos[1]] - i) * 6 + (diag[pos[2]] - j);
+ i = 3;
+ break;
+
+ case 1: /* K3 */
+ j = (pos[2] > pos[0]) + (pos[2] > pos[1]);
+
+ idx = KK_idx[triangle[pos[0]]][pos[1]];
+ if (idx < 441)
+ idx = idx + 441 * (pos[2] - j);
+ else {
+ idx = 441*62 + (idx - 441) + 21 * lower[pos[2]];
+ if (!offdiag[pos[2]])
+ idx -= j * 21;
+ }
+ i = 3;
+ break;
+
+ default: /* K2 */
+ idx = KK_idx[triangle[pos[0]]][pos[1]];
+ i = 2;
+ break;
+ }
+ idx *= factor[0];
+
+ for (; i < n;) {
+ int t = norm[i];
+ for (j = i; j < i + t; j++)
+ for (k = j + 1; k < i + t; k++)
+ if (pos[j] > pos[k]) Swap(pos[j], pos[k]);
+ int s = 0;
+ for (m = i; m < i + t; m++) {
+ p = pos[m];
+ for (l = 0, j = 0; l < i; l++)
+ j += (p > pos[l]);
+ s += binomial[m - i][p - j];
+ }
+ idx += ((uint64_t)s) * ((uint64_t)factor[i]);
+ i += t;
+ }
+
+ return idx;
+}
+#else
+static uint64_t encode_piece(struct TBEntry_piece *ptr, ubyte *norm, int *pos, int *factor)
+{
+ uint64_t idx;
+ int i, j, k, m, l, p;
+ int n = ptr->num;
+
+ if (ptr->enc_type < 3) {
+ if (pos[0] & 0x04) {
+ for (i = 0; i < n; i++)
+ pos[i] ^= 0x07;
+ }
+ if (pos[0] & 0x20) {
+ for (i = 0; i < n; i++)
+ pos[i] ^= 0x38;
+ }
+
+ for (i = 0; i < n; i++)
+ if (offdiag[pos[i]]) break;
+ if (i < (ptr->enc_type == 0 ? 3 : 2) && offdiag[pos[i]] > 0)
+ for (i = 0; i < n; i++)
+ pos[i] = flipdiag[pos[i]];
+
+ switch (ptr->enc_type) {
+
+ case 0: /* 111 */
+ i = (pos[1] > pos[0]);
+ j = (pos[2] > pos[0]) + (pos[2] > pos[1]);
+
+ if (offdiag[pos[0]])
+ idx = triangle[pos[0]] * 63*62 + (pos[1] - i) * 62 + (pos[2] - j);
+ else if (offdiag[pos[1]])
+ idx = 6*63*62 + diag[pos[0]] * 28*62 + lower[pos[1]] * 62 + pos[2] - j;
+ else if (offdiag[pos[2]])
+ idx = 6*63*62 + 4*28*62 + (diag[pos[0]]) * 7*28 + (diag[pos[1]] - i) * 28 + lower[pos[2]];
+ else
+ idx = 6*63*62 + 4*28*62 + 4*7*28 + (diag[pos[0]] * 7*6) + (diag[pos[1]] - i) * 6 + (diag[pos[2]] - j);
+ i = 3;
+ break;
+
+ case 2: /* 11 */
+ i = (pos[1] > pos[0]);
+
+ if (offdiag[pos[0]])
+ idx = triangle[pos[0]] * 63 + (pos[1] - i);
+ else if (offdiag[pos[1]])
+ idx = 6*63 + diag[pos[0]] * 28 + lower[pos[1]];
+ else
+ idx = 6*63 + 4*28 + (diag[pos[0]]) * 7 + (diag[pos[1]] - i);
+ i = 2;
+ break;
+
+ }
+ } else if (ptr->enc_type == 3) { /* 2, e.g. KKvK */
+ if (triangle[pos[0]] > triangle[pos[1]])
+ Swap(pos[0], pos[1]);
+ if (pos[0] & 0x04)
+ for (i = 0; i < n; i++)
+ pos[i] ^= 0x07;
+ if (pos[0] & 0x20)
+ for (i = 0; i < n; i++)
+ pos[i] ^= 0x38;
+ if (offdiag[pos[0]] > 0 || (offdiag[pos[0]] == 0 && offdiag[pos[1]] > 0))
+ for (i = 0; i < n; i++)
+ pos[i] = flipdiag[pos[i]];
+ if (test45[pos[1]] && triangle[pos[0]] == triangle[pos[1]]) {
+ Swap(pos[0], pos[1]);
+ for (i = 0; i < n; i++)
+ pos[i] = flipdiag[pos[i] ^ 0x38];
+ }
+ idx = PP_idx[triangle[pos[0]]][pos[1]];
+ i = 2;
+ } else { /* 3 and higher, e.g. KKKvK and KKKKvK */
+ for (i = 1; i < norm[0]; i++)
+ if (triangle[pos[0]] > triangle[pos[i]])
+ Swap(pos[0], pos[i]);
+ if (pos[0] & 0x04)
+ for (i = 0; i < n; i++)
+ pos[i] ^= 0x07;
+ if (pos[0] & 0x20)
+ for (i = 0; i < n; i++)
+ pos[i] ^= 0x38;
+ if (offdiag[pos[0]] > 0)
+ for (i = 0; i < n; i++)
+ pos[i] = flipdiag[pos[i]];
+ for (i = 1; i < norm[0]; i++)
+ for (j = i + 1; j < norm[0]; j++)
+ if (mtwist[pos[i]] > mtwist[pos[j]])
+ Swap(pos[i], pos[j]);
+
+ idx = multidx[norm[0] - 1][triangle[pos[0]]];
+ for (i = 1; i < norm[0]; i++)
+ idx += binomial[i - 1][mtwist[pos[i]]];
+ }
+ idx *= factor[0];
+
+ for (; i < n;) {
+ int t = norm[i];
+ for (j = i; j < i + t; j++)
+ for (k = j + 1; k < i + t; k++)
+ if (pos[j] > pos[k]) Swap(pos[j], pos[k]);
+ int s = 0;
+ for (m = i; m < i + t; m++) {
+ p = pos[m];
+ for (l = 0, j = 0; l < i; l++)
+ j += (p > pos[l]);
+ s += binomial[m - i][p - j];
+ }
+ idx += ((uint64_t)s) * ((uint64_t)factor[i]);
+ i += t;
+ }
+
+ return idx;
+}
+#endif
+
+// determine file of leftmost pawn and sort pawns
+static int pawn_file(struct TBEntry_pawn *ptr, int *pos)
+{
+ int i;
+
+ for (i = 1; i < ptr->pawns[0]; i++)
+ if (flap[pos[0]] > flap[pos[i]])
+ Swap(pos[0], pos[i]);
+
+ return file_to_file[pos[0] & 0x07];
+}
+
+static uint64_t encode_pawn(struct TBEntry_pawn *ptr, ubyte *norm, int *pos, int *factor)
+{
+ uint64_t idx;
+ int i, j, k, m, s, t;
+ int n = ptr->num;
+
+ if (pos[0] & 0x04)
+ for (i = 0; i < n; i++)
+ pos[i] ^= 0x07;
+
+ for (i = 1; i < ptr->pawns[0]; i++)
+ for (j = i + 1; j < ptr->pawns[0]; j++)
+ if (ptwist[pos[i]] < ptwist[pos[j]])
+ Swap(pos[i], pos[j]);
+
+ t = ptr->pawns[0] - 1;
+ idx = pawnidx[t][flap[pos[0]]];
+ for (i = t; i > 0; i--)
+ idx += binomial[t - i][ptwist[pos[i]]];
+ idx *= factor[0];
+
+ // remaining pawns
+ i = ptr->pawns[0];
+ t = i + ptr->pawns[1];
+ if (t > i) {
+ for (j = i; j < t; j++)
+ for (k = j + 1; k < t; k++)
+ if (pos[j] > pos[k]) Swap(pos[j], pos[k]);
+ s = 0;
+ for (m = i; m < t; m++) {
+ int p = pos[m];
+ for (k = 0, j = 0; k < i; k++)
+ j += (p > pos[k]);
+ s += binomial[m - i][p - j - 8];
+ }
+ idx += ((uint64_t)s) * ((uint64_t)factor[i]);
+ i = t;
+ }
+
+ for (; i < n;) {
+ t = norm[i];
+ for (j = i; j < i + t; j++)
+ for (k = j + 1; k < i + t; k++)
+ if (pos[j] > pos[k]) Swap(pos[j], pos[k]);
+ s = 0;
+ for (m = i; m < i + t; m++) {
+ int p = pos[m];
+ for (k = 0, j = 0; k < i; k++)
+ j += (p > pos[k]);
+ s += binomial[m - i][p - j];
+ }
+ idx += ((uint64_t)s) * ((uint64_t)factor[i]);
+ i += t;
+ }
+
+ return idx;
+}
+
+static ubyte decompress_pairs(struct PairsData *d, uint64_t index);
+
+// place k like pieces on n squares
+static int subfactor(int k, int n)
+{
+ int i, f, l;
+
+ f = n;
+ l = 1;
+ for (i = 1; i < k; i++) {
+ f *= n - i;
+ l *= i + 1;
+ }
+
+ return f / l;
+}
+
+static uint64_t calc_factors_piece(int *factor, int num, int order, ubyte *norm, ubyte enc_type)
+{
+ int i, k, n;
+ uint64_t f;
+#ifndef CONNECTED_KINGS
+ static int pivfac[] = { 31332, 28056, 462 };
+#else
+ static int pivfac[] = { 31332, 0, 518, 278 };
+#endif
+
+ n = 64 - norm[0];
+
+ f = 1;
+ for (i = norm[0], k = 0; i < num || k == order; k++) {
+ if (k == order) {
+ factor[0] = f;
+#ifndef CONNECTED_KINGS
+ f *= pivfac[enc_type];
+#else
+ if (enc_type < 4)
+ f *= pivfac[enc_type];
+ else
+ f *= mfactor[enc_type - 2];
+#endif
+ } else {
+ factor[i] = f;
+ f *= subfactor(norm[i], n);
+ n -= norm[i];
+ i += norm[i];
+ }
+ }
+
+ return f;
+}
+
+static uint64_t calc_factors_pawn(int *factor, int num, int order, int order2, ubyte *norm, int file)
+{
+ int i, k, n;
+ uint64_t f;
+
+ i = norm[0];
+ if (order2 < 0x0f) i += norm[i];
+ n = 64 - i;
+
+ f = 1;
+ for (k = 0; i < num || k == order || k == order2; k++) {
+ if (k == order) {
+ factor[0] = f;
+ f *= pfactor[norm[0] - 1][file];
+ } else if (k == order2) {
+ factor[norm[0]] = f;
+ f *= subfactor(norm[norm[0]], 48 - norm[0]);
+ } else {
+ factor[i] = f;
+ f *= subfactor(norm[i], n);
+ n -= norm[i];
+ i += norm[i];
+ }
+ }
+
+ return f;
+}
+
+static void set_norm_piece(struct TBEntry_piece *ptr, ubyte *norm, ubyte *pieces)
+{
+ int i, j;
+
+ for (i = 0; i < ptr->num; i++)
+ norm[i] = 0;
+
+ switch (ptr->enc_type) {
+ case 0:
+ norm[0] = 3;
+ break;
+ case 2:
+ norm[0] = 2;
+ break;
+ default:
+ norm[0] = ptr->enc_type - 1;
+ break;
+ }
+
+ for (i = norm[0]; i < ptr->num; i += norm[i])
+ for (j = i; j < ptr->num && pieces[j] == pieces[i]; j++)
+ norm[i]++;
+}
+
+static void set_norm_pawn(struct TBEntry_pawn *ptr, ubyte *norm, ubyte *pieces)
+{
+ int i, j;
+
+ for (i = 0; i < ptr->num; i++)
+ norm[i] = 0;
+
+ norm[0] = ptr->pawns[0];
+ if (ptr->pawns[1]) norm[ptr->pawns[0]] = ptr->pawns[1];
+
+ for (i = ptr->pawns[0] + ptr->pawns[1]; i < ptr->num; i += norm[i])
+ for (j = i; j < ptr->num && pieces[j] == pieces[i]; j++)
+ norm[i]++;
+}
+
+static void setup_pieces_piece(struct TBEntry_piece *ptr, unsigned char *data, uint64_t *tb_size)
+{
+ int i;
+ int order;
+
+ for (i = 0; i < ptr->num; i++)
+ ptr->pieces[0][i] = data[i + 1] & 0x0f;
+ order = data[0] & 0x0f;
+ set_norm_piece(ptr, ptr->norm[0], ptr->pieces[0]);
+ tb_size[0] = calc_factors_piece(ptr->factor[0], ptr->num, order, ptr->norm[0], ptr->enc_type);
+
+ for (i = 0; i < ptr->num; i++)
+ ptr->pieces[1][i] = data[i + 1] >> 4;
+ order = data[0] >> 4;
+ set_norm_piece(ptr, ptr->norm[1], ptr->pieces[1]);
+ tb_size[1] = calc_factors_piece(ptr->factor[1], ptr->num, order, ptr->norm[1], ptr->enc_type);
+}
+
+static void setup_pieces_piece_dtz(struct DTZEntry_piece *ptr, unsigned char *data, uint64_t *tb_size)
+{
+ int i;
+ int order;
+
+ for (i = 0; i < ptr->num; i++)
+ ptr->pieces[i] = data[i + 1] & 0x0f;
+ order = data[0] & 0x0f;
+ set_norm_piece((struct TBEntry_piece *)ptr, ptr->norm, ptr->pieces);
+ tb_size[0] = calc_factors_piece(ptr->factor, ptr->num, order, ptr->norm, ptr->enc_type);
+}
+
+static void setup_pieces_pawn(struct TBEntry_pawn *ptr, unsigned char *data, uint64_t *tb_size, int f)
+{
+ int i, j;
+ int order, order2;
+
+ j = 1 + (ptr->pawns[1] > 0);
+ order = data[0] & 0x0f;
+ order2 = ptr->pawns[1] ? (data[1] & 0x0f) : 0x0f;
+ for (i = 0; i < ptr->num; i++)
+ ptr->file[f].pieces[0][i] = data[i + j] & 0x0f;
+ set_norm_pawn(ptr, ptr->file[f].norm[0], ptr->file[f].pieces[0]);
+ tb_size[0] = calc_factors_pawn(ptr->file[f].factor[0], ptr->num, order, order2, ptr->file[f].norm[0], f);
+
+ order = data[0] >> 4;
+ order2 = ptr->pawns[1] ? (data[1] >> 4) : 0x0f;
+ for (i = 0; i < ptr->num; i++)
+ ptr->file[f].pieces[1][i] = data[i + j] >> 4;
+ set_norm_pawn(ptr, ptr->file[f].norm[1], ptr->file[f].pieces[1]);
+ tb_size[1] = calc_factors_pawn(ptr->file[f].factor[1], ptr->num, order, order2, ptr->file[f].norm[1], f);
+}
+
+static void setup_pieces_pawn_dtz(struct DTZEntry_pawn *ptr, unsigned char *data, uint64_t *tb_size, int f)
+{
+ int i, j;
+ int order, order2;
+
+ j = 1 + (ptr->pawns[1] > 0);
+ order = data[0] & 0x0f;
+ order2 = ptr->pawns[1] ? (data[1] & 0x0f) : 0x0f;
+ for (i = 0; i < ptr->num; i++)
+ ptr->file[f].pieces[i] = data[i + j] & 0x0f;
+ set_norm_pawn((struct TBEntry_pawn *)ptr, ptr->file[f].norm, ptr->file[f].pieces);
+ tb_size[0] = calc_factors_pawn(ptr->file[f].factor, ptr->num, order, order2, ptr->file[f].norm, f);
+}
+
+static void calc_symlen(struct PairsData *d, int s, char *tmp)
+{
+ int s1, s2;
+
+ int w = *(int *)(d->sympat + 3 * s);
+ s2 = (w >> 12) & 0x0fff;
+ if (s2 == 0x0fff)
+ d->symlen[s] = 0;
+ else {
+ s1 = w & 0x0fff;
+ if (!tmp[s1]) calc_symlen(d, s1, tmp);
+ if (!tmp[s2]) calc_symlen(d, s2, tmp);
+ d->symlen[s] = d->symlen[s1] + d->symlen[s2] + 1;
+ }
+ tmp[s] = 1;
+}
+
+static struct PairsData *setup_pairs(unsigned char *data, uint64_t tb_size, uint64_t *size, unsigned char **next, ubyte *flags, int wdl)
+{
+ struct PairsData *d;
+ int i;
+
+ *flags = data[0];
+ if (data[0] & 0x80) {
+ d = (struct PairsData *)malloc(sizeof(struct PairsData));
+ d->idxbits = 0;
+ if (wdl)
+ d->min_len = data[1];
+ else
+ d->min_len = 0;
+ *next = data + 2;
+ size[0] = size[1] = size[2] = 0;
+ return d;
+ }
+
+ int blocksize = data[1];
+ int idxbits = data[2];
+ int real_num_blocks = *(uint32_t *)(&data[4]);
+ int num_blocks = real_num_blocks + *(ubyte *)(&data[3]);
+ int max_len = data[8];
+ int min_len = data[9];
+ int h = max_len - min_len + 1;
+ int num_syms = *(ushort *)(&data[10 + 2 * h]);
+ d = (struct PairsData *)malloc(sizeof(struct PairsData) + (h - 1) * sizeof(base_t) + num_syms);
+ d->blocksize = blocksize;
+ d->idxbits = idxbits;
+ d->offset = (ushort *)(&data[10]);
+ d->symlen = ((ubyte *)d) + sizeof(struct PairsData) + (h - 1) * sizeof(base_t);
+ d->sympat = &data[12 + 2 * h];
+ d->min_len = min_len;
+ *next = &data[12 + 2 * h + 3 * num_syms + (num_syms & 1)];
+
+ int num_indices = (tb_size + (1ULL << idxbits) - 1) >> idxbits;
+ size[0] = 6ULL * num_indices;
+ size[1] = 2ULL * num_blocks;
+ size[2] = (1ULL << blocksize) * real_num_blocks;
+
+ // char tmp[num_syms];
+ char tmp[4096];
+ for (i = 0; i < num_syms; i++)
+ tmp[i] = 0;
+ for (i = 0; i < num_syms; i++)
+ if (!tmp[i])
+ calc_symlen(d, i, tmp);
+
+ d->base[h - 1] = 0;
+ for (i = h - 2; i >= 0; i--)
+ d->base[i] = (d->base[i + 1] + d->offset[i] - d->offset[i + 1]) / 2;
+ for (i = 0; i < h; i++)
+ d->base[i] <<= 64 - (min_len + i);
+
+ d->offset -= d->min_len;
+
+ return d;
+}
+
+static int init_table_wdl(struct TBEntry *entry, const char *str)
+{
+ ubyte *next;
+ int f, s;
+ uint64_t tb_size[8];
+ uint64_t size[8 * 3];
+ ubyte flags;
+
+ // first mmap the table into memory
+
+ entry->data = map_file(str, WDLSUFFIX, &entry->mapping);
+ if (!entry->data) {
+ std::cout << "Could not find " << str << WDLSUFFIX << std::endl;
+ return 0;
+ }
+
+ ubyte *data = (ubyte *)entry->data;
+ if (((uint32_t *)data)[0] != WDL_MAGIC) {
+ std::cout << "Corrupted table" << std::endl;
+ unmap_file(entry->data, entry->mapping);
+ entry->data = 0;
+ return 0;
+ }
+
+ int split = data[4] & 0x01;
+ int files = data[4] & 0x02 ? 4 : 1;
+
+ data += 5;
+
+ if (!entry->has_pawns) {
+ struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry;
+ setup_pieces_piece(ptr, data, &tb_size[0]);
+ data += ptr->num + 1;
+ data += ((uintptr_t)data) & 0x01;
+
+ ptr->precomp[0] = setup_pairs(data, tb_size[0], &size[0], &next, &flags, 1);
+ data = next;
+ if (split) {
+ ptr->precomp[1] = setup_pairs(data, tb_size[1], &size[3], &next, &flags, 1);
+ data = next;
+ } else
+ ptr->precomp[1] = NULL;
+
+ ptr->precomp[0]->indextable = (char *)data;
+ data += size[0];
+ if (split) {
+ ptr->precomp[1]->indextable = (char *)data;
+ data += size[3];
+ }
+
+ ptr->precomp[0]->sizetable = (ushort *)data;
+ data += size[1];
+ if (split) {
+ ptr->precomp[1]->sizetable = (ushort *)data;
+ data += size[4];
+ }
+
+ data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f);
+ ptr->precomp[0]->data = data;
+ data += size[2];
+ if (split) {
+ data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f);
+ ptr->precomp[1]->data = data;
+ }
+ } else {
+ struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry;
+ s = 1 + (ptr->pawns[1] > 0);
+ for (f = 0; f < 4; f++) {
+ setup_pieces_pawn((struct TBEntry_pawn *)ptr, data, &tb_size[2 * f], f);
+ data += ptr->num + s;
+ }
+ data += ((uintptr_t)data) & 0x01;
+
+ for (f = 0; f < files; f++) {
+ ptr->file[f].precomp[0] = setup_pairs(data, tb_size[2 * f], &size[6 * f], &next, &flags, 1);
+ data = next;
+ if (split) {
+ ptr->file[f].precomp[1] = setup_pairs(data, tb_size[2 * f + 1], &size[6 * f + 3], &next, &flags, 1);
+ data = next;
+ } else
+ ptr->file[f].precomp[1] = NULL;
+ }
+
+ for (f = 0; f < files; f++) {
+ ptr->file[f].precomp[0]->indextable = (char *)data;
+ data += size[6 * f];
+ if (split) {
+ ptr->file[f].precomp[1]->indextable = (char *)data;
+ data += size[6 * f + 3];
+ }
+ }
+
+ for (f = 0; f < files; f++) {
+ ptr->file[f].precomp[0]->sizetable = (ushort *)data;
+ data += size[6 * f + 1];
+ if (split) {
+ ptr->file[f].precomp[1]->sizetable = (ushort *)data;
+ data += size[6 * f + 4];
+ }
+ }
+
+ for (f = 0; f < files; f++) {
+ data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f);
+ ptr->file[f].precomp[0]->data = data;
+ data += size[6 * f + 2];
+ if (split) {
+ data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f);
+ ptr->file[f].precomp[1]->data = data;
+ data += size[6 * f + 5];
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int init_table_dtz(struct TBEntry *entry)
+{
+ ubyte *data = (ubyte *)entry->data;
+ ubyte *next;
+ int f, s;
+ uint64_t tb_size[4];
+ uint64_t size[4 * 3];
+
+ if (!data)
+ return 0;
+
+ if (((uint32_t *)data)[0] != DTZ_MAGIC) {
+ std::cout << "Corrupted table" << std::endl;
+ return 0;
+ }
+
+ int files = data[4] & 0x02 ? 4 : 1;
+
+ data += 5;
+
+ if (!entry->has_pawns) {
+ struct DTZEntry_piece *ptr = (struct DTZEntry_piece *)entry;
+ setup_pieces_piece_dtz(ptr, data, &tb_size[0]);
+ data += ptr->num + 1;
+ data += ((uintptr_t)data) & 0x01;
+
+ ptr->precomp = setup_pairs(data, tb_size[0], &size[0], &next, &(ptr->flags), 0);
+ data = next;
+
+ ptr->map = data;
+ if (ptr->flags & 2) {
+ int i;
+ for (i = 0; i < 4; i++) {
+ ptr->map_idx[i] = (data + 1 - ptr->map);
+ data += 1 + data[0];
+ }
+ data += ((uintptr_t)data) & 0x01;
+ }
+
+ ptr->precomp->indextable = (char *)data;
+ data += size[0];
+
+ ptr->precomp->sizetable = (ushort *)data;
+ data += size[1];
+
+ data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f);
+ ptr->precomp->data = data;
+ data += size[2];
+ } else {
+ struct DTZEntry_pawn *ptr = (struct DTZEntry_pawn *)entry;
+ s = 1 + (ptr->pawns[1] > 0);
+ for (f = 0; f < 4; f++) {
+ setup_pieces_pawn_dtz(ptr, data, &tb_size[f], f);
+ data += ptr->num + s;
+ }
+ data += ((uintptr_t)data) & 0x01;
+
+ for (f = 0; f < files; f++) {
+ ptr->file[f].precomp = setup_pairs(data, tb_size[f], &size[3 * f], &next, &(ptr->flags[f]), 0);
+ data = next;
+ }
+
+ ptr->map = data;
+ for (f = 0; f < files; f++) {
+ if (ptr->flags[f] & 2) {
+ int i;
+ for (i = 0; i < 4; i++) {
+ ptr->map_idx[f][i] = (data + 1 - ptr->map);
+ data += 1 + data[0];
+ }
+ }
+ }
+ data += ((uintptr_t)data) & 0x01;
+
+ for (f = 0; f < files; f++) {
+ ptr->file[f].precomp->indextable = (char *)data;
+ data += size[3 * f];
+ }
+
+ for (f = 0; f < files; f++) {
+ ptr->file[f].precomp->sizetable = (ushort *)data;
+ data += size[3 * f + 1];
+ }
+
+ for (f = 0; f < files; f++) {
+ data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f);
+ ptr->file[f].precomp->data = data;
+ data += size[3 * f + 2];
+ }
+ }
+
+ return 1;
+}
+
+static ubyte decompress_pairs(struct PairsData *d, uint64_t idx)
+{
+ if (!d->idxbits)
+ return d->min_len;
+
+ uint32_t mainidx = idx >> d->idxbits;
+ int litidx = (idx & ((1 << d->idxbits) - 1)) - (1 << (d->idxbits - 1));
+ uint32_t block = *(uint32_t *)(d->indextable + 6 * mainidx);
+ litidx += *(ushort *)(d->indextable + 6 * mainidx + 4);
+ if (litidx < 0) {
+ do {
+ litidx += d->sizetable[--block] + 1;
+ } while (litidx < 0);
+ } else {
+ while (litidx > d->sizetable[block])
+ litidx -= d->sizetable[block++] + 1;
+ }
+
+ uint32_t *ptr = (uint32_t *)(d->data + (block << d->blocksize));
+
+ int m = d->min_len;
+ ushort *offset = d->offset;
+ base_t *base = d->base - m;
+ ubyte *symlen = d->symlen;
+ int sym, bitcnt;
+
+ uint64_t code = __builtin_bswap64(*((uint64_t *)ptr));
+ ptr += 2;
+ bitcnt = 0; // number of "empty bits" in code
+ for (;;) {
+ int l = m;
+ while (code < base[l]) l++;
+ sym = offset[l] + ((code - base[l]) >> (64 - l));
+ if (litidx < (int)symlen[sym] + 1) break;
+ litidx -= (int)symlen[sym] + 1;
+ code <<= l;
+ bitcnt += l;
+ if (bitcnt >= 32) {
+ bitcnt -= 32;
+ code |= ((uint64_t)(__builtin_bswap32(*ptr++))) << bitcnt;
+ }
+ }
+
+ ubyte *sympat = d->sympat;
+ while (symlen[sym] != 0) {
+ int w = *(int *)(sympat + 3 * sym);
+ int s1 = w & 0x0fff;
+ if (litidx < (int)symlen[s1] + 1)
+ sym = s1;
+ else {
+ litidx -= (int)symlen[s1] + 1;
+ sym = (w >> 12) & 0x0fff;
+ }
+ }
+
+ return *(sympat + 3 * sym);
+}
+
+TBEntry* load_dtz_table(const char* str, uint64_t key1, uint64_t key2)
+{
+ int i;
+ struct TBEntry *ptr, *ptr3;
+ struct TBHashEntry *ptr2;
+
+ // find corresponding WDL entry
+ ptr2 = WDL_hash[key1 >> (64 - TBHASHBITS)];
+ for (i = 0; i < HSHMAX; i++)
+ if (ptr2[i].key == key1) break;
+ if (i == HSHMAX) return NULL;
+ ptr = ptr2[i].ptr;
+
+ ptr3 = (struct TBEntry *)malloc(ptr->has_pawns
+ ? sizeof(struct DTZEntry_pawn)
+ : sizeof(struct DTZEntry_piece));
+
+ ptr3->data = map_file(str, DTZSUFFIX, &ptr3->mapping);
+ ptr3->key = ptr->key;
+ ptr3->num = ptr->num;
+ ptr3->symmetric = ptr->symmetric;
+ ptr3->has_pawns = ptr->has_pawns;
+ if (ptr3->has_pawns) {
+ struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr3;
+ entry->pawns[0] = ((struct TBEntry_pawn *)ptr)->pawns[0];
+ entry->pawns[1] = ((struct TBEntry_pawn *)ptr)->pawns[1];
+ } else {
+ struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr3;
+ entry->enc_type = ((struct TBEntry_piece *)ptr)->enc_type;
+ }
+ if (!init_table_dtz(ptr3)) {
+ free(ptr3);
+ return NULL;
+ }
+ return ptr3;
+}
+
+static void free_wdl_entry(struct TBEntry *entry)
+{
+ unmap_file(entry->data, entry->mapping);
+ entry->data = NULL;
+ if (!entry->has_pawns) {
+ struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry;
+ free(ptr->precomp[0]); ptr->precomp[0] = NULL;
+ free(ptr->precomp[1]); ptr->precomp[1] = NULL;
+ } else {
+ struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry;
+ int f;
+ for (f = 0; f < 4; f++) {
+ free(ptr->file[f].precomp[0]); ptr->file[f].precomp[0] = NULL;
+ free(ptr->file[f].precomp[1]); ptr->file[f].precomp[1] = NULL;
+ }
+ }
+}
+
+static void free_dtz_entry(struct TBEntry *entry)
+{
+ unmap_file(entry->data, entry->mapping);
+ entry->data = NULL;
+ if (!entry->has_pawns) {
+ struct DTZEntry_piece *ptr = (struct DTZEntry_piece *)entry;
+ free(ptr->precomp); ptr->precomp = NULL;
+ } else {
+ struct DTZEntry_pawn *ptr = (struct DTZEntry_pawn *)entry;
+ int f;
+ for (f = 0; f < 4; f++) {
+ free(ptr->file[f].precomp); ptr->file[f].precomp = NULL;
+ }
+ }
+ free(entry);
+}
+
+static int wdl_to_map[5] = { 1, 3, 0, 2, 0 };
+static ubyte pa_flags[5] = { 8, 0, 0, 0, 4 };
diff --git a/DroidFish/jni/rtb/rtb-core.hpp b/DroidFish/jni/rtb/rtb-core.hpp
new file mode 100644
index 0000000..9a98d1b
--- /dev/null
+++ b/DroidFish/jni/rtb/rtb-core.hpp
@@ -0,0 +1,142 @@
+/*
+ Copyright (c) 2011-2013 Ronald de Man
+*/
+
+#ifndef RTB_CORE_HPP_
+#define RTB_CORE_HPP_
+
+#ifndef __WIN32__
+#define SEP_CHAR ':'
+#define FD int
+#define FD_ERR -1
+#else
+#include
+#define SEP_CHAR ';'
+#define FD HANDLE
+#define FD_ERR INVALID_HANDLE_VALUE
+#endif
+
+#include
+#include
+
+#define WDLSUFFIX ".rtbw"
+#define DTZSUFFIX ".rtbz"
+#define TBPIECES 6
+
+#define WDL_MAGIC 0x5d23e871
+#define DTZ_MAGIC 0xa50c66d7
+
+#define TBHASHBITS 11
+
+typedef unsigned char ubyte;
+typedef unsigned short ushort;
+
+struct TBHashEntry;
+
+typedef uint64_t base_t;
+
+struct PairsData {
+ char *indextable;
+ ushort *sizetable;
+ ubyte *data;
+ ushort *offset;
+ ubyte *symlen;
+ ubyte *sympat;
+ int blocksize;
+ int idxbits;
+ int min_len;
+ base_t base[1]; // C++ complains about base[]...
+};
+
+struct TBEntry {
+ char *data;
+ uint64_t key;
+ uint64_t mapping;
+ std::atomic ready;
+ ubyte num;
+ ubyte symmetric;
+ ubyte has_pawns;
+} __attribute__((__may_alias__));
+
+struct TBEntry_piece {
+ char *data;
+ uint64_t key;
+ uint64_t mapping;
+ std::atomic ready;
+ ubyte num;
+ ubyte symmetric;
+ ubyte has_pawns;
+ ubyte enc_type;
+ struct PairsData *precomp[2];
+ int factor[2][TBPIECES];
+ ubyte pieces[2][TBPIECES];
+ ubyte norm[2][TBPIECES];
+};
+
+struct TBEntry_pawn {
+ char *data;
+ uint64_t key;
+ uint64_t mapping;
+ std::atomic ready;
+ ubyte num;
+ ubyte symmetric;
+ ubyte has_pawns;
+ ubyte pawns[2];
+ struct {
+ struct PairsData *precomp[2];
+ int factor[2][TBPIECES];
+ ubyte pieces[2][TBPIECES];
+ ubyte norm[2][TBPIECES];
+ } file[4];
+};
+
+struct DTZEntry_piece {
+ char *data;
+ uint64_t key;
+ uint64_t mapping;
+ std::atomic ready;
+ ubyte num;
+ ubyte symmetric;
+ ubyte has_pawns;
+ ubyte enc_type;
+ struct PairsData *precomp;
+ int factor[TBPIECES];
+ ubyte pieces[TBPIECES];
+ ubyte norm[TBPIECES];
+ ubyte flags; // accurate, mapped, side
+ ushort map_idx[4];
+ ubyte *map;
+};
+
+struct DTZEntry_pawn {
+ char *data;
+ uint64_t key;
+ uint64_t mapping;
+ std::atomic ready;
+ ubyte num;
+ ubyte symmetric;
+ ubyte has_pawns;
+ ubyte pawns[2];
+ struct {
+ struct PairsData *precomp;
+ int factor[TBPIECES];
+ ubyte pieces[TBPIECES];
+ ubyte norm[TBPIECES];
+ } file[4];
+ ubyte flags[4];
+ ushort map_idx[4][4];
+ ubyte *map;
+};
+
+struct TBHashEntry {
+ uint64_t key;
+ struct TBEntry *ptr;
+};
+
+struct DTZTableEntry {
+ uint64_t key1;
+ uint64_t key2;
+ std::atomic entry;
+};
+
+#endif
diff --git a/DroidFish/jni/rtb/rtb-probe.cpp b/DroidFish/jni/rtb/rtb-probe.cpp
new file mode 100644
index 0000000..010beaa
--- /dev/null
+++ b/DroidFish/jni/rtb/rtb-probe.cpp
@@ -0,0 +1,604 @@
+/*
+ Copyright (c) 2013 Ronald de Man
+ This file may be redistributed and/or modified without restrictions.
+
+ tbprobe.cpp contains the Stockfish-specific routines of the
+ tablebase probing code. It should be relatively easy to adapt
+ this code to other chess engines.
+*/
+
+#include "piece.hpp"
+#include "position.hpp"
+#include "moveGen.hpp"
+
+#include
+
+#include "rtb-probe.hpp"
+#include "rtb-core.hpp"
+
+#include "rtb-core-impl.hpp"
+
+int Syzygy::TBLargest = 0;
+
+// Given a position with 6 or fewer pieces, produce a text string
+// of the form KQPvKRP, where "KQP" represents the white pieces if
+// mirror == false and the black pieces if mirror == true.
+static void prt_str(Position& pos, char *str, bool mirror)
+{
+ static_assert(Piece::WQUEEN == Piece::WKING + 1, "");
+ static_assert(Piece::WROOK == Piece::WQUEEN + 1, "");
+ static_assert(Piece::WBISHOP == Piece::WROOK + 1, "");
+ static_assert(Piece::WKNIGHT == Piece::WBISHOP + 1, "");
+ static_assert(Piece::WPAWN == Piece::WKNIGHT + 1, "");
+ static_assert(Piece::BQUEEN == Piece::BKING + 1, "");
+ static_assert(Piece::BROOK == Piece::BQUEEN + 1, "");
+ static_assert(Piece::BBISHOP == Piece::BROOK + 1, "");
+ static_assert(Piece::BKNIGHT == Piece::BBISHOP + 1, "");
+ static_assert(Piece::BPAWN == Piece::BKNIGHT + 1, "");
+
+ static char pchr[Piece::nPieceTypes+1] = " KQRBNPKQRBNP";
+
+ int p1Beg = mirror ? Piece::BKING : Piece::WKING;
+ int p1End = mirror ? Piece::BPAWN : Piece::WPAWN;
+ int p2Beg = mirror ? Piece::WKING : Piece::BKING;
+ int p2End = mirror ? Piece::WPAWN : Piece::BPAWN;
+
+ for (int p = p1Beg; p <= p1End; p++) {
+ int cnt = BitBoard::bitCount(pos.pieceTypeBB((Piece::Type)p));
+ for (int i = 0; i < cnt; i++)
+ *str++ = pchr[p];
+ }
+ *str++ = 'v';
+ for (int p = p2Beg; p <= p2End; p++) {
+ int cnt = BitBoard::bitCount(pos.pieceTypeBB((Piece::Type)p));
+ for (int i = 0; i < cnt; i++)
+ *str++ = pchr[p];
+ }
+ *str++ = 0;
+}
+
+// Given a position, produce a 64-bit material signature key.
+// If the engine supports such a key, it should equal the engine's key.
+static uint64_t calc_key(const Position& pos, bool mirror)
+{
+ uint64_t h = mirror ? MatId::mirror(pos.materialId()) : pos.materialId();
+ h *= 0x842c2f50a7ac0ae1ULL;
+ h = ((h >> 32) ^ h) * 0xace7b66dbad28265ULL;
+ return h;
+}
+
+// Produce a 64-bit material key corresponding to the material combination
+// defined by pcs[16], where pcs[1], ..., pcs[6] is the number of white
+// pawns, ..., kings and pcs[9], ..., pcs[14] is the number of black
+// pawns, ..., kings.
+static uint64_t calc_key_from_pcs(const int *pcs, bool mirror)
+{
+ MatId key;
+ key.addPieceCnt(Piece::WPAWN, pcs[1]);
+ key.addPieceCnt(Piece::WKNIGHT, pcs[2]);
+ key.addPieceCnt(Piece::WBISHOP, pcs[3]);
+ key.addPieceCnt(Piece::WROOK, pcs[4]);
+ key.addPieceCnt(Piece::WQUEEN, pcs[5]);
+ key.addPieceCnt(Piece::BPAWN, pcs[8+1]);
+ key.addPieceCnt(Piece::BKNIGHT, pcs[8+2]);
+ key.addPieceCnt(Piece::BBISHOP, pcs[8+3]);
+ key.addPieceCnt(Piece::BROOK, pcs[8+4]);
+ key.addPieceCnt(Piece::BQUEEN, pcs[8+5]);
+
+ uint64_t h = mirror ? MatId::mirror(key()) : key();
+ h *= 0x842c2f50a7ac0ae1ULL;
+ h = ((h >> 32) ^ h) * 0xace7b66dbad28265ULL;
+ return h;
+}
+
+static uint64_t get_pieces(const Position& pos, int color, int piece) {
+ int p = 7 - piece;
+ if (color)
+ p += Piece::BKING - Piece::WKING;
+ return pos.pieceTypeBB((Piece::Type)p);
+}
+
+static inline int pop_lsb(uint64_t& bb) {
+ int ret = BitBoard::numberOfTrailingZeros(bb);
+ bb &= bb - 1;
+ return ret;
+}
+
+// probe_wdl_table and probe_dtz_table require similar adaptations.
+static int probe_wdl_table(Position& pos, int *success)
+{
+ struct TBEntry *ptr;
+ struct TBHashEntry *ptr2;
+ uint64_t idx;
+ uint64_t key;
+ int i;
+ ubyte res;
+ int p[TBPIECES];
+
+ // Obtain the position's material signature key.
+ key = calc_key(pos, false);
+
+ // Test for KvK.
+ if (!key) return 0;
+
+ ptr2 = WDL_hash[key >> (64 - TBHASHBITS)];
+ for (i = 0; i < HSHMAX; i++)
+ if (ptr2[i].key == key) break;
+ if (i == HSHMAX) {
+ *success = 0;
+ return 0;
+ }
+
+ ptr = ptr2[i].ptr;
+ ubyte ready = ptr->ready.load(std::memory_order_relaxed);
+ std::atomic_thread_fence(std::memory_order_acquire);
+ if (!ready) {
+ std::lock_guard L(TB_mutex);
+ ready = ptr->ready.load(std::memory_order_relaxed);
+ if (!ready) {
+ char str[16];
+ prt_str(pos, str, ptr->key != key);
+ if (!init_table_wdl(ptr, str)) {
+ ptr2[i].key = 0ULL;
+ *success = 0;
+ return 0;
+ }
+ std::atomic_thread_fence(std::memory_order_release);
+ ptr->ready.store(1, std::memory_order_relaxed);
+ }
+ }
+
+ int bside, mirror, cmirror;
+ if (!ptr->symmetric) {
+ if (key != ptr->key) {
+ cmirror = 8;
+ mirror = 0x38;
+ bside = pos.isWhiteMove();
+ } else {
+ cmirror = mirror = 0;
+ bside = !pos.isWhiteMove();
+ }
+ } else {
+ cmirror = pos.isWhiteMove() ? 0 : 8;
+ mirror = pos.isWhiteMove() ? 0 : 0x38;
+ bside = 0;
+ }
+
+ // p[i] is to contain the square 0-63 (A1-H8) for a piece of type
+ // pc[i] ^ cmirror, where 1 = white pawn, ..., 14 = black king.
+ // Pieces of the same type are guaranteed to be consecutive.
+ if (!ptr->has_pawns) {
+ struct TBEntry_piece *entry = (struct TBEntry_piece *)ptr;
+ ubyte *pc = entry->pieces[bside];
+ for (i = 0; i < entry->num;) {
+ uint64_t bb = get_pieces(pos, (pc[i] ^ cmirror) >> 3, pc[i] & 0x07);
+ do {
+ p[i++] = pop_lsb(bb);
+ } while (bb);
+ }
+ idx = encode_piece(entry, entry->norm[bside], p, entry->factor[bside]);
+ res = decompress_pairs(entry->precomp[bside], idx);
+ } else {
+ struct TBEntry_pawn *entry = (struct TBEntry_pawn *)ptr;
+ int k = entry->file[0].pieces[0][0] ^ cmirror;
+ uint64_t bb = get_pieces(pos, k >> 3, k & 0x07);
+ i = 0;
+ do {
+ p[i++] = pop_lsb(bb) ^ mirror;
+ } while (bb);
+ int f = pawn_file(entry, p);
+ ubyte *pc = entry->file[f].pieces[bside];
+ for (; i < entry->num;) {
+ bb = get_pieces(pos, (pc[i] ^ cmirror) >> 3, pc[i] & 0x07);
+ do {
+ p[i++] = pop_lsb(bb) ^ mirror;
+ } while (bb);
+ }
+ idx = encode_pawn(entry, entry->file[f].norm[bside], p, entry->file[f].factor[bside]);
+ res = decompress_pairs(entry->file[f].precomp[bside], idx);
+ }
+
+ return ((int)res) - 2;
+}
+
+static int probe_dtz_table(Position& pos, int wdl, int *success)
+{
+ uint64_t idx;
+ int i, res;
+ int p[TBPIECES];
+
+ // Obtain the position's material signature key.
+ uint64_t key = calc_key(pos, false);
+
+ DTZTableEntry* dtzTabEnt;
+ {
+ dtzTabEnt = DTZ_hash[key >> (64 - TBHASHBITS)];
+ for (i = 0; i < HSHMAX; i++)
+ if (dtzTabEnt[i].key1 == key) break;
+ if (i == HSHMAX) {
+ uint64_t key2 = calc_key(pos, true);
+ dtzTabEnt = DTZ_hash[key2 >> (64 - TBHASHBITS)];
+ for (i = 0; i < HSHMAX; i++)
+ if (dtzTabEnt[i].key2 == key) break;
+ }
+ if (i == HSHMAX) {
+ *success = 0;
+ return 0;
+ }
+ dtzTabEnt += i;
+ }
+
+ TBEntry* ptr = dtzTabEnt->entry.load(std::memory_order_relaxed);
+ std::atomic_thread_fence(std::memory_order_acquire);
+ if (!ptr) {
+ std::lock_guard L(TB_mutex);
+ ptr = dtzTabEnt->entry.load(std::memory_order_relaxed);
+ if (!ptr) {
+ struct TBHashEntry *ptr2 = WDL_hash[key >> (64 - TBHASHBITS)];
+ for (i = 0; i < HSHMAX; i++)
+ if (ptr2[i].key == key) break;
+ if (i == HSHMAX) {
+ *success = 0;
+ return 0;
+ }
+ char str[16];
+ bool mirror = (ptr2[i].ptr->key != key);
+ prt_str(pos, str, mirror);
+ ptr = load_dtz_table(str, calc_key(pos, mirror), calc_key(pos, !mirror));
+ std::atomic_thread_fence(std::memory_order_release);
+ dtzTabEnt->entry.store(ptr, std::memory_order_relaxed);
+ }
+ }
+
+ if (!ptr) {
+ *success = 0;
+ return 0;
+ }
+
+ int bside, mirror, cmirror;
+ if (!ptr->symmetric) {
+ if (key != ptr->key) {
+ cmirror = 8;
+ mirror = 0x38;
+ bside = pos.isWhiteMove();
+ } else {
+ cmirror = mirror = 0;
+ bside = !pos.isWhiteMove();
+ }
+ } else {
+ cmirror = pos.isWhiteMove() ? 0 : 8;
+ mirror = pos.isWhiteMove() ? 0 : 0x38;
+ bside = 0;
+ }
+
+ if (!ptr->has_pawns) {
+ struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr;
+ if ((entry->flags & 1) != bside && !entry->symmetric) {
+ *success = -1;
+ return 0;
+ }
+ ubyte *pc = entry->pieces;
+ for (i = 0; i < entry->num;) {
+ uint64_t bb = get_pieces(pos, (pc[i] ^ cmirror) >> 3, pc[i] & 0x07);
+ do {
+ p[i++] = pop_lsb(bb);
+ } while (bb);
+ }
+ idx = encode_piece((struct TBEntry_piece *)entry, entry->norm, p, entry->factor);
+ res = decompress_pairs(entry->precomp, idx);
+
+ if (entry->flags & 2)
+ res = entry->map[entry->map_idx[wdl_to_map[wdl + 2]] + res];
+
+ if (!(entry->flags & pa_flags[wdl + 2]) || (wdl & 1))
+ res *= 2;
+ } else {
+ struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr;
+ int k = entry->file[0].pieces[0] ^ cmirror;
+ uint64_t bb = get_pieces(pos, k >> 3, k & 0x07);
+ i = 0;
+ do {
+ p[i++] = pop_lsb(bb) ^ mirror;
+ } while (bb);
+ int f = pawn_file((struct TBEntry_pawn *)entry, p);
+ if ((entry->flags[f] & 1) != bside) {
+ *success = -1;
+ return 0;
+ }
+ ubyte *pc = entry->file[f].pieces;
+ for (; i < entry->num;) {
+ bb = get_pieces(pos, (pc[i] ^ cmirror) >> 3, pc[i] & 0x07);
+ do {
+ p[i++] = pop_lsb(bb) ^ mirror;
+ } while (bb);
+ }
+ idx = encode_pawn((struct TBEntry_pawn *)entry, entry->file[f].norm, p, entry->file[f].factor);
+ res = decompress_pairs(entry->file[f].precomp, idx);
+
+ if (entry->flags[f] & 2)
+ res = entry->map[entry->map_idx[f][wdl_to_map[wdl + 2]] + res];
+
+ if (!(entry->flags[f] & pa_flags[wdl + 2]) || (wdl & 1))
+ res *= 2;
+ }
+
+ return res;
+}
+
+// Add bishop and rook underpromotion captures to move list.
+static void add_underprom_caps(Position& pos, MoveGen::MoveList& moveList)
+{
+ const int nMoves = moveList.size;
+ const bool wtm = pos.isWhiteMove();
+ const int queen = wtm ? Piece::WQUEEN : Piece::BQUEEN;
+ for (int i = 0; i < nMoves; i++) {
+ const Move& m = moveList[i];
+ if ((m.promoteTo() == queen) && (pos.getPiece(m.to()) != Piece::EMPTY)) {
+ moveList.addMove(m.from(), m.to(), wtm ? Piece::WROOK : Piece::BROOK);
+ moveList.addMove(m.from(), m.to(), wtm ? Piece::WBISHOP : Piece::BBISHOP);
+ }
+ }
+}
+
+static int probe_ab(Position& pos, int alpha, int beta, int *success)
+{
+ // Generate (at least) all legal non-ep captures including (under)promotions.
+ // It is OK to generate more, as long as they are filtered out below.
+ MoveGen::MoveList moveList;
+ const bool inCheck = MoveGen::inCheck(pos);
+ if (inCheck) {
+ MoveGen::checkEvasions(pos, moveList);
+ } else {
+ MoveGen::pseudoLegalCaptures(pos, moveList);
+ // Since bishop and rook promotions are not included, we need to add them.
+ add_underprom_caps(pos, moveList);
+ }
+
+ UndoInfo ui;
+ for (int m = 0; m < moveList.size; m++) {
+ const Move& capture = moveList[m];
+ if ((pos.getPiece(capture.to()) == Piece::EMPTY) ||
+ !MoveGen::isLegal(pos, capture, inCheck))
+ continue;
+ pos.makeMove(capture, ui);
+ int v = -probe_ab(pos, -beta, -alpha, success);
+ pos.unMakeMove(capture, ui);
+ if (*success == 0) return 0;
+ if (v > alpha) {
+ if (v >= beta) {
+ *success = 2;
+ return v;
+ }
+ alpha = v;
+ }
+ }
+
+ int v = probe_wdl_table(pos, success);
+ if (*success == 0) return 0;
+ if (alpha >= v) {
+ *success = 1 + (alpha > 0);
+ return alpha;
+ } else {
+ *success = 1;
+ return v;
+ }
+}
+
+int Syzygy::probe_wdl(Position& pos, int *success)
+{
+ *success = 1;
+ int v = probe_ab(pos, -2, 2, success);
+
+ // If en passant is not possible, we are done.
+ if (pos.getEpSquare() == -1)
+ return v;
+ if (!(*success)) return 0;
+
+ // Now handle en passant.
+ int v1 = -3;
+ // Generate (at least) all legal en passant captures.
+ MoveGen::MoveList moveList;
+
+ const bool inCheck = MoveGen::inCheck(pos);
+ if (inCheck) {
+ MoveGen::checkEvasions(pos, moveList);
+ } else {
+ MoveGen::pseudoLegalMoves(pos, moveList);
+ }
+
+ const int pawn = pos.isWhiteMove() ? Piece::WPAWN : Piece::BPAWN;
+ UndoInfo ui;
+ for (int m = 0; m < moveList.size; m++) {
+ const Move& capture = moveList[m];
+ if ((capture.to() != pos.getEpSquare()) || (pos.getPiece(capture.from()) != pawn) ||
+ !MoveGen::isLegal(pos, capture, inCheck))
+ continue;
+ pos.makeMove(capture, ui);
+ int v0 = -probe_ab(pos, -2, 2, success);
+ pos.unMakeMove(capture, ui);
+ if (*success == 0) return 0;
+ if (v0 > v1) v1 = v0;
+ }
+ if (v1 > -3) {
+ if (v1 >= v) v = v1;
+ else if (v == 0) {
+ // Check whether there is at least one legal non-ep move.
+ for (int m = 0; m < moveList.size; m++) {
+ const Move& capture = moveList[m];
+ if ((capture.to() == pos.getEpSquare()) &&
+ (pos.getPiece(capture.from()) == pawn))
+ continue;
+ if (MoveGen::isLegal(pos, capture, inCheck))
+ return v;
+ }
+ // If not, then we are forced to play the losing ep capture.
+ v = v1;
+ }
+ }
+
+ return v;
+}
+
+// This routine treats a position with en passant captures as one without.
+static int probe_dtz_no_ep(Position& pos, int *success)
+{
+ const int wdl = probe_ab(pos, -2, 2, success);
+ if (*success == 0) return 0;
+
+ if (wdl == 0) return 0;
+
+ if (*success == 2)
+ return wdl == 2 ? 1 : 101;
+
+ MoveGen::MoveList moveList;
+ const bool inCheck = MoveGen::inCheck(pos);
+ const int pawn = pos.isWhiteMove() ? Piece::WPAWN : Piece::BPAWN;
+ UndoInfo ui;
+
+ if (wdl > 0) {
+ // Generate at least all legal non-capturing pawn moves
+ // including non-capturing promotions.
+ if (inCheck) {
+ MoveGen::checkEvasions(pos, moveList);
+ } else {
+ MoveGen::pseudoLegalMoves(pos, moveList);
+ }
+
+ for (int m = 0; m < moveList.size; m++) {
+ const Move& move = moveList[m];
+ if ((pos.getPiece(move.from()) != pawn) ||
+ (Position::getX(move.from()) != Position::getX(move.to())) ||
+ !MoveGen::isLegal(pos, move, inCheck))
+ continue;
+ pos.makeMove(move, ui);
+ int v = -probe_ab(pos, -2, -wdl + 1, success);
+ pos.unMakeMove(move, ui);
+ if (*success == 0) return 0;
+ if (v == wdl)
+ return v == 2 ? 1 : 101;
+ }
+ }
+
+ int dtz = 1 + probe_dtz_table(pos, wdl, success);
+
+ if (*success >= 0) {
+ if (wdl & 1) dtz += 100;
+ return wdl >= 0 ? dtz : -dtz;
+ }
+
+ if (wdl > 0) {
+ int best = 0xffff;
+ for (int m = 0; m < moveList.size; m++) {
+ const Move& move = moveList[m];
+ if ((pos.getPiece(move.to()) != Piece::EMPTY) ||
+ (pos.getPiece(move.from()) == pawn) ||
+ !MoveGen::isLegal(pos, move, inCheck))
+ continue;
+ pos.makeMove(move, ui);
+ int v = -Syzygy::probe_dtz(pos, success);
+ pos.unMakeMove(move, ui);
+ if (*success == 0) return 0;
+ if (v > 0 && v + 1 < best)
+ best = v + 1;
+ }
+ return best;
+ } else {
+ int best = -1;
+ if (inCheck) {
+ MoveGen::checkEvasions(pos, moveList);
+ } else {
+ MoveGen::pseudoLegalMoves(pos, moveList);
+ }
+ for (int m = 0; m < moveList.size; m++) {
+ const Move& move = moveList[m];
+ if (!MoveGen::isLegal(pos, move, inCheck))
+ continue;
+ pos.makeMove(move, ui);
+ int v;
+ if (pos.getHalfMoveClock() == 0) {
+ if (wdl == -2) v = -1;
+ else {
+ v = probe_ab(pos, 1, 2, success);
+ v = (v == 2) ? 0 : -101;
+ }
+ } else {
+ v = -Syzygy::probe_dtz(pos, success) - 1;
+ }
+ pos.unMakeMove(move, ui);
+ if (*success == 0) return 0;
+ if (v < best)
+ best = v;
+ }
+ return best;
+ }
+}
+
+static int wdl_to_dtz[] = {
+ -1, -101, 0, 101, 1
+};
+
+int Syzygy::probe_dtz(Position& pos, int *success)
+{
+ *success = 1;
+ int v = probe_dtz_no_ep(pos, success);
+
+ if (pos.getEpSquare() == -1)
+ return v;
+ if (*success == 0) return 0;
+
+ // Now handle en passant.
+ int v1 = -3;
+
+ MoveGen::MoveList moveList;
+ const bool inCheck = MoveGen::inCheck(pos);
+ const int pawn = pos.isWhiteMove() ? Piece::WPAWN : Piece::BPAWN;
+ UndoInfo ui;
+
+ if (!inCheck) {
+ MoveGen::pseudoLegalMoves(pos, moveList);
+ } else {
+ MoveGen::checkEvasions(pos, moveList);
+ }
+
+ for (int m = 0; m < moveList.size; m++) {
+ const Move& capture = moveList[m];
+ if ((capture.to() != pos.getEpSquare()) ||
+ (pos.getPiece(capture.from()) != pawn) ||
+ !MoveGen::isLegal(pos, capture, inCheck))
+ continue;
+ pos.makeMove(capture, ui);
+ int v0 = -probe_ab(pos, -2, 2, success);
+ pos.unMakeMove(capture, ui);
+ if (*success == 0) return 0;
+ if (v0 > v1) v1 = v0;
+ }
+ if (v1 > -3) {
+ v1 = wdl_to_dtz[v1 + 2];
+ if (v < -100) {
+ if (v1 >= 0)
+ v = v1;
+ } else if (v < 0) {
+ if (v1 >= 0 || v1 < 100)
+ v = v1;
+ } else if (v > 100) {
+ if (v1 > 0)
+ v = v1;
+ } else if (v > 0) {
+ if (v1 == 1)
+ v = v1;
+ } else if (v1 >= 0) {
+ v = v1;
+ } else {
+ for (int m = 0; m < moveList.size; m++) {
+ const Move& move = moveList[m];
+ if ((move.to() == pos.getEpSquare()) && (pos.getPiece(move.from()) == pawn))
+ continue;
+ if (MoveGen::isLegal(pos, move, inCheck))
+ return v;
+ }
+ v = v1;
+ }
+ }
+
+ return v;
+}
diff --git a/DroidFish/jni/rtb/rtb-probe.hpp b/DroidFish/jni/rtb/rtb-probe.hpp
new file mode 100644
index 0000000..81bc500
--- /dev/null
+++ b/DroidFish/jni/rtb/rtb-probe.hpp
@@ -0,0 +1,54 @@
+#ifndef RTB_PROBE_HPP_
+#define RTB_PROBE_HPP_
+
+#include
+
+class Position;
+
+namespace Syzygy {
+
+extern int TBLargest;
+
+void init(const std::string& path);
+
+// Probe the WDL table for a particular position.
+// If *success != 0, the probe was successful.
+// The return value is from the point of view of the side to move:
+// -2 : loss
+// -1 : loss, but draw under 50-move rule
+// 0 : draw
+// 1 : win, but draw under 50-move rule
+// 2 : win
+int probe_wdl(Position& pos, int *success);
+
+// Probe the DTZ table for a particular position.
+// If *success != 0, the probe was successful.
+// The return value is from the point of view of the side to move:
+// n < -100 : loss, but draw under 50-move rule
+// -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0)
+// 0 : draw
+// 1 < n <= 100 : win in n ply (assuming 50-move counter == 0)
+// 100 < n : win, but draw under 50-move rule
+//
+// The return value n can be off by 1: a return value -n can mean a loss
+// in n+1 ply and a return value +n can mean a win in n+1 ply. This
+// cannot happen for tables with positions exactly on the "edge" of
+// the 50-move rule.
+//
+// This implies that if dtz > 0 is returned, the position is certainly
+// a win if dtz + 50-move-counter <= 99. Care must be taken that the engine
+// picks moves that preserve dtz + 50-move-counter <= 99.
+//
+// If n = 100 immediately after a capture or pawn move, then the position
+// is also certainly a win, and during the whole phase until the next
+// capture or pawn move, the inequality to be preserved is
+// dtz + 50-movecounter <= 100.
+//
+// In short, if a move is available resulting in dtz + 50-move-counter <= 99,
+// then do not accept moves leading to dtz + 50-move-counter == 100.
+//
+int probe_dtz(Position& pos, int *success);
+
+}
+
+#endif
diff --git a/DroidFish/jni/rtb/tbprobe.cpp b/DroidFish/jni/rtb/tbprobe.cpp
new file mode 100644
index 0000000..e8b25c0
--- /dev/null
+++ b/DroidFish/jni/rtb/tbprobe.cpp
@@ -0,0 +1,108 @@
+/*
+ Texel - A UCI chess engine.
+ Copyright (C) 2014 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*
+ * tbprobe.cpp
+ *
+ * Created on: Jun 2, 2014
+ * Author: petero
+ */
+
+#include "tbprobe.hpp"
+#include "rtb-probe.hpp"
+#include "bitBoard.hpp"
+#include "position.hpp"
+#include "moveGen.hpp"
+#include
+#include
+
+
+static std::string currentRtbPath;
+
+void
+TBProbe::initialize(const std::string& rtbPath) {
+ if (rtbPath != currentRtbPath) {
+ Syzygy::init(rtbPath);
+ currentRtbPath = rtbPath;
+ }
+}
+
+bool
+TBProbe::rtbProbeDTZ(Position& pos, int& score) {
+ const int nPieces = BitBoard::bitCount(pos.occupiedBB());
+ if (nPieces > Syzygy::TBLargest)
+ return false;
+ if (pos.getCastleMask())
+ return false;
+ if (MoveGen::canTakeKing(pos))
+ return false;
+
+ int success;
+ const int dtz = Syzygy::probe_dtz(pos, &success);
+ if (!success)
+ return false;
+ if (dtz == 0) {
+ score = 0;
+ return true;
+ }
+ const int maxHalfMoveClock = std::abs(dtz) + pos.getHalfMoveClock();
+ if (abs(dtz) <= 2) {
+ if (maxHalfMoveClock > 101) {
+ score = 0;
+ return true;
+ } else if (maxHalfMoveClock == 101)
+ return false; // DTZ can be wrong when mate-in-1
+ } else {
+ if (maxHalfMoveClock > 100) {
+ score = 0;
+ return true;
+ }
+ }
+ score = dtz;
+ return true;
+}
+
+bool
+TBProbe::rtbProbeWDL(Position& pos, int& score) {
+ if (BitBoard::bitCount(pos.occupiedBB()) > Syzygy::TBLargest)
+ return false;
+ if (pos.getCastleMask())
+ return false;
+ if (MoveGen::canTakeKing(pos))
+ return false;
+
+ int success;
+ int wdl = Syzygy::probe_wdl(pos, &success);
+ if (!success)
+ return false;
+ switch (wdl) {
+ case 0: case 1: case -1:
+ score = 0;
+ break;
+ case 2:
+ score = 1;
+ break;
+ case -2:
+ score = -1;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
diff --git a/DroidFish/jni/rtb/tbprobe.hpp b/DroidFish/jni/rtb/tbprobe.hpp
new file mode 100644
index 0000000..266a82c
--- /dev/null
+++ b/DroidFish/jni/rtb/tbprobe.hpp
@@ -0,0 +1,66 @@
+/*
+ Texel - A UCI chess engine.
+ Copyright (C) 2014 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*
+ * tbprobe.hpp
+ *
+ * Created on: Jun 2, 2014
+ * Author: petero
+ */
+
+#ifndef TBPROBE_HPP_
+#define TBPROBE_HPP_
+
+#include "moveGen.hpp"
+
+#include
+
+
+class Position;
+
+/**
+ * Handle tablebase probing.
+ */
+class TBProbe {
+public:
+ /** Initialize tablebases. */
+ static void initialize(const std::string& rtbPath);
+
+ /**
+ * Probe syzygy DTZ tablebases.
+ * @param pos The position to probe. The position can be temporarily modified
+ * but is restored to original state before function returns.
+ * @param score The tablebase score. Only modified for tablebase hits.
+ * The returned score is either 0 or a mate bound. The bound
+ * is computed by considering the DTZ value and the maximum number
+ * of zeroing moves before mate.
+ */
+ static bool rtbProbeDTZ(Position& pos, int& score);
+
+ /**
+ * Probe syzygy WDL tablebases.
+ * @param pos The position to probe. The position can be temporarily modified
+ * but is restored to original state before function returns.
+ * @param score The tablebase score. Only modified for tablebase hits.
+ * The returned score is either 0 or +/- 1.
+ */
+ static bool rtbProbeWDL(Position& pos, int& score);
+};
+
+
+#endif /* TBPROBE_HPP_ */
diff --git a/DroidFish/jni/rtb/undoInfo.hpp b/DroidFish/jni/rtb/undoInfo.hpp
new file mode 100644
index 0000000..787bd12
--- /dev/null
+++ b/DroidFish/jni/rtb/undoInfo.hpp
@@ -0,0 +1,40 @@
+/*
+ Texel - A UCI chess engine.
+ Copyright (C) 2012 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*
+ * undoInfo.hpp
+ *
+ * Created on: Feb 25, 2012
+ * Author: petero
+ */
+
+#ifndef UNDOINFO_HPP_
+#define UNDOINFO_HPP_
+
+/**
+ * Contains enough information to undo a previous move.
+ * Set by makeMove(). Used by unMakeMove().
+ */
+struct UndoInfo {
+ int capturedPiece;
+ int castleMask;
+ int epSquare;
+ int halfMoveClock;
+};
+
+#endif /* UNDOINFO_HPP_ */
diff --git a/DroidFish/jni/rtb/util.hpp b/DroidFish/jni/rtb/util.hpp
new file mode 100644
index 0000000..334ae50
--- /dev/null
+++ b/DroidFish/jni/rtb/util.hpp
@@ -0,0 +1,50 @@
+/*
+ Texel - A UCI chess engine.
+ Copyright (C) 2012-2014 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*
+ * util.hpp
+ *
+ * Created on: Feb 26, 2012
+ * Author: petero
+ */
+
+#ifndef UTIL_HPP_
+#define UTIL_HPP_
+
+#include
+
+typedef uint64_t U64;
+typedef int64_t S64;
+typedef uint32_t U32;
+typedef int32_t S32;
+typedef uint16_t U16;
+typedef int16_t S16;
+typedef int8_t S8;
+typedef uint8_t U8;
+
+
+/** Helper class to perform static initialization of a class T. */
+template
+class StaticInitializer {
+public:
+ StaticInitializer() {
+ T::staticInitialize();
+ }
+};
+
+#endif /* UTIL_HPP_ */
diff --git a/DroidFish/src/org/petero/droidfish/ChessBoard.java b/DroidFish/src/org/petero/droidfish/ChessBoard.java
index 662c5c4..c05d14f 100644
--- a/DroidFish/src/org/petero/droidfish/ChessBoard.java
+++ b/DroidFish/src/org/petero/droidfish/ChessBoard.java
@@ -26,6 +26,7 @@ import org.petero.droidfish.gamelogic.Move;
import org.petero.droidfish.gamelogic.Piece;
import org.petero.droidfish.gamelogic.Position;
import org.petero.droidfish.gamelogic.UndoInfo;
+import org.petero.droidfish.gtb.ProbeResult;
import android.content.Context;
import android.graphics.Canvas;
@@ -57,22 +58,17 @@ public abstract class ChessBoard extends View {
List moveHints;
- /** Decoration for a square. Currently the only possible decoration is a number. */
+ /** Decoration for a square. Currently the only possible decoration is a tablebase probe result. */
public final static class SquareDecoration implements Comparable {
int sq;
- int number;
- public SquareDecoration(int sq, int number) {
+ ProbeResult tbData;
+ public SquareDecoration(int sq, ProbeResult tbData) {
this.sq = sq;
- this.number = number;
+ this.tbData = tbData;
}
@Override
public int compareTo(SquareDecoration another) {
- int M0 = 100000;
- int n = number;
- int s1 = (n > 0) ? M0 - n : ((n == 0) ? 0 : -M0-n);
- n = another.number;
- int s2 = (n > 0) ? M0 - n : ((n == 0) ? 0 : -M0-n);
- return s2 - s1;
+ return tbData.compareTo(another.tbData);
}
}
private ArrayList decorations;
@@ -681,20 +677,42 @@ public abstract class ChessBoard extends View {
int xCrd = getXCrd(Position.getX(sq));
int yCrd = getYCrd(Position.getY(sq));
- int num = sd.number;
- String s;
- if (num > 0)
- s = "+" + String.valueOf(num);
- else if (num < 0)
- s = String.valueOf(num);
- else
- s = "0";
-
- Rect bounds = new Rect();
- decorationPaint.getTextBounds(s, 0, s.length(), bounds);
- xCrd += (sqSize - (bounds.left + bounds.right)) / 2;
- yCrd += (sqSize - (bounds.top + bounds.bottom)) / 2;
- canvas.drawText(s, xCrd, yCrd, decorationPaint);
+ String s = null;
+ int wdl = sd.tbData.wdl;
+ int num = (sd.tbData.score + 1) / 2;
+ switch (sd.tbData.type) {
+ case DTM:
+ if (wdl > 0)
+ s = "+" + String.valueOf(num);
+ else if (wdl < 0)
+ s = "-" + String.valueOf(num);
+ else
+ s = "0";
+ break;
+ case DTZ:
+ if (wdl > 0)
+ s = "W" + String.valueOf(num);
+ else if (wdl < 0)
+ s = "L" + String.valueOf(num);
+ else
+ s = "0";
+ break;
+ case WDL:
+ if (wdl > 0)
+ s = "W";
+ else if (wdl < 0)
+ s = "L";
+ else
+ s = "0";
+ break;
+ }
+ if (s != null) {
+ Rect bounds = new Rect();
+ decorationPaint.getTextBounds(s, 0, s.length(), bounds);
+ xCrd += (sqSize - (bounds.left + bounds.right)) / 2;
+ yCrd += (sqSize - (bounds.top + bounds.bottom)) / 2;
+ canvas.drawText(s, xCrd, yCrd, decorationPaint);
+ }
}
}
diff --git a/DroidFish/src/org/petero/droidfish/DroidFish.java b/DroidFish/src/org/petero/droidfish/DroidFish.java
index a57bd6f..4905395 100644
--- a/DroidFish/src/org/petero/droidfish/DroidFish.java
+++ b/DroidFish/src/org/petero/droidfish/DroidFish.java
@@ -58,6 +58,7 @@ import org.petero.droidfish.gamelogic.PgnToken;
import org.petero.droidfish.gamelogic.GameTree.Node;
import org.petero.droidfish.gamelogic.TimeControlData;
import org.petero.droidfish.gtb.Probe;
+import org.petero.droidfish.gtb.ProbeResult;
import com.kalab.chess.enginesupport.ChessEngine;
import com.kalab.chess.enginesupport.ChessEngineResolver;
@@ -147,7 +148,6 @@ public class DroidFish extends Activity implements GUIInterface {
// FIXME!!! Computer clock should stop if phone turned off (computer stops thinking if unplugged)
// FIXME!!! Add support for "no time control" and "hour-glass time control" as defined by the PGN standard
- // FIXME!!! Online play on FICS
// FIXME!!! Add chess960 support
// FIXME!!! Implement "hint" feature
@@ -1234,7 +1234,8 @@ public class DroidFish extends Activity implements GUIInterface {
private final void setEngineOptions(boolean restart) {
computeNetEngineID();
ctrl.setEngineOptions(new EngineOptions(engineOptions), restart);
- Probe.getInstance().setPath(engineOptions.gtbPath, egtbForceReload);
+ Probe.getInstance().setPath(engineOptions.gtbPath, engineOptions.rtbPath,
+ egtbForceReload);
egtbForceReload = false;
}
@@ -1259,14 +1260,14 @@ public class DroidFish extends Activity implements GUIInterface {
}
Probe gtbProbe = Probe.getInstance();
- ArrayList> x = gtbProbe.movePieceProbe(cb.pos, sq);
+ ArrayList> x = gtbProbe.movePieceProbe(cb.pos, sq);
if (x == null) {
cb.setSquareDecorations(null);
return;
}
ArrayList sd = new ArrayList();
- for (Pair p : x)
+ for (Pair p : x)
sd.add(new SquareDecoration(p.first, p.second));
cb.setSquareDecorations(sd);
}
diff --git a/DroidFish/src/org/petero/droidfish/activities/EditBoard.java b/DroidFish/src/org/petero/droidfish/activities/EditBoard.java
index 9b62264..0477b00 100644
--- a/DroidFish/src/org/petero/droidfish/activities/EditBoard.java
+++ b/DroidFish/src/org/petero/droidfish/activities/EditBoard.java
@@ -35,6 +35,7 @@ import org.petero.droidfish.gamelogic.Piece;
import org.petero.droidfish.gamelogic.Position;
import org.petero.droidfish.gamelogic.TextIO;
import org.petero.droidfish.gtb.Probe;
+import org.petero.droidfish.gtb.ProbeResult;
import android.annotation.SuppressLint;
import android.app.Activity;
@@ -256,14 +257,14 @@ public class EditBoard extends Activity {
}
Probe gtbProbe = Probe.getInstance();
- ArrayList> x = gtbProbe.relocatePieceProbe(cb.pos, sq);
+ ArrayList> x = gtbProbe.relocatePieceProbe(cb.pos, sq);
if (x == null) {
cb.setSquareDecorations(null);
return;
}
ArrayList sd = new ArrayList();
- for (Pair p : x)
+ for (Pair p : x)
sd.add(new SquareDecoration(p.first, p.second));
cb.setSquareDecorations(sd);
}
diff --git a/DroidFish/src/org/petero/droidfish/gamelogic/Position.java b/DroidFish/src/org/petero/droidfish/gamelogic/Position.java
index de5bd4a..bd728e9 100644
--- a/DroidFish/src/org/petero/droidfish/gamelogic/Position.java
+++ b/DroidFish/src/org/petero/droidfish/gamelogic/Position.java
@@ -214,15 +214,21 @@ public class Position {
return whiteMove ? wKingSq : bKingSq;
}
- /**
- * Count number of pieces of a certain type.
- */
+ /** Count number of pieces of a certain type. */
public final int nPieces(int pType) {
int ret = 0;
- for (int sq = 0; sq < 64; sq++) {
+ for (int sq = 0; sq < 64; sq++)
if (squares[sq] == pType)
ret++;
- }
+ return ret;
+ }
+
+ /** Count total number of pieces. */
+ public final int nPieces() {
+ int ret = 0;
+ for (int sq = 0; sq < 64; sq++)
+ if (squares[sq] != Piece.EMPTY)
+ ret++;
return ret;
}
diff --git a/DroidFish/src/org/petero/droidfish/gtb/Probe.java b/DroidFish/src/org/petero/droidfish/gtb/Probe.java
index 6b785b2..ad3df65 100644
--- a/DroidFish/src/org/petero/droidfish/gtb/Probe.java
+++ b/DroidFish/src/org/petero/droidfish/gtb/Probe.java
@@ -27,53 +27,56 @@ import org.petero.droidfish.gamelogic.Piece;
import org.petero.droidfish.gamelogic.Position;
import org.petero.droidfish.gamelogic.UndoInfo;
-/** Interface between Position class and GTB probing code. */
+/** Interface between Position class and GTB/RTB probing code. */
public class Probe {
private final GtbProbe gtb;
+ private final RtbProbe rtb;
private final int whiteSquares[];
private final int blackSquares[];
private final byte whitePieces[];
private final byte blackPieces[];
- private static final Probe INSTANCE = new Probe();
+ private static final Probe instance = new Probe();
/** Get singleton instance. */
public static Probe getInstance() {
- return INSTANCE;
+ return instance;
}
/** Constructor. */
private Probe() {
gtb = new GtbProbe();
+ rtb = new RtbProbe();
whiteSquares = new int[65];
blackSquares = new int[65];
whitePieces = new byte[65];
blackPieces = new byte[65];
}
- public void setPath(String tbPath, boolean forceReload) {
- gtb.setPath(tbPath, forceReload);
+ public void setPath(String gtbPath, String rtbPath, boolean forceReload) {
+ gtb.setPath(gtbPath, forceReload);
+ rtb.setPath(rtbPath, forceReload);
}
- public static final class ProbeResult {
+ private static final class GtbProbeResult {
public final static int DRAW = 0;
public final static int WMATE = 1;
public final static int BMATE = 2;
public final static int UNKNOWN = 3;
public int result;
- public int movesToMate; // Full moves to mate, or 0 if DRAW or UNKNOWN.
+ public int pliesToMate; // Plies to mate, or 0 if DRAW or UNKNOWN.
}
/**
- * Probe table bases.
+ * Probe GTB tablebases.
* @param pos The position to probe.
* @param result Two element array. Set to [tbinfo, plies].
* @return True if success.
*/
- public final ProbeResult probeHard(Position pos) {
- ProbeResult ret = probeHardRaw(pos);
- if (ret.result == ProbeResult.DRAW && pos.getEpSquare() != -1) {
+ private final GtbProbeResult gtbProbe(Position pos) {
+ GtbProbeResult ret = gtbProbeRaw(pos);
+ if (ret.result == GtbProbeResult.DRAW && pos.getEpSquare() != -1) {
ArrayList moveList = MoveGen.instance.legalMoves(pos);
int pawn = pos.whiteMove ? Piece.WPAWN : Piece.BPAWN;
int maxMate = -1;
@@ -82,29 +85,29 @@ public class Probe {
if ((move.to != pos.getEpSquare()) || (pos.getPiece(move.from) != pawn))
return ret;
pos.makeMove(move, ui);
- ProbeResult ret2 = probeHard(pos);
+ GtbProbeResult ret2 = gtbProbe(pos);
pos.unMakeMove(move, ui);
switch (ret2.result) {
- case ProbeResult.DRAW:
+ case GtbProbeResult.DRAW:
break;
- case ProbeResult.WMATE:
- case ProbeResult.BMATE:
- maxMate = Math.max(maxMate, ret2.movesToMate);
+ case GtbProbeResult.WMATE:
+ case GtbProbeResult.BMATE:
+ maxMate = Math.max(maxMate, ret2.pliesToMate);
break;
- case ProbeResult.UNKNOWN:
- ret.result = ProbeResult.UNKNOWN;
+ case GtbProbeResult.UNKNOWN:
+ ret.result = GtbProbeResult.UNKNOWN;
return ret;
}
}
if (maxMate != -1) {
- ret.result = pos.whiteMove ? ProbeResult.BMATE : ProbeResult.WMATE;
- ret.movesToMate = maxMate;
+ ret.result = pos.whiteMove ? GtbProbeResult.BMATE : GtbProbeResult.WMATE;
+ ret.pliesToMate = maxMate;
}
}
return ret;
}
- private final ProbeResult probeHardRaw(Position pos) {
+ private final GtbProbeResult gtbProbeRaw(Position pos) {
int castleMask = 0;
if (pos.a1Castle()) castleMask |= GtbProbe.A1_CASTLE;
if (pos.h1Castle()) castleMask |= GtbProbe.H1_CASTLE;
@@ -183,33 +186,81 @@ public class Probe {
whiteSquares, blackSquares, whitePieces, blackPieces,
result);
}
- ProbeResult ret = new ProbeResult();
+ GtbProbeResult ret = new GtbProbeResult();
if (res) {
switch (result[0]) {
case GtbProbe.DRAW:
- ret.result = ProbeResult.DRAW;
- ret.movesToMate = 0;
+ ret.result = GtbProbeResult.DRAW;
+ ret.pliesToMate = 0;
break;
case GtbProbe.WMATE:
- ret.result = ProbeResult.WMATE;
- ret.movesToMate = (result[1] + 1) / 2;
+ ret.result = GtbProbeResult.WMATE;
+ ret.pliesToMate = result[1];
break;
case GtbProbe.BMATE:
- ret.result = ProbeResult.BMATE;
- ret.movesToMate = (result[1] + 1) / 2;
+ ret.result = GtbProbeResult.BMATE;
+ ret.pliesToMate = result[1];
break;
default:
- ret.result = ProbeResult.UNKNOWN;
- ret.movesToMate = 0;
+ ret.result = GtbProbeResult.UNKNOWN;
+ ret.pliesToMate = 0;
break;
}
} else {
- ret.result = ProbeResult.UNKNOWN;
- ret.movesToMate = 0;
+ ret.result = GtbProbeResult.UNKNOWN;
+ ret.pliesToMate = 0;
}
return ret;
}
+ private final ProbeResult rtbProbe(Position pos) {
+ if (pos.nPieces() > 6)
+ return new ProbeResult(ProbeResult.Type.NONE, 0, 0);
+
+ rtb.initIfNeeded();
+
+ byte[] squares = new byte[64];
+ for (int sq = 0; sq < 64; sq++)
+ squares[sq] = (byte)pos.getPiece(sq);
+ int[] result = new int[2];
+ rtb.probe(squares, pos.whiteMove, pos.getEpSquare(), pos.getCastleMask(),
+ pos.halfMoveClock, pos.fullMoveCounter, result);
+ int wdl = 0;
+ if (result[1] != RtbProbe.NOINFO) {
+ int score = result[1];
+ if (score > 0) {
+ wdl = 1;
+ } else if (score < 0) {
+ wdl = -1;
+ score = -score;
+ }
+ return new ProbeResult(ProbeResult.Type.DTZ, wdl, score);
+ } else if (result[0] != RtbProbe.NOINFO) {
+ return new ProbeResult(ProbeResult.Type.WDL, result[0], 0);
+ } else {
+ return new ProbeResult(ProbeResult.Type.NONE, 0, 0);
+ }
+ }
+
+ final ProbeResult probe(Position pos) {
+ GtbProbeResult gtbRes = gtbProbe(pos);
+ if (gtbRes.result != GtbProbeResult.UNKNOWN) {
+ int wdl = 0;
+ int score = 0;
+ if (gtbRes.result == GtbProbeResult.WMATE) {
+ wdl = 1;
+ score = gtbRes.pliesToMate;
+ } else if (gtbRes.result == GtbProbeResult.BMATE) {
+ wdl = -1;
+ score = gtbRes.pliesToMate;
+ }
+ if (!pos.whiteMove)
+ wdl = -wdl;
+ return new ProbeResult(ProbeResult.Type.DTM, wdl, score);
+ }
+ return rtbProbe(pos);
+ }
+
/** Return a list of all moves in moveList that are not known to be non-optimal.
* Returns null if no legal move could be excluded. */
public final ArrayList removeNonOptimal(Position pos, ArrayList moveList) {
@@ -220,16 +271,16 @@ public class Probe {
UndoInfo ui = new UndoInfo();
for (Move m : moveList) {
pos.makeMove(m, ui);
- ProbeResult res = probeHard(pos);
+ GtbProbeResult res = gtbProbe(pos);
pos.unMakeMove(m, ui);
- if (res.result == ProbeResult.UNKNOWN) {
+ if (res.result == GtbProbeResult.UNKNOWN) {
unknownMoves.add(m);
} else {
int wScore;
- if (res.result == ProbeResult.WMATE)
- wScore = MATE0 - res.movesToMate;
- else if (res.result == ProbeResult.BMATE)
- wScore = -(MATE0 - res.movesToMate);
+ if (res.result == GtbProbeResult.WMATE)
+ wScore = MATE0 - res.pliesToMate;
+ else if (res.result == GtbProbeResult.BMATE)
+ wScore = -(MATE0 - res.pliesToMate);
else
wScore = 0;
int score = pos.whiteMove ? wScore : -wScore;
@@ -251,11 +302,11 @@ public class Probe {
/** For a given position and from square, return EGTB information
* about all legal destination squares. Return null if no information available. */
- public final ArrayList> movePieceProbe(Position pos, int fromSq) {
+ public final ArrayList> movePieceProbe(Position pos, int fromSq) {
int p = pos.getPiece(fromSq);
if ((p == Piece.EMPTY) || (pos.whiteMove != Piece.isWhite(p)))
return null;
- ArrayList> ret = new ArrayList>();
+ ArrayList> ret = new ArrayList>();
ArrayList moveList = new MoveGen().legalMoves(pos);
UndoInfo ui = new UndoInfo();
@@ -263,17 +314,18 @@ public class Probe {
if (m.from != fromSq)
continue;
pos.makeMove(m, ui);
- ProbeResult res = probeHard(pos);
+ boolean isZeroing = pos.halfMoveClock == 0;
+ ProbeResult res = probe(pos);
pos.unMakeMove(m, ui);
- if (res.result == ProbeResult.UNKNOWN)
+ if (res.type == ProbeResult.Type.NONE)
continue;
- int score = 0;
- if (res.result == ProbeResult.WMATE) {
- score = pos.whiteMove ? res.movesToMate + 1 : -res.movesToMate;
- } else if (res.result == ProbeResult.BMATE) {
- score = pos.whiteMove ? -res.movesToMate : res.movesToMate + 1;
+ res.wdl = -res.wdl;
+ if (isZeroing && (res.type == ProbeResult.Type.DTZ)) {
+ res.score = 1;
+ } else if (res.type != ProbeResult.Type.WDL) {
+ res.score++;
}
- ret.add(new Pair(m.to, score));
+ ret.add(new Pair(m.to, res));
}
return ret;
}
@@ -281,12 +333,12 @@ public class Probe {
/** For a given position and from square, return EGTB information
* about all legal alternative positions for the piece on from square.
* Return null if no information is available. */
- public final ArrayList> relocatePieceProbe(Position pos, int fromSq) {
+ public final ArrayList> relocatePieceProbe(Position pos, int fromSq) {
int p = pos.getPiece(fromSq);
if (p == Piece.EMPTY)
return null;
boolean isPawn = (Piece.makeWhite(p) == Piece.WPAWN);
- ArrayList> ret = new ArrayList>();
+ ArrayList> ret = new ArrayList>();
for (int sq = 0; sq < 64; sq++) {
if ((sq != fromSq) && (pos.getPiece(sq) != Piece.EMPTY))
continue;
@@ -294,18 +346,14 @@ public class Probe {
continue;
pos.setPiece(fromSq, Piece.EMPTY);
pos.setPiece(sq, p);
- ProbeResult res = probeHard(pos);
+ ProbeResult res = probe(pos);
pos.setPiece(sq, Piece.EMPTY);
pos.setPiece(fromSq, p);
- if (res.result == ProbeResult.UNKNOWN)
+ if (res.type == ProbeResult.Type.NONE)
continue;
- int score = 0;
- if (res.result == ProbeResult.WMATE) {
- score = res.movesToMate;
- } else if (res.result == ProbeResult.BMATE) {
- score = -res.movesToMate;
- }
- ret.add(new Pair(sq, score));
+ if (!pos.whiteMove)
+ res.wdl = -res.wdl;
+ ret.add(new Pair(sq, res));
}
return ret;
}
diff --git a/DroidFish/src/org/petero/droidfish/gtb/ProbeResult.java b/DroidFish/src/org/petero/droidfish/gtb/ProbeResult.java
new file mode 100644
index 0000000..133a9f5
--- /dev/null
+++ b/DroidFish/src/org/petero/droidfish/gtb/ProbeResult.java
@@ -0,0 +1,96 @@
+package org.petero.droidfish.gtb;
+
+/** Tablebase probe result. */
+public final class ProbeResult implements Comparable {
+ public static enum Type {
+ DTM, // score is distance (full moves) to mate, or 0
+ DTZ, // score is distance (full moves) to zeroing move, or 0
+ WDL, // score is +-1 or 0
+ NONE, // No info available, score is 0
+ }
+
+ public Type type;
+ public int wdl; // +1 if if side to move wins, 0 for draw, -1 for loss
+ public int score; // Distance to win in plies. Always >= 0.
+ // Note! Zero if side to move is checkmated.
+
+ ProbeResult(Type type, int wdl, int score) {
+ this.type = type;
+ this.wdl = wdl;
+ this.score = score;
+ }
+
+ /**
+ * Return > 0 if other is "better" than this.
+ * A win is better than a draw, which is better than a loss.
+ * A DTM win is better than a DTZ win, which is better than a WDL win.
+ * A WDL loss is better than a DTZ loss, which is better than a DTM loss.
+ */
+ @Override
+ public final int compareTo(ProbeResult other) {
+ final Type type1 = this.type;
+ final Type type2 = other.type;
+ final boolean none1 = type1 == Type.NONE;
+ final boolean none2 = type2 == Type.NONE;
+ if (none1 != none2)
+ return none2 ? -1 : 1;
+ if (none1)
+ return 0;
+ final int wdl1 = this.wdl;
+ final int wdl2 = other.wdl;
+ final boolean win1 = wdl1 > 0;
+ final boolean win2 = wdl2 > 0;
+ if (win1 != win2)
+ return win2 ? 1 : -1;
+ final boolean draw1 = wdl1 == 0;
+ final boolean draw2 = wdl2 == 0;
+ if (draw1 != draw2)
+ return draw2 ? 1 : -1;
+ final int score1 = this.score;
+ final int score2 = other.score;
+ if (win1) {
+ final boolean dtm1 = type1 == Type.DTM;
+ final boolean dtm2 = type2 == Type.DTM;
+ if (dtm1 != dtm2)
+ return dtm2 ? 1 : -1;
+ if (dtm1)
+ return -compareScore(wdl1, score1, wdl2, score2);
+ final boolean dtz1 = type1 == Type.DTZ;
+ final boolean dtz2 = type2 == Type.DTZ;
+ if (dtz1 != dtz2)
+ return dtz2 ? 1 : -1;
+ return -compareScore(wdl1, score1, wdl2, score2);
+ } else if (draw1) {
+ return 0;
+ } else {
+ final boolean wdlType1 = type1 == Type.WDL;
+ final boolean wdlType2 = type2 == Type.WDL;
+ if (wdlType1 != wdlType2)
+ return wdlType2 ? 1 : -1;
+ if (wdlType1)
+ return -compareScore(wdl1, score1, wdl2, score2);
+ final boolean dtzType1 = type1 == Type.DTZ;
+ final boolean dtzType2 = type2 == Type.DTZ;
+ if (dtzType1 != dtzType2)
+ return dtzType2 ? 1 : -1;
+ return -compareScore(wdl1, score1, wdl2, score2);
+ }
+ }
+
+ /** Return f((wdl1,score1)) - f((wdl2,score2)), where f(x) modifies
+ * the score so that larger values are better. */
+ final static int compareScore(int wdl1, int score1,
+ int wdl2, int score2) {
+ final int M = 1000;
+ if (wdl1 > 0)
+ score1 = M - score1;
+ else if (wdl1 < 0)
+ score1 = -M + score1;
+
+ if (wdl2 > 0)
+ score2 = M - score2;
+ else if (wdl2 < 0)
+ score2 = -M + score2;
+ return score1 - score2;
+ }
+}
diff --git a/DroidFish/src/org/petero/droidfish/gtb/RtbProbe.java b/DroidFish/src/org/petero/droidfish/gtb/RtbProbe.java
new file mode 100644
index 0000000..0c0bd0c
--- /dev/null
+++ b/DroidFish/src/org/petero/droidfish/gtb/RtbProbe.java
@@ -0,0 +1,95 @@
+/*
+ DroidFish - An Android chess program.
+ Copyright (C) 2011-2014 Peter Österlund, peterosterlund2@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package org.petero.droidfish.gtb;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.petero.droidfish.engine.EngineUtil;
+
+/** */
+public class RtbProbe {
+ static {
+ System.loadLibrary("rtb");
+ }
+
+ private String currTbPath = "";
+ private ConcurrentLinkedQueue tbPathQueue = new ConcurrentLinkedQueue();
+
+ RtbProbe() {
+ }
+
+ public final void setPath(String tbPath, boolean forceReload) {
+ if (forceReload || !tbPathQueue.isEmpty() || !currTbPath.equals(tbPath)) {
+ tbPathQueue.add(tbPath);
+ Thread t = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ // Sleep 0.4s to increase probability that engine
+ // is initialized before TB.
+ try { Thread.sleep(400); } catch (InterruptedException e) { }
+ initIfNeeded();
+ }
+ });
+ t.setPriority(Thread.MIN_PRIORITY);
+ t.start();
+ }
+ }
+
+ public final synchronized void initIfNeeded() {
+ String path = tbPathQueue.poll();
+ while (!tbPathQueue.isEmpty())
+ path = tbPathQueue.poll();
+ if (path != null) {
+ currTbPath = path;
+ synchronized (EngineUtil.nativeLock) {
+ init(currTbPath);
+ }
+ }
+ }
+
+ public final static int NOINFO = 1000;
+
+ /**
+ * Probe table bases.
+ * @param squares Array of length 64, see Position class.
+ * @param wtm True if white to move.
+ * @param epSq En passant square, see Position class.
+ * @param castleMask Castle mask, see Position class.
+ * @param halfMoveClock half move clock, see Position class.
+ * @param fullMoveCounter Full move counter, see Position class.
+ * @param result Two element array. Set to [wdlScore, dtzScore].
+ * The wdl score is one of: 0: Draw
+ * 1: win for side to move
+ * -1: loss for side to move
+ * NOINFO: No info available
+ * The dtz score is one of: 0: Draw
+ * x>0: Win in x plies
+ * x<0: Loss in -x plies
+ * NOINFO: No info available
+ * @return True if success.
+ */
+ public final native void probe(byte[] squares,
+ boolean wtm,
+ int epSq, int castleMask,
+ int halfMoveClock,
+ int fullMoveCounter,
+ int[] result);
+
+ private final native static boolean init(String tbPath);
+}
diff --git a/DroidFishTest/AndroidManifest.xml b/DroidFishTest/AndroidManifest.xml
index b87b05e..cc740be 100644
--- a/DroidFishTest/AndroidManifest.xml
+++ b/DroidFishTest/AndroidManifest.xml
@@ -4,7 +4,7 @@
android:versionCode="1"
android:versionName="1.0" >
-
+
+ android:label="@string/app_name"
+ android:allowBackup="true">
-
\ No newline at end of file
+
diff --git a/DroidFishTest/src/org/petero/droidfish/gtb/ProbeResultTest.java b/DroidFishTest/src/org/petero/droidfish/gtb/ProbeResultTest.java
new file mode 100644
index 0000000..514bfeb
--- /dev/null
+++ b/DroidFishTest/src/org/petero/droidfish/gtb/ProbeResultTest.java
@@ -0,0 +1,214 @@
+package org.petero.droidfish.gtb;
+
+import org.petero.droidfish.gtb.ProbeResult.Type;
+
+import junit.framework.TestCase;
+
+public class ProbeResultTest extends TestCase {
+ public ProbeResultTest() {
+ }
+
+ public void testCompareScore() {
+ assertEquals(0, ProbeResult.compareScore(0, 0, 0, 0));
+ assertEquals(0, ProbeResult.compareScore(1, 3, 1, 3));
+ assertEquals(0, ProbeResult.compareScore(-1, 4, -1, 4));
+ assertEquals(true, ProbeResult.compareScore(0, 0, 1, 1) < 0);
+ assertEquals(true, ProbeResult.compareScore(1, 1, 1, 2) > 0);
+ assertEquals(true, ProbeResult.compareScore(-1, 1, 0, 0) < 0);
+ assertEquals(true, ProbeResult.compareScore(-1, 20, -1, 10) > 0);
+ assertEquals(true, ProbeResult.compareScore(-1, 20, 1, 21) < 0);
+ assertEquals(true, ProbeResult.compareScore(-1, 20, 1, 19) < 0);
+
+ assertEquals(true, ProbeResult.compareScore(1, 0, 0, 0) > 0);
+ assertEquals(true, ProbeResult.compareScore(1, 0, 1, 1) > 0);
+ assertEquals(true, ProbeResult.compareScore(0, 0, 1, 0) < 0);
+ assertEquals(true, ProbeResult.compareScore(1, 1, 1, 0) < 0);
+
+ assertEquals(true, ProbeResult.compareScore(-1, 0, 0, 0) < 0);
+ assertEquals(true, ProbeResult.compareScore(-1, 0, -1, 1) < 0);
+ assertEquals(true, ProbeResult.compareScore(0, 0, -1, 0) > 0);
+ assertEquals(true, ProbeResult.compareScore(-1, 1, -1, 0) > 0);
+
+ assertEquals(true, ProbeResult.compareScore(-1, 0, 1, 0) < 0);
+ assertEquals(true, ProbeResult.compareScore(1, 0, -1, 0) > 0);
+ }
+
+ public void testCompareProbeResult() {
+ // NONE vs NONE
+ assertEquals(0, new ProbeResult(Type.NONE, 0, 0).compareTo(new ProbeResult(Type.NONE, 0, 0)));
+ assertEquals(0, new ProbeResult(Type.NONE, -1, 1).compareTo(new ProbeResult(Type.NONE, 1, 2)));
+ assertEquals(0, new ProbeResult(Type.NONE, 1, 2).compareTo(new ProbeResult(Type.NONE, -1, 3)));
+ assertEquals(0, new ProbeResult(Type.NONE, 1, 2).compareTo(new ProbeResult(Type.NONE, 0, 0)));
+ assertEquals(0, new ProbeResult(Type.NONE, 0, 0).compareTo(new ProbeResult(Type.NONE, 1, 2)));
+
+ // NONE vs DTM,DTZ,WDL
+ assertEquals(true, new ProbeResult(Type.NONE, 0, 0).compareTo(new ProbeResult(Type.DTM, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, 0, 0).compareTo(new ProbeResult(Type.DTZ, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, 0, 0).compareTo(new ProbeResult(Type.WDL, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, 0, 0).compareTo(new ProbeResult(Type.DTM, 1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, 0, 0).compareTo(new ProbeResult(Type.DTZ, 1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, 0, 0).compareTo(new ProbeResult(Type.WDL, 1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, 0, 0).compareTo(new ProbeResult(Type.DTM, -1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, 0, 0).compareTo(new ProbeResult(Type.DTZ, -1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, 0, 0).compareTo(new ProbeResult(Type.WDL, -1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 0, 0).compareTo(new ProbeResult(Type.NONE, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 0, 0).compareTo(new ProbeResult(Type.NONE, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 0, 0).compareTo(new ProbeResult(Type.NONE, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 1, 1).compareTo(new ProbeResult(Type.NONE, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 1, 1).compareTo(new ProbeResult(Type.NONE, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 1, 1).compareTo(new ProbeResult(Type.NONE, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 1).compareTo(new ProbeResult(Type.NONE, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, -1, 1).compareTo(new ProbeResult(Type.NONE, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, -1, 1).compareTo(new ProbeResult(Type.NONE, 0, 0)) < 0);
+
+ assertEquals(true, new ProbeResult(Type.NONE, 1, 1).compareTo(new ProbeResult(Type.DTM, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, 1, 1).compareTo(new ProbeResult(Type.DTZ, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, 1, 1).compareTo(new ProbeResult(Type.WDL, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, 1, 1).compareTo(new ProbeResult(Type.DTM, 1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, 1, 1).compareTo(new ProbeResult(Type.DTZ, 1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, 1, 1).compareTo(new ProbeResult(Type.WDL, 1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, 1, 1).compareTo(new ProbeResult(Type.DTM, -1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, 1, 1).compareTo(new ProbeResult(Type.DTZ, -1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, 1, 1).compareTo(new ProbeResult(Type.WDL, -1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 0, 0).compareTo(new ProbeResult(Type.NONE, 1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 0, 0).compareTo(new ProbeResult(Type.NONE, 1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 0, 0).compareTo(new ProbeResult(Type.NONE, 1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 1, 1).compareTo(new ProbeResult(Type.NONE, 1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 1, 1).compareTo(new ProbeResult(Type.NONE, 1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 1, 1).compareTo(new ProbeResult(Type.NONE, 1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 1).compareTo(new ProbeResult(Type.NONE, 1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, -1, 1).compareTo(new ProbeResult(Type.NONE, 1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, -1, 1).compareTo(new ProbeResult(Type.NONE, 1, 1)) < 0);
+
+ assertEquals(true, new ProbeResult(Type.NONE, -1, 1).compareTo(new ProbeResult(Type.DTM, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, -1, 1).compareTo(new ProbeResult(Type.DTZ, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, -1, 1).compareTo(new ProbeResult(Type.WDL, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, -1, 1).compareTo(new ProbeResult(Type.DTM, 1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, -1, 1).compareTo(new ProbeResult(Type.DTZ, 1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, -1, 1).compareTo(new ProbeResult(Type.WDL, 1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, -1, 1).compareTo(new ProbeResult(Type.DTM, -1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, -1, 1).compareTo(new ProbeResult(Type.DTZ, -1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.NONE, -1, 1).compareTo(new ProbeResult(Type.WDL, -1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 0, 0).compareTo(new ProbeResult(Type.NONE, -1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 0, 0).compareTo(new ProbeResult(Type.NONE, -1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 0, 0).compareTo(new ProbeResult(Type.NONE, -1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 1, 1).compareTo(new ProbeResult(Type.NONE, -1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 1, 1).compareTo(new ProbeResult(Type.NONE, -1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 1, 1).compareTo(new ProbeResult(Type.NONE, -1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 1).compareTo(new ProbeResult(Type.NONE, -1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, -1, 1).compareTo(new ProbeResult(Type.NONE, -1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, -1, 1).compareTo(new ProbeResult(Type.NONE, -1, 1)) < 0);
+
+ // DTM vs DTM
+ assertEquals(true, new ProbeResult(Type.DTM, 1, 10).compareTo(new ProbeResult(Type.DTM, 1, 10)) == 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 1, 10).compareTo(new ProbeResult(Type.DTM, 1, 11)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 1, 11).compareTo(new ProbeResult(Type.DTM, 1, 10)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 0, 0).compareTo(new ProbeResult(Type.DTM, 0, 0)) == 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 0, 0).compareTo(new ProbeResult(Type.DTM, 1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 1, 1).compareTo(new ProbeResult(Type.DTM, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 1).compareTo(new ProbeResult(Type.DTM, -1, 1)) == 0);
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 1).compareTo(new ProbeResult(Type.DTM, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 0, 0).compareTo(new ProbeResult(Type.DTM, -1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 3).compareTo(new ProbeResult(Type.DTM, -1, 3)) == 0);
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 3).compareTo(new ProbeResult(Type.DTM, -1, 5)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 5).compareTo(new ProbeResult(Type.DTM, -1, 3)) < 0);
+
+ // DTZ vs DTZ
+ assertEquals(true, new ProbeResult(Type.DTZ, 1, 10).compareTo(new ProbeResult(Type.DTZ, 1, 10)) == 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 1, 10).compareTo(new ProbeResult(Type.DTZ, 1, 11)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 1, 11).compareTo(new ProbeResult(Type.DTZ, 1, 10)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 0, 0).compareTo(new ProbeResult(Type.DTZ, 0, 0)) == 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 0, 0).compareTo(new ProbeResult(Type.DTZ, 1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 1, 1).compareTo(new ProbeResult(Type.DTZ, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, -1, 1).compareTo(new ProbeResult(Type.DTZ, -1, 1)) == 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, -1, 1).compareTo(new ProbeResult(Type.DTZ, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 0, 0).compareTo(new ProbeResult(Type.DTZ, -1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, -1, 3).compareTo(new ProbeResult(Type.DTZ, -1, 3)) == 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, -1, 3).compareTo(new ProbeResult(Type.DTZ, -1, 5)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, -1, 5).compareTo(new ProbeResult(Type.DTZ, -1, 3)) < 0);
+
+ assertEquals(true, new ProbeResult(Type.WDL, -1, 1).compareTo(new ProbeResult(Type.WDL, -1, 1)) == 0);
+ assertEquals(true, new ProbeResult(Type.WDL, -1, 1).compareTo(new ProbeResult(Type.WDL, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.WDL, -1, 1).compareTo(new ProbeResult(Type.WDL, 1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 0, 0).compareTo(new ProbeResult(Type.WDL, -1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 0, 0).compareTo(new ProbeResult(Type.WDL, 0, 0)) == 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 0, 0).compareTo(new ProbeResult(Type.WDL, 1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 1, 1).compareTo(new ProbeResult(Type.WDL, -1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 1, 1).compareTo(new ProbeResult(Type.WDL, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 1, 1).compareTo(new ProbeResult(Type.WDL, 1, 1)) == 0);
+
+ // DTM vs DTZ
+ assertEquals(true, new ProbeResult(Type.DTM, 1, 10).compareTo(new ProbeResult(Type.DTZ, 1, 11)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 1, 10).compareTo(new ProbeResult(Type.DTZ, 1, 9)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 1, 10).compareTo(new ProbeResult(Type.DTZ, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 1, 10).compareTo(new ProbeResult(Type.DTZ, -1, 11)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 1, 10).compareTo(new ProbeResult(Type.DTZ, -1, 9)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 1, 11).compareTo(new ProbeResult(Type.DTM, 1, 10)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 1, 9).compareTo(new ProbeResult(Type.DTM, 1, 10)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 0, 0).compareTo(new ProbeResult(Type.DTM, 1, 10)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, -1, 11).compareTo(new ProbeResult(Type.DTM, 1, 10)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, -1, 9).compareTo(new ProbeResult(Type.DTM, 1, 10)) > 0);
+
+ assertEquals(true, new ProbeResult(Type.DTM, 0, 0).compareTo(new ProbeResult(Type.DTZ, 0, 0)) == 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 0, 0).compareTo(new ProbeResult(Type.DTZ, 1, 3)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 0, 0).compareTo(new ProbeResult(Type.DTZ, -1, 4)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 0, 0).compareTo(new ProbeResult(Type.DTM, 0, 0)) == 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 1, 3).compareTo(new ProbeResult(Type.DTM, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, -1, 4).compareTo(new ProbeResult(Type.DTM, 0, 0)) > 0);
+
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 8).compareTo(new ProbeResult(Type.DTZ, -1, 7)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 8).compareTo(new ProbeResult(Type.DTZ, -1, 9)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 8).compareTo(new ProbeResult(Type.DTZ, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 8).compareTo(new ProbeResult(Type.DTZ, 1, 7)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 8).compareTo(new ProbeResult(Type.DTZ, 1, 9)) > 0);
+
+ // DTM vs WDL
+ assertEquals(true, new ProbeResult(Type.DTM, 1, 10).compareTo(new ProbeResult(Type.WDL, 1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 1, 10).compareTo(new ProbeResult(Type.WDL, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 1, 10).compareTo(new ProbeResult(Type.WDL, -1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 1, 1).compareTo(new ProbeResult(Type.DTM, 1, 10)) > 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 0, 0).compareTo(new ProbeResult(Type.DTM, 1, 10)) > 0);
+ assertEquals(true, new ProbeResult(Type.WDL, -1, 1).compareTo(new ProbeResult(Type.DTM, 1, 10)) > 0);
+
+ assertEquals(true, new ProbeResult(Type.DTM, 0, 0).compareTo(new ProbeResult(Type.WDL, 0, 0)) == 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 0, 0).compareTo(new ProbeResult(Type.WDL, 1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 0, 0).compareTo(new ProbeResult(Type.WDL, -1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 0, 0).compareTo(new ProbeResult(Type.DTM, 0, 0)) == 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 1, 1).compareTo(new ProbeResult(Type.DTM, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, -1, 1).compareTo(new ProbeResult(Type.DTM, 0, 0)) > 0);
+
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 8).compareTo(new ProbeResult(Type.WDL, -1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 8).compareTo(new ProbeResult(Type.WDL, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 8).compareTo(new ProbeResult(Type.WDL, 1, 1)) > 0);
+
+ // DTZ vs WDL
+ assertEquals(true, new ProbeResult(Type.DTZ, 1, 10).compareTo(new ProbeResult(Type.WDL, 1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 1, 10).compareTo(new ProbeResult(Type.WDL, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 1, 10).compareTo(new ProbeResult(Type.WDL, -1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 1, 1).compareTo(new ProbeResult(Type.DTZ, 1, 10)) > 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 0, 0).compareTo(new ProbeResult(Type.DTZ, 1, 10)) > 0);
+ assertEquals(true, new ProbeResult(Type.WDL, -1, 1).compareTo(new ProbeResult(Type.DTZ, 1, 10)) > 0);
+
+ assertEquals(true, new ProbeResult(Type.DTZ, 0, 0).compareTo(new ProbeResult(Type.WDL, 0, 0)) == 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 0, 0).compareTo(new ProbeResult(Type.WDL, 1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 0, 0).compareTo(new ProbeResult(Type.WDL, -1, 1)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 0, 0).compareTo(new ProbeResult(Type.DTZ, 0, 0)) == 0);
+ assertEquals(true, new ProbeResult(Type.WDL, 1, 1).compareTo(new ProbeResult(Type.DTZ, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.WDL, -1, 1).compareTo(new ProbeResult(Type.DTZ, 0, 0)) > 0);
+
+ assertEquals(true, new ProbeResult(Type.DTZ, -1, 8).compareTo(new ProbeResult(Type.WDL, -1, 1)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, -1, 8).compareTo(new ProbeResult(Type.WDL, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, -1, 8).compareTo(new ProbeResult(Type.WDL, 1, 1)) > 0);
+
+ // Win-in-zero and loss-in-zero
+ assertEquals(true, new ProbeResult(Type.DTM, 1, 0).compareTo(new ProbeResult(Type.DTM, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 0, 0).compareTo(new ProbeResult(Type.DTM, 1, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, -1, 0).compareTo(new ProbeResult(Type.DTM, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTM, 0, 0).compareTo(new ProbeResult(Type.DTM, -1, 0)) < 0);
+
+ assertEquals(true, new ProbeResult(Type.DTZ, 1, 0).compareTo(new ProbeResult(Type.DTZ, 0, 0)) < 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 0, 0).compareTo(new ProbeResult(Type.DTZ, 1, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, -1, 0).compareTo(new ProbeResult(Type.DTZ, 0, 0)) > 0);
+ assertEquals(true, new ProbeResult(Type.DTZ, 0, 0).compareTo(new ProbeResult(Type.DTZ, -1, 0)) < 0);
+ }
+}
diff --git a/DroidFishTest/src/org/petero/droidfish/gtb/ProbeTest.java b/DroidFishTest/src/org/petero/droidfish/gtb/ProbeTest.java
new file mode 100644
index 0000000..186a52a
--- /dev/null
+++ b/DroidFishTest/src/org/petero/droidfish/gtb/ProbeTest.java
@@ -0,0 +1,31 @@
+package org.petero.droidfish.gtb;
+
+import org.petero.droidfish.gamelogic.Position;
+import org.petero.droidfish.gamelogic.TextIO;
+
+import android.os.Environment;
+
+import junit.framework.TestCase;
+
+public class ProbeTest extends TestCase {
+ public ProbeTest() {
+ }
+
+ public void testDTZProbe() throws Throwable {
+ Probe probe = Probe.getInstance();
+ String sd = Environment.getExternalStorageDirectory().getAbsolutePath();
+ probe.setPath("", sd + "/DroidFish/rtb", true);
+
+ Position pos = TextIO.readFEN("K7/P1k2b2/8/3N4/8/8/8/8 b - - 0 1");
+ ProbeResult res = probe.probe(pos);
+ assertEquals(ProbeResult.Type.DTZ, res.type);
+ assertEquals(1, res.wdl);
+ assertEquals(1, res.score);
+
+ pos = TextIO.readFEN("8/5N2/8/8/p1N2k2/3K4/8/8 w - - 0 1"); // Draw because of 50-move rule
+ res = probe.probe(pos);
+ assertEquals(ProbeResult.Type.DTZ, res.type);
+ assertEquals(0, res.wdl);
+ assertEquals(0, res.score);
+ }
+}