DroidFish: Implemented DTZ/WDL tablebase hints in the GUI when only syzygy tablebases are available.

This commit is contained in:
Peter Osterlund
2014-10-12 00:21:10 +00:00
parent 0545004d3a
commit aa95d57e7b
33 changed files with 5312 additions and 97 deletions

View File

@@ -10,3 +10,5 @@ include $(BUILD_SHARED_LIBRARY)
include jni/stockfish/Android.mk
include jni/gtb/Android.mk
include jni/rtb/Android.mk

View File

@@ -1,3 +1,4 @@
APP_ABI := armeabi armeabi-v7a x86 mips
APP_STL := gnustl_static
APP_OPTIM := release
NDK_TOOLCHAIN_VERSION := 4.8

View File

@@ -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)

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include "RtbProbe.h"
#include "tbprobe.hpp"
#include <algorithm>
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);
}

View File

@@ -0,0 +1,29 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* 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

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
/*
* bitBoard.cpp
*
* Created on: Feb 25, 2012
* Author: petero
*/
#include "bitBoard.hpp"
#include "position.hpp"
#include <cassert>
#include <iostream>
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<U64> 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<BitBoard> 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;
}
}
}
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
/*
* bitBoard.hpp
*
* Created on: Feb 25, 2012
* Author: petero
*/
#ifndef BITBOARD_HPP_
#define BITBOARD_HPP_
#include "util.hpp"
#include <vector>
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<U64> tableData;
static const S8 dirTable[];
static const S8 kingDistTable[];
static const S8 taxiDistTable[];
static const int trailingZ[64];
};
#endif /* BITBOARD_HPP_ */

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
/*
* 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
};

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
/*
* 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_ */

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
/*
* 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_ */

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
/*
* moveGen.cpp
*
* Created on: Feb 25, 2012
* Author: petero
*/
#include "moveGen.hpp"
template void MoveGen::pseudoLegalMoves<true>(const Position& pos, MoveList& moveList);
template void MoveGen::pseudoLegalMoves<false>(const Position& pos, MoveList& moveList);
template <bool wtm>
void
MoveGen::pseudoLegalMoves(const Position& pos, MoveList& moveList) {
typedef ColorTraits<wtm> 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<wtm>(moveList, m, -8, true);
m = ((m & BitBoard::maskRow3) << 8) & ~occupied;
addPawnDoubleMovesByMask(moveList, m, -16);
m = (pawns << 7) & BitBoard::maskAToGFiles & (pos.colorBB(!wtm) | epMask);
addPawnMovesByMask<wtm>(moveList, m, -7, true);
m = (pawns << 9) & BitBoard::maskBToHFiles & (pos.colorBB(!wtm) | epMask);
addPawnMovesByMask<wtm>(moveList, m, -9, true);
} else {
U64 m = (pawns >> 8) & ~occupied;
addPawnMovesByMask<wtm>(moveList, m, 8, true);
m = ((m & BitBoard::maskRow6) >> 8) & ~occupied;
addPawnDoubleMovesByMask(moveList, m, 16);
m = (pawns >> 9) & BitBoard::maskAToGFiles & (pos.colorBB(!wtm) | epMask);
addPawnMovesByMask<wtm>(moveList, m, 9, true);
m = (pawns >> 7) & BitBoard::maskBToHFiles & (pos.colorBB(!wtm) | epMask);
addPawnMovesByMask<wtm>(moveList, m, 7, true);
}
}
template void MoveGen::checkEvasions<true>(const Position& pos, MoveList& moveList);
template void MoveGen::checkEvasions<false>(const Position& pos, MoveList& moveList);
template <bool wtm>
void
MoveGen::checkEvasions(const Position& pos, MoveList& moveList) {
typedef ColorTraits<wtm> MyColor;
typedef ColorTraits<!wtm> 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<wtm>(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<wtm>(moveList, m, -7, true);
m = (pawns << 9) & BitBoard::maskBToHFiles & ((pos.colorBB(!wtm) & validTargets) | epMask);
addPawnMovesByMask<wtm>(moveList, m, -9, true);
} else {
U64 m = (pawns >> 8) & ~occupied;
addPawnMovesByMask<wtm>(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<wtm>(moveList, m, 9, true);
m = (pawns >> 7) & BitBoard::maskBToHFiles & ((pos.colorBB(!wtm) & validTargets) | epMask);
addPawnMovesByMask<wtm>(moveList, m, 7, true);
}
}
template void MoveGen::pseudoLegalCaptures<true>(const Position& pos, MoveList& moveList);
template void MoveGen::pseudoLegalCaptures<false>(const Position& pos, MoveList& moveList);
template <bool wtm>
void
MoveGen::pseudoLegalCaptures(const Position& pos, MoveList& moveList) {
typedef ColorTraits<wtm> 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<wtm>(moveList, m, -8, false);
m = (pawns << 7) & BitBoard::maskAToGFiles & (pos.colorBB(!wtm) | epMask);
addPawnMovesByMask<wtm>(moveList, m, -7, false);
m = (pawns << 9) & BitBoard::maskBToHFiles & (pos.colorBB(!wtm) | epMask);
addPawnMovesByMask<wtm>(moveList, m, -9, false);
} else {
m = (pawns >> 8) & ~occupied;
m &= BitBoard::maskRow1;
addPawnMovesByMask<wtm>(moveList, m, 8, false);
m = (pawns >> 9) & BitBoard::maskAToGFiles & (pos.colorBB(!wtm) | epMask);
addPawnMovesByMask<wtm>(moveList, m, 9, false);
m = (pawns >> 7) & BitBoard::maskBToHFiles & (pos.colorBB(!wtm) | epMask);
addPawnMovesByMask<wtm>(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<<m.from());
return !MoveGen::sqAttacked(pos, m.to(), occupied);
} else {
if (m.to() != epSquare) {
U64 occupied = pos.occupiedBB();
U64 fromMask = 1ULL << m.from();
if (((BitBoard::rookAttacks(kSq, occupied) & fromMask) == 0) &&
((BitBoard::bishopAttacks(kSq, occupied) & fromMask) == 0))
return true;
else if (BitBoard::getDirection(kSq, m.from()) == BitBoard::getDirection(kSq, m.to()))
return true;
}
pos.makeMove(m, ui);
bool legal = !canTakeKing(pos);
pos.unMakeMove(m, ui);
return legal;
}
}
}

