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); + } +}