View File

@@ -0,0 +1,216 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/*
* 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 <cassert>
//#define MOVELIST_DEBUG
#ifdef MOVELIST_DEBUG
# include <set>
# 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 <bool wtm>
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 <bool wtm>
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 <bool wtm>
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<true>(pos, sq, occupied)
: sqAttacked<false>(pos, sq, occupied);
}
template <bool wtm>
static bool sqAttacked(const Position& pos, int sq, U64 occupied) {
typedef ColorTraits<!wtm> 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 <bool wtm>
static void addPawnMovesByMask(MoveList& moveList, U64 mask, int delta, bool allPromotions) {
typedef ColorTraits<wtm> 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<true>(pos, moveList);
else
pseudoLegalMoves<false>(pos, moveList);
}
inline void
MoveGen::checkEvasions(const Position& pos, MoveList& moveList) {
if (pos.isWhiteMove())
checkEvasions<true>(pos, moveList);
else
checkEvasions<false>(pos, moveList);
}
inline void
MoveGen::pseudoLegalCaptures(const Position& pos, MoveList& moveList) {
if (pos.isWhiteMove())
pseudoLegalCaptures<true>(pos, moveList);
else
pseudoLegalCaptures<false>(pos, moveList);
}
#endif /* MOVEGEN_HPP_ */

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
/*
* 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 <bool wtm> struct ColorTraits {
};
template<> struct ColorTraits<true> {
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<false> {
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_ */

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
/*
* 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;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
/*
* 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 <algorithm>
#include <iostream>
/**
* 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 <typename Piece0, typename... Pieces> 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 <typename Piece0, typename... Pieces>
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_ */

File diff suppressed because it is too large Load Diff

View File

@@ -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 <windows.h>
#define SEP_CHAR ';'
#define FD HANDLE
#define FD_ERR INVALID_HANDLE_VALUE
#endif
#include <stdint.h>
#include <atomic>
#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<ubyte> ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
} __attribute__((__may_alias__));
struct TBEntry_piece {
char *data;
uint64_t key;
uint64_t mapping;
std::atomic<ubyte> 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<ubyte> 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<ubyte> 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<ubyte> 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<TBEntry*> entry;
};
#endif

View File

@@ -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 <type_traits>
#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<std::mutex> 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<std::mutex> 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;
}

View File

@@ -0,0 +1,54 @@
#ifndef RTB_PROBE_HPP_
#define RTB_PROBE_HPP_
#include <string>
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

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
/*
* 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 <unordered_map>
#include <cassert>
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;
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
/*
* tbprobe.hpp
*
* Created on: Jun 2, 2014
* Author: petero
*/
#ifndef TBPROBE_HPP_
#define TBPROBE_HPP_
#include "moveGen.hpp"
#include <string>
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_ */

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
/*
* 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_ */

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
/*
* util.hpp
*
* Created on: Feb 26, 2012
* Author: petero
*/
#ifndef UTIL_HPP_
#define UTIL_HPP_
#include <cstdint>
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 <typename T>
class StaticInitializer {
public:
StaticInitializer() {
T::staticInitialize();
}
};
#endif /* UTIL_HPP_ */

View File

@@ -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<Move> 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<SquareDecoration> {
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<SquareDecoration> 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);
}
}
}

View File

@@ -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<Pair<Integer, Integer>> x = gtbProbe.movePieceProbe(cb.pos, sq);
ArrayList<Pair<Integer,ProbeResult>> x = gtbProbe.movePieceProbe(cb.pos, sq);
if (x == null) {
cb.setSquareDecorations(null);
return;
}
ArrayList<SquareDecoration> sd = new ArrayList<SquareDecoration>();
for (Pair<Integer,Integer> p : x)
for (Pair<Integer,ProbeResult> p : x)
sd.add(new SquareDecoration(p.first, p.second));
cb.setSquareDecorations(sd);
}

View File

@@ -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<Pair<Integer, Integer>> x = gtbProbe.relocatePieceProbe(cb.pos, sq);
ArrayList<Pair<Integer,ProbeResult>> x = gtbProbe.relocatePieceProbe(cb.pos, sq);
if (x == null) {
cb.setSquareDecorations(null);
return;
}
ArrayList<SquareDecoration> sd = new ArrayList<SquareDecoration>();
for (Pair<Integer,Integer> p : x)
for (Pair<Integer,ProbeResult> p : x)
sd.add(new SquareDecoration(p.first, p.second));
cb.setSquareDecorations(sd);
}

View File

@@ -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;
}

View File

@@ -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<Move> 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<Move> removeNonOptimal(Position pos, ArrayList<Move> 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<Pair<Integer,Integer>> movePieceProbe(Position pos, int fromSq) {
public final ArrayList<Pair<Integer,ProbeResult>> movePieceProbe(Position pos, int fromSq) {
int p = pos.getPiece(fromSq);
if ((p == Piece.EMPTY) || (pos.whiteMove != Piece.isWhite(p)))
return null;
ArrayList<Pair<Integer,Integer>> ret = new ArrayList<Pair<Integer,Integer>>();
ArrayList<Pair<Integer,ProbeResult>> ret = new ArrayList<Pair<Integer,ProbeResult>>();
ArrayList<Move> 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<Integer,Integer>(m.to, score));
ret.add(new Pair<Integer,ProbeResult>(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<Pair<Integer, Integer>> relocatePieceProbe(Position pos, int fromSq) {
public final ArrayList<Pair<Integer,ProbeResult>> relocatePieceProbe(Position pos, int fromSq) {
int p = pos.getPiece(fromSq);
if (p == Piece.EMPTY)
return null;
boolean isPawn = (Piece.makeWhite(p) == Piece.WPAWN);
ArrayList<Pair<Integer,Integer>> ret = new ArrayList<Pair<Integer,Integer>>();
ArrayList<Pair<Integer,ProbeResult>> ret = new ArrayList<Pair<Integer,ProbeResult>>();
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<Integer,Integer>(sq, score));
if (!pos.whiteMove)
res.wdl = -res.wdl;
ret.add(new Pair<Integer,ProbeResult>(sq, res));
}
return ret;
}

View File

@@ -0,0 +1,96 @@
package org.petero.droidfish.gtb;
/** Tablebase probe result. */
public final class ProbeResult implements Comparable<ProbeResult> {
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;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<String> tbPathQueue = new ConcurrentLinkedQueue<String>();
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);
}

View File

@@ -4,7 +4,7 @@
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="3" />
<uses-sdk android:minSdkVersion="4" />
<instrumentation
android:name="android.test.InstrumentationTestRunner"
@@ -12,8 +12,9 @@
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
android:label="@string/app_name"
android:allowBackup="true">
<uses-library android:name="android.test.runner" />
</application>
</manifest>
</manifest>

View File

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

View File

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