mirror of
https://github.com/HChaZZY/Stockfish.git
synced 2025-12-19 16:46:30 +08:00
Compare commits
143 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa2de53a83 | ||
|
|
f826923f8e | ||
|
|
3201a43460 | ||
|
|
5405efabcb | ||
|
|
6df86fc9da | ||
|
|
7315001f37 | ||
|
|
191662a159 | ||
|
|
a01df59f5e | ||
|
|
9f3c7ded5c | ||
|
|
df73af30dd | ||
|
|
9394db765f | ||
|
|
4d6258bb32 | ||
|
|
3e8bb6f63d | ||
|
|
3835f49aa1 | ||
|
|
8e75384dd2 | ||
|
|
db4a68a99b | ||
|
|
f803f33e63 | ||
|
|
6f2e4c006c | ||
|
|
afe0203f98 | ||
|
|
b01e5fc612 | ||
|
|
d2a4aac53d | ||
|
|
58c6e64069 | ||
|
|
3346ccc9d9 | ||
|
|
0007b43a91 | ||
|
|
6235904898 | ||
|
|
5b08494312 | ||
|
|
30c14fdc95 | ||
|
|
e8b5420300 | ||
|
|
12eb27b6e9 | ||
|
|
e7ab3a0d16 | ||
|
|
4dded4e72f | ||
|
|
cb7f20913e | ||
|
|
f08a6eed0d | ||
|
|
6080fecf9c | ||
|
|
94a67c49b0 | ||
|
|
0a73a23dc9 | ||
|
|
a0f0a7dc4f | ||
|
|
61c03b9d22 | ||
|
|
dee8780829 | ||
|
|
d40a12f948 | ||
|
|
f97c5b6909 | ||
|
|
d55a5a4d81 | ||
|
|
201e8d5f87 | ||
|
|
47f5560e2d | ||
|
|
cf85ffbb97 | ||
|
|
6596dc9516 | ||
|
|
6afcfd00f2 | ||
|
|
2d63f2157e | ||
|
|
56de5ae561 | ||
|
|
556b63b6b6 | ||
|
|
4ad03c9bad | ||
|
|
dedc6d7588 | ||
|
|
38e7ec3e44 | ||
|
|
421f7b74c6 | ||
|
|
0da461f23b | ||
|
|
97a6e1559e | ||
|
|
8a858aea34 | ||
|
|
03dd1d3c13 | ||
|
|
5529706426 | ||
|
|
6ed409ecee | ||
|
|
200fc56e9c | ||
|
|
d0dc05ad41 | ||
|
|
9ecdfd2401 | ||
|
|
5d6b2f2144 | ||
|
|
efeb37c33f | ||
|
|
00d9fe8af0 | ||
|
|
85df24624a | ||
|
|
f44aea7508 | ||
|
|
fa80479b1d | ||
|
|
df6ba1fa5c | ||
|
|
f57d51b7f3 | ||
|
|
358ccf206b | ||
|
|
24e6ed907b | ||
|
|
bdfd656c24 | ||
|
|
d08a8d76f7 | ||
|
|
6f70e762a9 | ||
|
|
b36900ef44 | ||
|
|
a1c02815cc | ||
|
|
660378d10e | ||
|
|
9dcc2aad98 | ||
|
|
fad595f5b6 | ||
|
|
d2d953713f | ||
|
|
c28b9ef182 | ||
|
|
da6e2b5fd1 | ||
|
|
b06f0460a2 | ||
|
|
d2ad5acddd | ||
|
|
4cd53b68d0 | ||
|
|
8fb16df70e | ||
|
|
f5e28ef512 | ||
|
|
287556f97d | ||
|
|
469e7c5143 | ||
|
|
bacb645939 | ||
|
|
fb50e16cdd | ||
|
|
9f626725ae | ||
|
|
cfca92cd7c | ||
|
|
d607febb38 | ||
|
|
19cf779629 | ||
|
|
d74025a34e | ||
|
|
49a6fee4fa | ||
|
|
2991ff0dc2 | ||
|
|
c416133e2f | ||
|
|
ff95bbd41f | ||
|
|
2d7a417d0a | ||
|
|
f790752daa | ||
|
|
5254ca22f3 | ||
|
|
5b445cdf59 | ||
|
|
96e589646d | ||
|
|
c81bf3743f | ||
|
|
f6e11ee2a3 | ||
|
|
65606bc49e | ||
|
|
3b7bf34b02 | ||
|
|
141caf1d5b | ||
|
|
c59efc53c9 | ||
|
|
8fdc635255 | ||
|
|
472971f851 | ||
|
|
389edb8099 | ||
|
|
13d8231746 | ||
|
|
9440fb06da | ||
|
|
3a564ed5db | ||
|
|
1fdb436e78 | ||
|
|
dcf2edfdea | ||
|
|
85a7456bd7 | ||
|
|
d664773a83 | ||
|
|
f092667460 | ||
|
|
19ff8e2902 | ||
|
|
a7f4ee7540 | ||
|
|
f7722d4de7 | ||
|
|
37055ad002 | ||
|
|
79a7647fe0 | ||
|
|
00950fec00 | ||
|
|
7c7a77698a | ||
|
|
083ed1ce94 | ||
|
|
2feeb206ff | ||
|
|
5dfbbb79be | ||
|
|
d440ddb487 | ||
|
|
9c9914d72a | ||
|
|
a0474a72a6 | ||
|
|
7733dadfd7 | ||
|
|
9ca4359f36 | ||
|
|
f00c976bb2 | ||
|
|
1bbbc13b46 | ||
|
|
1ee1d852fe | ||
|
|
812e843939 |
35
src/Makefile
35
src/Makefile
@@ -33,10 +33,9 @@ BINDIR = $(PREFIX)/bin
|
||||
PGOBENCH = ./$(EXE) bench 32 1 10 default depth
|
||||
|
||||
### Object files
|
||||
OBJS = application.o bitboard.o pawns.o material.o endgame.o evaluate.o main.o \
|
||||
misc.o move.o movegen.o history.o movepick.o search.o piece.o \
|
||||
position.o direction.o tt.o uci.o ucioption.o \
|
||||
mersenne.o book.o bitbase.o san.o benchmark.o timeman.o
|
||||
OBJS = bitboard.o pawns.o material.o endgame.o evaluate.o main.o \
|
||||
misc.o move.o movegen.o history.o movepick.o search.o position.o \
|
||||
tt.o uci.o ucioption.o book.o bitbase.o san.o benchmark.o timeman.o
|
||||
|
||||
|
||||
### ==========================================================================
|
||||
@@ -200,6 +199,15 @@ ifeq ($(COMP),)
|
||||
COMP=gcc
|
||||
endif
|
||||
|
||||
ifeq ($(COMP),mingw)
|
||||
comp=mingw
|
||||
CXX=g++
|
||||
profile_prepare = gcc-profile-prepare
|
||||
profile_make = gcc-profile-make
|
||||
profile_use = gcc-profile-use
|
||||
profile_clean = gcc-profile-clean
|
||||
endif
|
||||
|
||||
ifeq ($(COMP),gcc)
|
||||
comp=gcc
|
||||
CXX=g++
|
||||
@@ -219,14 +227,18 @@ ifeq ($(COMP),icc)
|
||||
endif
|
||||
|
||||
### 3.2 General compiler settings
|
||||
CXXFLAGS = -g -Wall -Wcast-qual -ansi -fno-exceptions -fno-rtti $(EXTRACXXFLAGS)
|
||||
CXXFLAGS = -g -Wall -Wcast-qual -fno-exceptions -fno-rtti $(EXTRACXXFLAGS)
|
||||
|
||||
ifeq ($(comp),gcc)
|
||||
CXXFLAGS += -pedantic -Wno-long-long -Wextra
|
||||
CXXFLAGS += -ansi -pedantic -Wno-long-long -Wextra
|
||||
endif
|
||||
|
||||
ifeq ($(comp),mingw)
|
||||
CXXFLAGS += -Wno-long-long -Wextra
|
||||
endif
|
||||
|
||||
ifeq ($(comp),icc)
|
||||
CXXFLAGS += -wd383,869,981,10187,10188,11505,11503
|
||||
CXXFLAGS += -wd383,981,1418,1419,10187,10188,11505,11503 -Wcheck -Wabi -Wdeprecated -strict-ansi
|
||||
endif
|
||||
|
||||
ifeq ($(os),osx)
|
||||
@@ -261,6 +273,10 @@ ifeq ($(optimize),yes)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(comp),mingw)
|
||||
CXXFLAGS += -O3
|
||||
endif
|
||||
|
||||
ifeq ($(comp),icc)
|
||||
CXXFLAGS += -fast
|
||||
|
||||
@@ -340,6 +356,7 @@ help:
|
||||
@echo ""
|
||||
@echo "gcc > Gnu compiler (default)"
|
||||
@echo "icc > Intel compiler"
|
||||
@echo "mingw > Gnu compiler with MinGW under Windows"
|
||||
@echo ""
|
||||
@echo "Non-standard targets:"
|
||||
@echo ""
|
||||
@@ -412,7 +429,7 @@ install:
|
||||
-strip $(BINDIR)/$(EXE)
|
||||
|
||||
clean:
|
||||
$(RM) $(EXE) *.o .depend *~ core bench.txt *.gcda
|
||||
$(RM) $(EXE) $(EXE).exe *.o .depend *~ core bench.txt *.gcda
|
||||
|
||||
testrun:
|
||||
@$(PGOBENCH)
|
||||
@@ -453,7 +470,7 @@ config-sanity:
|
||||
@test "$(prefetch)" = "yes" || test "$(prefetch)" = "no"
|
||||
@test "$(bsfq)" = "yes" || test "$(bsfq)" = "no"
|
||||
@test "$(popcnt)" = "yes" || test "$(popcnt)" = "no"
|
||||
@test "$(comp)" = "gcc" || test "$(comp)" = "icc"
|
||||
@test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw"
|
||||
|
||||
$(EXE): $(OBJS)
|
||||
$(CXX) -o $@ $(OBJS) $(LDFLAGS)
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
|
||||
Stockfish 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.
|
||||
|
||||
Stockfish 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/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "direction.h"
|
||||
#include "endgame.h"
|
||||
#include "evaluate.h"
|
||||
#include "material.h"
|
||||
#include "mersenne.h"
|
||||
#include "misc.h"
|
||||
#include "movepick.h"
|
||||
#include "position.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
#include "ucioption.h"
|
||||
|
||||
|
||||
/// Application class is in charge of initializing global resources
|
||||
/// at startup and cleanly releases them when program terminates.
|
||||
|
||||
Application::Application() {
|
||||
|
||||
init_mersenne();
|
||||
init_direction_table();
|
||||
init_bitboards();
|
||||
init_uci_options();
|
||||
Position::init_zobrist();
|
||||
Position::init_piece_square_tables();
|
||||
init_eval(1);
|
||||
init_bitbases();
|
||||
init_search();
|
||||
init_threads();
|
||||
|
||||
// Make random number generation less deterministic, for book moves
|
||||
for (int i = abs(get_system_time() % 10000); i > 0; i--)
|
||||
genrand_int32();
|
||||
}
|
||||
|
||||
void Application::initialize() {
|
||||
|
||||
// A static Application object is allocated
|
||||
// once only when this function is called.
|
||||
static Application singleton;
|
||||
}
|
||||
|
||||
void Application::free_resources() {
|
||||
|
||||
// Warning, following functions reference global objects that
|
||||
// must be still alive when free_resources() is called.
|
||||
exit_threads();
|
||||
quit_eval();
|
||||
}
|
||||
|
||||
void Application::exit_with_failure() {
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
|
||||
Stockfish 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.
|
||||
|
||||
Stockfish 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(APPLICATION_H_INCLUDED)
|
||||
#define APPLICATION_H_INCLUDED
|
||||
|
||||
|
||||
/// Singleton class used to housekeep memory and global resources
|
||||
/// so to be sure we always leave in a clean state.
|
||||
|
||||
class Application {
|
||||
|
||||
Application();
|
||||
Application(const Application&);
|
||||
|
||||
public:
|
||||
static void initialize();
|
||||
static void free_resources();
|
||||
static void exit_with_failure();
|
||||
};
|
||||
|
||||
#endif // !defined(APPLICATION_H_INCLUDED)
|
||||
@@ -22,10 +22,8 @@
|
||||
//// Includes
|
||||
////
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "benchmark.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
#include "ucioption.h"
|
||||
@@ -36,7 +34,7 @@ using namespace std;
|
||||
//// Variables
|
||||
////
|
||||
|
||||
const string BenchmarkPositions[] = {
|
||||
static const string BenchmarkPositions[] = {
|
||||
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
|
||||
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -",
|
||||
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -",
|
||||
@@ -52,7 +50,8 @@ const string BenchmarkPositions[] = {
|
||||
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
|
||||
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
|
||||
"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22",
|
||||
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26"
|
||||
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26",
|
||||
""
|
||||
};
|
||||
|
||||
|
||||
@@ -61,63 +60,49 @@ const string BenchmarkPositions[] = {
|
||||
////
|
||||
|
||||
/// benchmark() runs a simple benchmark by letting Stockfish analyze a set
|
||||
/// of positions for a given time each. There are four parameters; the
|
||||
/// of positions for a given limit each. There are five parameters; the
|
||||
/// transposition table size, the number of search threads that should
|
||||
/// be used, the time in seconds spent for each position (optional, default
|
||||
/// is 60) and an optional file name where to look for positions in fen
|
||||
/// format (default are the BenchmarkPositions defined above).
|
||||
/// be used, the limit value spent for each position (optional, default
|
||||
/// is ply 12), an optional file name where to look for positions in fen
|
||||
/// format (default are the BenchmarkPositions defined above) and the type
|
||||
/// of the limit value: depth (default), time in secs or number of nodes.
|
||||
/// The analysis is written to a file named bench.txt.
|
||||
|
||||
void benchmark(const string& commandLine) {
|
||||
void benchmark(int argc, char* argv[]) {
|
||||
|
||||
istringstream csVal(commandLine);
|
||||
istringstream csStr(commandLine);
|
||||
string ttSize, threads, fileName, limitType, timFile;
|
||||
vector<string> positions;
|
||||
string ttSize, threads, valStr, posFile, valType;
|
||||
int val, secsPerPos, maxDepth, maxNodes;
|
||||
|
||||
csStr >> ttSize;
|
||||
csVal >> val;
|
||||
if (val < 4 || val > 1024)
|
||||
{
|
||||
cerr << "The hash table size must be between 4 and 1024" << endl;
|
||||
Application::exit_with_failure();
|
||||
}
|
||||
csStr >> threads;
|
||||
csVal >> val;
|
||||
if (val < 1 || val > MAX_THREADS)
|
||||
{
|
||||
cerr << "The number of threads must be between 1 and " << MAX_THREADS << endl;
|
||||
Application::exit_with_failure();
|
||||
}
|
||||
set_option_value("Hash", ttSize);
|
||||
set_option_value("Threads", threads);
|
||||
set_option_value("OwnBook", "false");
|
||||
set_option_value("Use Search Log", "true");
|
||||
set_option_value("Search Log Filename", "bench.txt");
|
||||
ttSize = argc > 2 ? argv[2] : "128";
|
||||
threads = argc > 3 ? argv[3] : "1";
|
||||
valStr = argc > 4 ? argv[4] : "12";
|
||||
posFile = argc > 5 ? argv[5] : "default";
|
||||
valType = argc > 6 ? argv[6] : "depth";
|
||||
|
||||
csVal >> val;
|
||||
csVal >> fileName;
|
||||
csVal >> limitType;
|
||||
csVal >> timFile;
|
||||
Options["Hash"].set_value(ttSize);
|
||||
Options["Threads"].set_value(threads);
|
||||
Options["OwnBook"].set_value("false");
|
||||
Options["Use Search Log"].set_value("true");
|
||||
Options["Search Log Filename"].set_value("bench.txt");
|
||||
|
||||
secsPerPos = maxDepth = maxNodes = 0;
|
||||
val = atoi(valStr.c_str());
|
||||
|
||||
if (limitType == "time")
|
||||
secsPerPos = val * 1000;
|
||||
else if (limitType == "depth" || limitType == "perft")
|
||||
if (valType == "depth" || valType == "perft")
|
||||
maxDepth = val;
|
||||
else if (valType == "time")
|
||||
secsPerPos = val * 1000;
|
||||
else
|
||||
maxNodes = val;
|
||||
|
||||
vector<string> positions;
|
||||
|
||||
if (fileName != "default")
|
||||
if (posFile != "default")
|
||||
{
|
||||
ifstream fenFile(fileName.c_str());
|
||||
ifstream fenFile(posFile.c_str());
|
||||
if (!fenFile.is_open())
|
||||
{
|
||||
cerr << "Unable to open positions file " << fileName << endl;
|
||||
Application::exit_with_failure();
|
||||
cerr << "Unable to open positions file " << posFile << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
string pos;
|
||||
while (fenFile.good())
|
||||
@@ -128,19 +113,8 @@ void benchmark(const string& commandLine) {
|
||||
}
|
||||
fenFile.close();
|
||||
} else
|
||||
for (int i = 0; i < 16; i++)
|
||||
positions.push_back(string(BenchmarkPositions[i]));
|
||||
|
||||
ofstream timingFile;
|
||||
if (!timFile.empty())
|
||||
{
|
||||
timingFile.open(timFile.c_str(), ios::out | ios::app);
|
||||
if (!timingFile.is_open())
|
||||
{
|
||||
cerr << "Unable to open timing file " << timFile << endl;
|
||||
Application::exit_with_failure();
|
||||
}
|
||||
}
|
||||
for (int i = 0; !BenchmarkPositions[i].empty(); i++)
|
||||
positions.push_back(BenchmarkPositions[i]);
|
||||
|
||||
vector<string>::iterator it;
|
||||
int cnt = 1;
|
||||
@@ -149,11 +123,11 @@ void benchmark(const string& commandLine) {
|
||||
|
||||
for (it = positions.begin(); it != positions.end(); ++it, ++cnt)
|
||||
{
|
||||
Move moves[1] = {MOVE_NONE};
|
||||
int dummy[2] = {0, 0};
|
||||
Move moves[1] = { MOVE_NONE };
|
||||
int dummy[2] = { 0, 0 };
|
||||
Position pos(*it, 0);
|
||||
cerr << "\nBench position: " << cnt << '/' << positions.size() << endl << endl;
|
||||
if (limitType == "perft")
|
||||
if (valType == "perft")
|
||||
{
|
||||
int64_t perftCnt = perft(pos, maxDepth * ONE_PLY);
|
||||
cerr << "\nPerft " << maxDepth << " result (nodes searched): " << perftCnt << endl << endl;
|
||||
@@ -161,7 +135,7 @@ void benchmark(const string& commandLine) {
|
||||
} else {
|
||||
if (!think(pos, false, false, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves))
|
||||
break;
|
||||
totalNodes += nodes_searched();
|
||||
totalNodes += pos.nodes_searched();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,16 +145,10 @@ void benchmark(const string& commandLine) {
|
||||
<< "\nNodes searched : " << totalNodes
|
||||
<< "\nNodes/second : " << (int)(totalNodes/(cnt/1000.0)) << endl << endl;
|
||||
|
||||
if (!timFile.empty())
|
||||
{
|
||||
timingFile << cnt << endl << endl;
|
||||
timingFile.close();
|
||||
}
|
||||
|
||||
// Under MS Visual C++ debug window always unconditionally closes
|
||||
// when program exits, this is bad because we want to read results before.
|
||||
#if (defined(WINDOWS) || defined(WIN32) || defined(WIN64))
|
||||
cerr << "Press any key to exit" << endl;
|
||||
cin >> fileName;
|
||||
cin >> ttSize;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
|
||||
Stockfish 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.
|
||||
|
||||
Stockfish 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(BENCHMARK_H_INCLUDED)
|
||||
#define BENCHMARK_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern void benchmark(const std::string& commandLine);
|
||||
|
||||
#endif // !defined(BENCHMARK_H_INCLUDED)
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "bitbase.h"
|
||||
#include "bitboard.h"
|
||||
#include "square.h"
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
|
||||
Stockfish 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.
|
||||
|
||||
Stockfish 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(BITBASE_H_INCLUDED)
|
||||
#define BITBASE_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "types.h"
|
||||
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern void generate_kpk_bitbase(uint8_t bitbase[]);
|
||||
|
||||
|
||||
#endif // !defined(BITBASE_H_INCLUDED)
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "bitcount.h"
|
||||
#include "direction.h"
|
||||
|
||||
|
||||
#if defined(IS_64BIT)
|
||||
@@ -226,7 +225,6 @@ Bitboard SetMaskBB[65];
|
||||
Bitboard ClearMaskBB[65];
|
||||
|
||||
Bitboard StepAttackBB[16][64];
|
||||
Bitboard RayBB[64][8];
|
||||
Bitboard BetweenBB[64][64];
|
||||
|
||||
Bitboard SquaresInFrontMask[2][64];
|
||||
@@ -247,10 +245,10 @@ uint8_t BitCount8Bit[256];
|
||||
namespace {
|
||||
|
||||
void init_masks();
|
||||
void init_ray_bitboards();
|
||||
void init_attacks();
|
||||
void init_between_bitboards();
|
||||
void init_pseudo_attacks();
|
||||
SquareDelta squares_delta(Square orig, Square dest);
|
||||
Bitboard index_to_bitboard(int index, Bitboard mask);
|
||||
Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
|
||||
int fmin, int fmax, int rmin, int rmax);
|
||||
@@ -289,7 +287,6 @@ void init_bitboards() {
|
||||
int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
|
||||
|
||||
init_masks();
|
||||
init_ray_bitboards();
|
||||
init_attacks();
|
||||
init_between_bitboards();
|
||||
init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift, RMult, rookDeltas);
|
||||
@@ -403,25 +400,10 @@ namespace {
|
||||
AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
|
||||
}
|
||||
|
||||
for (Bitboard b = 0ULL; b < 256ULL; b++)
|
||||
for (Bitboard b = 0; b < 256; b++)
|
||||
BitCount8Bit[b] = (uint8_t)count_1s<CNT32>(b);
|
||||
}
|
||||
|
||||
int remove_bit_8(int i) { return ((i & ~15) >> 1) | (i & 7); }
|
||||
|
||||
void init_ray_bitboards() {
|
||||
|
||||
int d[8] = {1, -1, 16, -16, 17, -17, 15, -15};
|
||||
|
||||
for (int i = 0; i < 128; i = (i + 9) & ~8)
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
RayBB[remove_bit_8(i)][j] = EmptyBoardBB;
|
||||
for (int k = i + d[j]; (k & 0x88) == 0; k += d[j])
|
||||
set_bit(&(RayBB[remove_bit_8(i)][j]), Square(remove_bit_8(k)));
|
||||
}
|
||||
}
|
||||
|
||||
void init_attacks() {
|
||||
|
||||
const int step[16][8] = {
|
||||
@@ -472,24 +454,42 @@ namespace {
|
||||
return result;
|
||||
}
|
||||
|
||||
SquareDelta squares_delta(Square orig, Square dest) {
|
||||
|
||||
const SquareDelta deltas[] = { DELTA_N, DELTA_NE, DELTA_E, DELTA_SE,
|
||||
DELTA_S, DELTA_SW, DELTA_W, DELTA_NW };
|
||||
|
||||
for (int idx = 0; idx < 8; idx++)
|
||||
{
|
||||
Square s = orig + deltas[idx];
|
||||
|
||||
while (square_is_ok(s) && square_distance(s, s - deltas[idx]) == 1)
|
||||
{
|
||||
if (s == dest)
|
||||
return deltas[idx];
|
||||
|
||||
s += deltas[idx];
|
||||
}
|
||||
}
|
||||
return DELTA_NONE;
|
||||
}
|
||||
|
||||
void init_between_bitboards() {
|
||||
|
||||
const SquareDelta step[8] = { DELTA_E, DELTA_W, DELTA_N, DELTA_S,
|
||||
DELTA_NE, DELTA_SW, DELTA_NW, DELTA_SE };
|
||||
Square s1, s2, s3;
|
||||
SquareDelta d;
|
||||
|
||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
|
||||
for (s1 = SQ_A1; s1 <= SQ_H8; s1++)
|
||||
for (s2 = SQ_A1; s2 <= SQ_H8; s2++)
|
||||
{
|
||||
BetweenBB[s1][s2] = EmptyBoardBB;
|
||||
SignedDirection d = signed_direction_between_squares(s1, s2);
|
||||
d = squares_delta(s1, s2);
|
||||
|
||||
if (d != SIGNED_DIR_NONE)
|
||||
{
|
||||
for (Square s3 = s1 + step[d]; s3 != s2; s3 += step[d])
|
||||
if (d != DELTA_NONE)
|
||||
for (s3 = s1 + d; s3 != s2; s3 += d)
|
||||
set_bit(&(BetweenBB[s1][s2]), s3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bitboard index_to_bitboard(int index, Bitboard mask) {
|
||||
|
||||
@@ -511,7 +511,7 @@ namespace {
|
||||
for (int i = 0, index = 0; i < 64; i++)
|
||||
{
|
||||
attackIndex[i] = index;
|
||||
mask[i] = sliding_attacks(i, 0ULL, 4, deltas, 1, 6, 1, 6);
|
||||
mask[i] = sliding_attacks(i, 0, 4, deltas, 1, 6, 1, 6);
|
||||
|
||||
#if defined(IS_64BIT)
|
||||
int j = (1 << (64 - shift[i]));
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "direction.h"
|
||||
#include "piece.h"
|
||||
#include "square.h"
|
||||
#include "types.h"
|
||||
@@ -68,7 +67,6 @@ extern Bitboard SetMaskBB[65];
|
||||
extern Bitboard ClearMaskBB[65];
|
||||
|
||||
extern Bitboard StepAttackBB[16][64];
|
||||
extern Bitboard RayBB[64][8];
|
||||
extern Bitboard BetweenBB[64][64];
|
||||
|
||||
extern Bitboard SquaresInFrontMask[2][64];
|
||||
@@ -209,14 +207,6 @@ inline Bitboard behind_bb(Color c, Square s) {
|
||||
}
|
||||
|
||||
|
||||
/// ray_bb() gives a bitboard representing all squares along the ray in a
|
||||
/// given direction from a given square.
|
||||
|
||||
inline Bitboard ray_bb(Square s, SignedDirection d) {
|
||||
return RayBB[s][d];
|
||||
}
|
||||
|
||||
|
||||
/// Functions for computing sliding attack bitboards. rook_attacks_bb(),
|
||||
/// bishop_attacks_bb() and queen_attacks_bb() all take a square and a
|
||||
/// bitboard of occupied squares as input, and return a bitboard representing
|
||||
@@ -307,6 +297,15 @@ inline Bitboard attack_span_mask(Color c, Square s) {
|
||||
}
|
||||
|
||||
|
||||
/// squares_aligned returns true if the squares s1, s2 and s3 are aligned
|
||||
/// either on a straight or on a diagonal line.
|
||||
|
||||
inline bool squares_aligned(Square s1, Square s2, Square s3) {
|
||||
return (BetweenBB[s1][s2] | BetweenBB[s1][s3] | BetweenBB[s2][s3])
|
||||
& ((1ULL << s1) | (1ULL << s2) | (1ULL << s3));
|
||||
}
|
||||
|
||||
|
||||
/// first_1() finds the least significant nonzero bit in a nonzero bitboard.
|
||||
/// pop_1st_bit() finds and clears the least significant nonzero bit in a
|
||||
/// nonzero bitboard.
|
||||
|
||||
118
src/book.cpp
118
src/book.cpp
@@ -32,30 +32,20 @@
|
||||
#include <cassert>
|
||||
|
||||
#include "book.h"
|
||||
#include "mersenne.h"
|
||||
#include "movegen.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
////
|
||||
//// Global variables
|
||||
////
|
||||
|
||||
Book OpeningBook;
|
||||
|
||||
|
||||
////
|
||||
//// Local definitions
|
||||
////
|
||||
|
||||
namespace {
|
||||
|
||||
/// Book entry size in bytes
|
||||
// Book entry size in bytes
|
||||
const int EntrySize = 16;
|
||||
|
||||
|
||||
/// Random numbers from PolyGlot, used to compute book hash keys
|
||||
|
||||
// Random numbers from PolyGlot, used to compute book hash keys
|
||||
const uint64_t Random64[781] = {
|
||||
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
|
||||
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
|
||||
@@ -320,17 +310,13 @@ namespace {
|
||||
0xF8D626AAAF278509ULL
|
||||
};
|
||||
|
||||
// Indices to the Random64[] array
|
||||
const int PieceIdx = 0;
|
||||
const int CastleIdx = 768;
|
||||
const int EnPassantIdx = 772;
|
||||
const int TurnIdx = 780;
|
||||
|
||||
/// Indices to the Random64[] array
|
||||
|
||||
const int RandomPiece = 0;
|
||||
const int RandomCastle = 768;
|
||||
const int RandomEnPassant = 772;
|
||||
const int RandomTurn = 780;
|
||||
|
||||
|
||||
/// Prototypes
|
||||
|
||||
// Local functions
|
||||
uint64_t book_key(const Position& pos);
|
||||
uint64_t book_piece_key(Piece p, Square s);
|
||||
uint64_t book_castle_key(const Position& pos);
|
||||
@@ -343,6 +329,13 @@ namespace {
|
||||
//// Functions
|
||||
////
|
||||
|
||||
// C'tor. Make random number generation less deterministic, for book moves
|
||||
Book::Book() {
|
||||
|
||||
for (int i = abs(get_system_time() % 10000); i > 0; i--)
|
||||
RKiss.rand<unsigned>();
|
||||
}
|
||||
|
||||
|
||||
/// Destructor. Be sure file is closed before we leave.
|
||||
|
||||
@@ -352,6 +345,16 @@ Book::~Book() {
|
||||
}
|
||||
|
||||
|
||||
/// Book::close() closes the file only if it is open, otherwise
|
||||
/// we can end up in a little mess due to how std::ifstream works.
|
||||
|
||||
void Book::close() {
|
||||
|
||||
if (is_open())
|
||||
ifstream::close();
|
||||
}
|
||||
|
||||
|
||||
/// Book::open() opens a book file with a given file name
|
||||
|
||||
void Book::open(const string& fName) {
|
||||
@@ -361,6 +364,8 @@ void Book::open(const string& fName) {
|
||||
|
||||
fileName = fName;
|
||||
ifstream::open(fileName.c_str(), ifstream::in | ifstream::binary);
|
||||
|
||||
// Silently return when asked to open a non-exsistent file
|
||||
if (!is_open())
|
||||
return;
|
||||
|
||||
@@ -372,21 +377,11 @@ void Book::open(const string& fName) {
|
||||
if (!good())
|
||||
{
|
||||
cerr << "Failed to open book file " << fileName << endl;
|
||||
Application::exit_with_failure();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Book::close() closes the file only if it is open, otherwise
|
||||
/// we can end up in a little mess due to how std::ifstream works.
|
||||
|
||||
void Book::close() {
|
||||
|
||||
if (is_open())
|
||||
ifstream::close();
|
||||
}
|
||||
|
||||
|
||||
/// Book::file_name() returns the file name of the currently active book,
|
||||
/// or the empty string if no book is open.
|
||||
|
||||
@@ -406,7 +401,7 @@ Move Book::get_move(const Position& pos, bool findBestMove) {
|
||||
|
||||
BookEntry entry;
|
||||
int bookMove = MOVE_NONE;
|
||||
int scoresSum = 0, bestScore = 0;
|
||||
unsigned scoresSum = 0, bestScore = 0;
|
||||
uint64_t key = book_key(pos);
|
||||
|
||||
// Choose a book move among the possible moves for the given position
|
||||
@@ -416,9 +411,7 @@ Move Book::get_move(const Position& pos, bool findBestMove) {
|
||||
if (entry.key != key)
|
||||
break;
|
||||
|
||||
int score = entry.count;
|
||||
|
||||
assert(score > 0);
|
||||
unsigned score = entry.count;
|
||||
|
||||
// If findBestMove is true choose highest rated book move
|
||||
if (findBestMove)
|
||||
@@ -435,13 +428,14 @@ Move Book::get_move(const Position& pos, bool findBestMove) {
|
||||
// high score it has more probability to be choosen then a one with
|
||||
// lower score. Note that first entry is always chosen.
|
||||
scoresSum += score;
|
||||
if (int(genrand_int32() % scoresSum) < score)
|
||||
if (RKiss.rand<unsigned>() % scoresSum < score)
|
||||
bookMove = entry.move;
|
||||
}
|
||||
if (!bookMove)
|
||||
return MOVE_NONE;
|
||||
|
||||
MoveStack mlist[256];
|
||||
// Verify the book move is legal
|
||||
MoveStack mlist[MOVES_MAX];
|
||||
MoveStack* last = generate_moves(pos, mlist);
|
||||
for (MoveStack* cur = mlist; cur != last; cur++)
|
||||
if ((int(cur->move) & 07777) == bookMove)
|
||||
@@ -474,6 +468,7 @@ int Book::find_key(uint64_t key) {
|
||||
assert(mid >= left && mid < right);
|
||||
|
||||
read_entry(entry, mid);
|
||||
|
||||
if (key <= entry.key)
|
||||
right = mid;
|
||||
else
|
||||
@@ -483,7 +478,7 @@ int Book::find_key(uint64_t key) {
|
||||
assert(left == right);
|
||||
|
||||
read_entry(entry, left);
|
||||
return (entry.key == key)? left : bookSize;
|
||||
return entry.key == key ? left : bookSize;
|
||||
}
|
||||
|
||||
|
||||
@@ -497,11 +492,13 @@ void Book::read_entry(BookEntry& entry, int idx) {
|
||||
assert(is_open());
|
||||
|
||||
seekg(idx * EntrySize, ios_base::beg);
|
||||
|
||||
*this >> entry;
|
||||
|
||||
if (!good())
|
||||
{
|
||||
cerr << "Failed to read book entry at index " << idx << endl;
|
||||
Application::exit_with_failure();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,10 +509,11 @@ void Book::read_entry(BookEntry& entry, int idx) {
|
||||
uint64_t Book::read_integer(int size) {
|
||||
|
||||
char buf[8];
|
||||
uint64_t n = 0;
|
||||
|
||||
read(buf, size);
|
||||
|
||||
// Numbers are stored on disk as a binary byte stream
|
||||
uint64_t n = 0ULL;
|
||||
for (int i = 0; i < size; i++)
|
||||
n = (n << 8) + (unsigned char)buf[i];
|
||||
|
||||
@@ -531,23 +529,15 @@ namespace {
|
||||
|
||||
uint64_t book_key(const Position& pos) {
|
||||
|
||||
uint64_t result = 0ULL;
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; c++)
|
||||
{
|
||||
Bitboard b = pos.pieces_of_color(c);
|
||||
uint64_t result = 0;
|
||||
Bitboard b = pos.occupied_squares();
|
||||
|
||||
while (b)
|
||||
{
|
||||
Square s = pop_1st_bit(&b);
|
||||
Piece p = pos.piece_on(s);
|
||||
|
||||
assert(piece_is_ok(p));
|
||||
assert(color_of_piece(p) == c);
|
||||
|
||||
result ^= book_piece_key(p, s);
|
||||
}
|
||||
result ^= book_piece_key(pos.piece_on(s), s);
|
||||
}
|
||||
|
||||
result ^= book_castle_key(pos);
|
||||
result ^= book_ep_key(pos);
|
||||
result ^= book_color_key(pos);
|
||||
@@ -557,39 +547,41 @@ namespace {
|
||||
|
||||
uint64_t book_piece_key(Piece p, Square s) {
|
||||
|
||||
/// Convert pieces to the range 0..11
|
||||
// Convert pieces to the range 0..11
|
||||
static const int PieceTo12[] = { 0, 0, 2, 4, 6, 8, 10, 0, 0, 1, 3, 5, 7, 9, 11 };
|
||||
|
||||
return Random64[RandomPiece + (PieceTo12[int(p)]^1) * 64 + int(s)];
|
||||
return Random64[PieceIdx + (PieceTo12[int(p)]^1) * 64 + int(s)];
|
||||
}
|
||||
|
||||
|
||||
uint64_t book_castle_key(const Position& pos) {
|
||||
|
||||
uint64_t result = 0ULL;
|
||||
uint64_t result = 0;
|
||||
|
||||
if (pos.can_castle_kingside(WHITE))
|
||||
result ^= Random64[RandomCastle+0];
|
||||
result ^= Random64[CastleIdx + 0];
|
||||
|
||||
if (pos.can_castle_queenside(WHITE))
|
||||
result ^= Random64[RandomCastle+1];
|
||||
result ^= Random64[CastleIdx + 1];
|
||||
|
||||
if (pos.can_castle_kingside(BLACK))
|
||||
result ^= Random64[RandomCastle+2];
|
||||
result ^= Random64[CastleIdx + 2];
|
||||
|
||||
if (pos.can_castle_queenside(BLACK))
|
||||
result ^= Random64[RandomCastle+3];
|
||||
result ^= Random64[CastleIdx + 3];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
uint64_t book_ep_key(const Position& pos) {
|
||||
return (pos.ep_square() == SQ_NONE ? 0ULL : Random64[RandomEnPassant + square_file(pos.ep_square())]);
|
||||
|
||||
return pos.ep_square() == SQ_NONE ? 0 : Random64[EnPassantIdx + square_file(pos.ep_square())];
|
||||
}
|
||||
|
||||
|
||||
uint64_t book_color_key(const Position& pos) {
|
||||
return (pos.side_to_move() == WHITE ? Random64[RandomTurn] : 0ULL);
|
||||
|
||||
return pos.side_to_move() == WHITE ? Random64[TurnIdx] : 0;
|
||||
}
|
||||
}
|
||||
|
||||
12
src/book.h
12
src/book.h
@@ -38,6 +38,7 @@
|
||||
|
||||
#include "move.h"
|
||||
#include "position.h"
|
||||
#include "rkiss.h"
|
||||
|
||||
|
||||
////
|
||||
@@ -56,7 +57,7 @@ class Book : private std::ifstream {
|
||||
Book(const Book&); // just decleared..
|
||||
Book& operator=(const Book&); // ..to avoid a warning
|
||||
public:
|
||||
Book() {}
|
||||
Book();
|
||||
~Book();
|
||||
void open(const std::string& fName);
|
||||
void close();
|
||||
@@ -74,14 +75,7 @@ private:
|
||||
|
||||
std::string fileName;
|
||||
int bookSize;
|
||||
RKISS RKiss;
|
||||
};
|
||||
|
||||
|
||||
////
|
||||
//// Global variables
|
||||
////
|
||||
|
||||
extern Book OpeningBook;
|
||||
|
||||
|
||||
#endif // !defined(BOOK_H_INCLUDED)
|
||||
|
||||
@@ -31,7 +31,10 @@ enum Depth {
|
||||
|
||||
ONE_PLY = 2,
|
||||
|
||||
DEPTH_ZERO = 0,
|
||||
DEPTH_ZERO = 0 * ONE_PLY,
|
||||
DEPTH_QS_CHECKS = -1 * ONE_PLY,
|
||||
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
|
||||
|
||||
DEPTH_NONE = -127 * ONE_PLY
|
||||
};
|
||||
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
|
||||
Stockfish 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.
|
||||
|
||||
Stockfish 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/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "direction.h"
|
||||
#include "square.h"
|
||||
|
||||
|
||||
////
|
||||
//// Local definitions
|
||||
////
|
||||
|
||||
namespace {
|
||||
|
||||
const SquareDelta directionToDelta[] = {
|
||||
DELTA_E, DELTA_W, DELTA_N, DELTA_S, DELTA_NE, DELTA_SW, DELTA_NW, DELTA_SE
|
||||
};
|
||||
|
||||
bool reachable(Square orig, Square dest, SignedDirection dir) {
|
||||
|
||||
SquareDelta delta = directionToDelta[dir];
|
||||
Square from = orig;
|
||||
Square to = from + delta;
|
||||
while (to != dest && square_distance(to, from) == 1 && square_is_ok(to))
|
||||
{
|
||||
from = to;
|
||||
to += delta;
|
||||
}
|
||||
return (to == dest && square_distance(from, to) == 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
//// Variables
|
||||
////
|
||||
|
||||
uint8_t DirectionTable[64][64];
|
||||
uint8_t SignedDirectionTable[64][64];
|
||||
|
||||
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
void init_direction_table() {
|
||||
|
||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
|
||||
{
|
||||
DirectionTable[s1][s2] = uint8_t(DIR_NONE);
|
||||
SignedDirectionTable[s1][s2] = uint8_t(SIGNED_DIR_NONE);
|
||||
if (s1 == s2)
|
||||
continue;
|
||||
|
||||
for (SignedDirection d = SIGNED_DIR_E; d != SIGNED_DIR_NONE; d++)
|
||||
{
|
||||
if (reachable(s1, s2, d))
|
||||
{
|
||||
SignedDirectionTable[s1][s2] = uint8_t(d);
|
||||
DirectionTable[s1][s2] = uint8_t(d / 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
|
||||
Stockfish 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.
|
||||
|
||||
Stockfish 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(DIRECTION_H_INCLUDED)
|
||||
#define DIRECTION_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "square.h"
|
||||
#include "types.h"
|
||||
|
||||
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
enum Direction {
|
||||
DIR_E = 0, DIR_N = 1, DIR_NE = 2, DIR_NW = 3, DIR_NONE = 4
|
||||
};
|
||||
|
||||
enum SignedDirection {
|
||||
SIGNED_DIR_E = 0, SIGNED_DIR_W = 1,
|
||||
SIGNED_DIR_N = 2, SIGNED_DIR_S = 3,
|
||||
SIGNED_DIR_NE = 4, SIGNED_DIR_SW = 5,
|
||||
SIGNED_DIR_NW = 6, SIGNED_DIR_SE = 7,
|
||||
SIGNED_DIR_NONE = 8
|
||||
};
|
||||
|
||||
ENABLE_OPERATORS_ON(SignedDirection);
|
||||
|
||||
|
||||
////
|
||||
//// Variables
|
||||
////
|
||||
|
||||
extern uint8_t DirectionTable[64][64];
|
||||
extern uint8_t SignedDirectionTable[64][64];
|
||||
|
||||
|
||||
////
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
inline Direction direction_between_squares(Square s1, Square s2) {
|
||||
return Direction(DirectionTable[s1][s2]);
|
||||
}
|
||||
|
||||
inline SignedDirection signed_direction_between_squares(Square s1, Square s2) {
|
||||
return SignedDirection(SignedDirectionTable[s1][s2]);
|
||||
}
|
||||
|
||||
inline int direction_is_diagonal(Square s1, Square s2) {
|
||||
return DirectionTable[s1][s2] & 2;
|
||||
}
|
||||
|
||||
inline bool direction_is_straight(Square s1, Square s2) {
|
||||
return DirectionTable[s1][s2] < 2;
|
||||
}
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern void init_direction_table();
|
||||
|
||||
|
||||
#endif // !defined(DIRECTION_H_INCLUDED)
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "bitbase.h"
|
||||
#include "bitcount.h"
|
||||
#include "endgame.h"
|
||||
|
||||
@@ -102,6 +101,7 @@ namespace {
|
||||
/// init_bitbases() is called during program initialization, and simply loads
|
||||
/// bitbases from disk into memory. At the moment, there is only the bitbase
|
||||
/// for KP vs K, but we may decide to add other bitbases later.
|
||||
extern void generate_kpk_bitbase(uint8_t bitbase[]);
|
||||
|
||||
void init_bitbases() {
|
||||
generate_kpk_bitbase(KPKBitbase);
|
||||
@@ -367,12 +367,12 @@ Value EvaluationFunction<KBBKN>::apply(const Position& pos) const {
|
||||
/// king alone are always draw.
|
||||
template<>
|
||||
Value EvaluationFunction<KmmKm>::apply(const Position&) const {
|
||||
return VALUE_ZERO;
|
||||
return VALUE_DRAW;
|
||||
}
|
||||
|
||||
template<>
|
||||
Value EvaluationFunction<KNNK>::apply(const Position&) const {
|
||||
return VALUE_ZERO;
|
||||
return VALUE_DRAW;
|
||||
}
|
||||
|
||||
/// KBPKScalingFunction scales endgames where the stronger side has king,
|
||||
@@ -699,11 +699,12 @@ ScaleFactor ScalingFunction<KBPKB>::apply(const Position& pos) const {
|
||||
return SCALE_FACTOR_ZERO;
|
||||
else
|
||||
{
|
||||
Bitboard ray = ray_bb(pawnSq, (strongerSide == WHITE)? SIGNED_DIR_N : SIGNED_DIR_S);
|
||||
if (ray & pos.pieces(KING, weakerSide))
|
||||
Bitboard path = squares_in_front_of(strongerSide, pawnSq);
|
||||
|
||||
if (path & pos.pieces(KING, weakerSide))
|
||||
return SCALE_FACTOR_ZERO;
|
||||
|
||||
if ( (pos.attacks_from<BISHOP>(weakerBishopSq) & ray)
|
||||
if ( (pos.attacks_from<BISHOP>(weakerBishopSq) & path)
|
||||
&& square_distance(weakerBishopSq, pawnSq) >= 3)
|
||||
return SCALE_FACTOR_ZERO;
|
||||
}
|
||||
|
||||
@@ -101,5 +101,4 @@ struct ScalingFunction : public EndgameScalingFunctionBase {
|
||||
|
||||
extern void init_bitbases();
|
||||
|
||||
|
||||
#endif // !defined(ENDGAME_H_INCLUDED)
|
||||
|
||||
231
src/evaluate.cpp
231
src/evaluate.cpp
@@ -45,11 +45,6 @@ namespace {
|
||||
// Pointer to pawn hash table entry
|
||||
PawnInfo* pi;
|
||||
|
||||
// updateKingTables[color] is set to true if we have enough material
|
||||
// to trigger the opponent's king safety calculation. When is false we
|
||||
// skip the time consuming update of the king attackers tables.
|
||||
bool updateKingTables[2];
|
||||
|
||||
// attackedBy[color][piece type] is a bitboard representing all squares
|
||||
// attacked by a given color and piece type, attackedBy[color][0] contains
|
||||
// all squares attacked by the given color.
|
||||
@@ -235,7 +230,7 @@ namespace {
|
||||
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility);
|
||||
|
||||
template<Color Us, bool HasPopCnt>
|
||||
Score evaluate_king(const Position& pos, EvalInfo& ei, Value& margin);
|
||||
Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]);
|
||||
|
||||
template<Color Us>
|
||||
Score evaluate_threats(const Position& pos, EvalInfo& ei);
|
||||
@@ -246,7 +241,10 @@ namespace {
|
||||
template<Color Us>
|
||||
Score evaluate_passed_pawns(const Position& pos, EvalInfo& ei);
|
||||
|
||||
Score apply_weight(Score v, Score weight);
|
||||
template<bool HasPopCnt>
|
||||
Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei);
|
||||
|
||||
inline Score apply_weight(Score v, Score weight);
|
||||
Value scale_by_game_phase(const Score& v, Phase ph, ScaleFactor sf);
|
||||
Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight);
|
||||
void init_safety();
|
||||
@@ -281,6 +279,7 @@ template<bool HasPopCnt>
|
||||
Value do_evaluate(const Position& pos, Value& margin) {
|
||||
|
||||
EvalInfo ei;
|
||||
Value margins[2];
|
||||
Score mobilityWhite, mobilityBlack;
|
||||
|
||||
assert(pos.is_ok());
|
||||
@@ -291,9 +290,9 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
// in the position object (material + piece square tables).
|
||||
Score bonus = pos.value();
|
||||
|
||||
// margin is the uncertainty estimation of position's evaluation
|
||||
// and typically is used by the search for pruning decisions.
|
||||
margin = VALUE_ZERO;
|
||||
// margins[] store the uncertainty estimation of position's evaluation
|
||||
// that typically is used by the search for pruning decisions.
|
||||
margins[WHITE] = margins[BLACK] = VALUE_ZERO;
|
||||
|
||||
// Probe the material hash table
|
||||
MaterialInfo* mi = MaterialTable[pos.thread()]->get_material_info(pos);
|
||||
@@ -302,7 +301,10 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
// If we have a specialized evaluation function for the current material
|
||||
// configuration, call it and return.
|
||||
if (mi->specialized_eval_exists())
|
||||
{
|
||||
margin = VALUE_ZERO;
|
||||
return mi->evaluate(pos);
|
||||
}
|
||||
|
||||
// Probe the pawn hash table
|
||||
ei.pi = PawnTable[pos.thread()]->get_pawn_info(pos);
|
||||
@@ -320,8 +322,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
|
||||
// Evaluate kings after all other pieces because we need complete attack
|
||||
// information when computing the king safety evaluation.
|
||||
bonus += evaluate_king<WHITE, HasPopCnt>(pos, ei, margin)
|
||||
- evaluate_king<BLACK, HasPopCnt>(pos, ei, margin);
|
||||
bonus += evaluate_king<WHITE, HasPopCnt>(pos, ei, margins)
|
||||
- evaluate_king<BLACK, HasPopCnt>(pos, ei, margins);
|
||||
|
||||
// Evaluate tactical threats, we need full attack information including king
|
||||
bonus += evaluate_threats<WHITE>(pos, ei)
|
||||
@@ -331,18 +333,21 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
bonus += evaluate_passed_pawns<WHITE>(pos, ei)
|
||||
- evaluate_passed_pawns<BLACK>(pos, ei);
|
||||
|
||||
Phase phase = mi->game_phase();
|
||||
// If one side has only a king, check whether exists any unstoppable passed pawn
|
||||
if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK))
|
||||
bonus += evaluate_unstoppable_pawns<HasPopCnt>(pos, ei);
|
||||
|
||||
// Evaluate space for both sides, only in middle-game.
|
||||
if (phase > PHASE_ENDGAME && mi->space_weight() > 0)
|
||||
if (mi->space_weight())
|
||||
{
|
||||
int s = evaluate_space<WHITE, HasPopCnt>(pos, ei) - evaluate_space<BLACK, HasPopCnt>(pos, ei);
|
||||
bonus += apply_weight(make_score(s * mi->space_weight(), 0), Weights[Space]);
|
||||
}
|
||||
|
||||
// Scale winning side if position is more drawish that what it appears
|
||||
ScaleFactor sf = eg_value(bonus) > VALUE_ZERO ? mi->scale_factor(pos, WHITE)
|
||||
ScaleFactor sf = eg_value(bonus) > VALUE_DRAW ? mi->scale_factor(pos, WHITE)
|
||||
: mi->scale_factor(pos, BLACK);
|
||||
Phase phase = mi->game_phase();
|
||||
|
||||
// If we don't already have an unusual scale factor, check for opposite
|
||||
// colored bishop endgames, and use a lower scale for those.
|
||||
@@ -366,6 +371,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
}
|
||||
|
||||
// Interpolate between the middle game and the endgame score
|
||||
margin = margins[pos.side_to_move()];
|
||||
Value v = scale_by_game_phase(bonus, phase, sf);
|
||||
return pos.side_to_move() == WHITE ? v : -v;
|
||||
}
|
||||
@@ -408,7 +414,7 @@ void quit_eval() {
|
||||
|
||||
/// read_weights() reads evaluation weights from the corresponding UCI parameters
|
||||
|
||||
void read_weights(Color us) {
|
||||
void read_evaluation_uci_options(Color us) {
|
||||
|
||||
// King safety is asymmetrical. Our king danger level is weighted by
|
||||
// "Cowardice" UCI parameter, instead the opponent one by "Aggressiveness".
|
||||
@@ -424,7 +430,7 @@ void read_weights(Color us) {
|
||||
|
||||
// If running in analysis mode, make sure we use symmetrical king safety. We do this
|
||||
// by replacing both Weights[kingDangerUs] and Weights[kingDangerThem] by their average.
|
||||
if (get_option_value_bool("UCI_AnalyseMode"))
|
||||
if (Options["UCI_AnalyseMode"].value<bool>())
|
||||
Weights[kingDangerUs] = Weights[kingDangerThem] = (Weights[kingDangerUs] + Weights[kingDangerThem]) / 2;
|
||||
|
||||
init_safety();
|
||||
@@ -443,15 +449,18 @@ namespace {
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
|
||||
Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them));
|
||||
ei.kingZone[Us] = (b | (Us == WHITE ? b >> 8 : b << 8));
|
||||
ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us);
|
||||
ei.updateKingTables[Us] = pos.piece_count(Us, QUEEN) && pos.non_pawn_material(Us) >= QueenValueMidgame + RookValueMidgame;
|
||||
if (ei.updateKingTables[Us])
|
||||
|
||||
// Init king safety tables only if we are going to use them
|
||||
if ( pos.piece_count(Us, QUEEN)
|
||||
&& pos.non_pawn_material(Us) >= QueenValueMidgame + RookValueMidgame)
|
||||
{
|
||||
ei.kingZone[Us] = (b | (Us == WHITE ? b >> 8 : b << 8));
|
||||
b &= ei.attackedBy[Us][PAWN];
|
||||
ei.kingAttackersCount[Us] = b ? count_1s<Max15>(b) / 2 : EmptyBoardBB;
|
||||
ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = EmptyBoardBB;
|
||||
}
|
||||
ei.kingAttackersCount[Us] = b ? count_1s<Max15>(b) / 2 : 0;
|
||||
ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0;
|
||||
} else
|
||||
ei.kingZone[Us] = ei.kingAttackersCount[Us] = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -515,7 +524,7 @@ namespace {
|
||||
ei.attackedBy[Us][Piece] |= b;
|
||||
|
||||
// King attacks
|
||||
if (ei.updateKingTables[Us] && (b & ei.kingZone[Us]))
|
||||
if (b & ei.kingZone[Us])
|
||||
{
|
||||
ei.kingAttackersCount[Us]++;
|
||||
ei.kingAttackersWeight[Us] += KingAttackWeights[Piece];
|
||||
@@ -653,7 +662,7 @@ namespace {
|
||||
// evaluate_king<>() assigns bonuses and penalties to a king of a given color
|
||||
|
||||
template<Color Us, bool HasPopCnt>
|
||||
Score evaluate_king(const Position& pos, EvalInfo& ei, Value& margin) {
|
||||
Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]) {
|
||||
|
||||
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
@@ -667,8 +676,7 @@ namespace {
|
||||
|
||||
// King safety. This is quite complicated, and is almost certainly far
|
||||
// from optimally tuned.
|
||||
if ( ei.updateKingTables[Them]
|
||||
&& ei.kingAttackersCount[Them] >= 2
|
||||
if ( ei.kingAttackersCount[Them] >= 2
|
||||
&& ei.kingAdjacentZoneAttacksCount[Them])
|
||||
{
|
||||
// Find the attacked squares around the king which has no defenders
|
||||
@@ -755,8 +763,7 @@ namespace {
|
||||
// be very big, and so capturing a single attacking piece can therefore
|
||||
// result in a score change far bigger than the value of the captured piece.
|
||||
bonus -= KingDangerTable[Us][attackUnits];
|
||||
if (pos.side_to_move() == Us)
|
||||
margin += mg_value(KingDangerTable[Us][attackUnits]);
|
||||
margins[Us] += mg_value(KingDangerTable[Us][attackUnits]);
|
||||
}
|
||||
return bonus;
|
||||
}
|
||||
@@ -859,6 +866,168 @@ namespace {
|
||||
return apply_weight(bonus, Weights[PassedPawns]);
|
||||
}
|
||||
|
||||
// evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides
|
||||
template<bool HasPopCnt>
|
||||
Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei) {
|
||||
|
||||
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
|
||||
|
||||
// Step 1. Hunt for unstoppable pawns. If we find at least one, record how many plies
|
||||
// are required for promotion
|
||||
int pliesToGo[2] = {256, 256};
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; c++)
|
||||
{
|
||||
// Skip if other side has non-pawn pieces
|
||||
if (pos.non_pawn_material(opposite_color(c)))
|
||||
continue;
|
||||
|
||||
Bitboard b = ei.pi->passed_pawns(c);
|
||||
|
||||
while (b)
|
||||
{
|
||||
Square s = pop_1st_bit(&b);
|
||||
Square queeningSquare = relative_square(c, make_square(square_file(s), RANK_8));
|
||||
|
||||
int mtg = RANK_8 - relative_rank(c, s) - int(relative_rank(c, s) == RANK_2);
|
||||
int oppmtg = square_distance(pos.king_square(opposite_color(c)), queeningSquare) - int(c != pos.side_to_move());
|
||||
bool pathDefended = ((ei.attackedBy[c][0] & squares_in_front_of(c, s)) == squares_in_front_of(c, s));
|
||||
|
||||
if (mtg >= oppmtg && !pathDefended)
|
||||
continue;
|
||||
|
||||
int blockerCount = count_1s<Max15>(squares_in_front_of(c, s) & pos.occupied_squares());
|
||||
mtg += blockerCount;
|
||||
|
||||
if (mtg >= oppmtg && !pathDefended)
|
||||
continue;
|
||||
|
||||
int ptg = 2 * mtg - int(c == pos.side_to_move());
|
||||
|
||||
if (ptg < pliesToGo[c])
|
||||
pliesToGo[c] = ptg;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2. If either side cannot promote at least three plies before the other side then
|
||||
// situation becomes too complex and we give up. Otherwise we determine the possibly "winning side"
|
||||
if (abs(pliesToGo[WHITE] - pliesToGo[BLACK]) < 3)
|
||||
return make_score(0, 0);
|
||||
|
||||
Color winnerSide = (pliesToGo[WHITE] < pliesToGo[BLACK] ? WHITE : BLACK);
|
||||
Color loserSide = opposite_color(winnerSide);
|
||||
|
||||
// Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss?
|
||||
// We collect the potential candidates in potentialBB.
|
||||
Bitboard pawnBB = pos.pieces(PAWN, loserSide);
|
||||
Bitboard potentialBB = pawnBB;
|
||||
const Bitboard passedBB = ei.pi->passed_pawns(loserSide);
|
||||
|
||||
while(pawnBB)
|
||||
{
|
||||
Square psq = pop_1st_bit(&pawnBB);
|
||||
|
||||
// Check direct advancement
|
||||
int mtg = RANK_8 - relative_rank(loserSide, psq) - int(relative_rank(loserSide, psq) == RANK_2);
|
||||
int ptg = 2 * mtg - int(loserSide == pos.side_to_move());
|
||||
|
||||
// Check if (without even considering any obstacles) we're too far away
|
||||
if (pliesToGo[winnerSide] + 3 <= ptg)
|
||||
{
|
||||
clear_bit(&potentialBB, psq);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this is passed pawn, then it _may_ promote in time. We give up.
|
||||
if (bit_is_set(passedBB, psq))
|
||||
return make_score(0, 0);
|
||||
|
||||
// Doubled pawn is worthless
|
||||
if (squares_in_front_of(loserSide, psq) & (pos.pieces(PAWN, loserSide)))
|
||||
{
|
||||
clear_bit(&potentialBB, psq);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4. Check new passed pawn creation through king capturing and sacrifises
|
||||
pawnBB = potentialBB;
|
||||
|
||||
while(pawnBB)
|
||||
{
|
||||
Square psq = pop_1st_bit(&pawnBB);
|
||||
|
||||
int mtg = RANK_8 - relative_rank(loserSide, psq) - int(relative_rank(loserSide, psq) == RANK_2);
|
||||
int ptg = 2 * mtg - int(loserSide == pos.side_to_move());
|
||||
|
||||
// Generate list of obstacles
|
||||
Bitboard obsBB = passed_pawn_mask(loserSide, psq) & pos.pieces(PAWN, winnerSide);
|
||||
const bool pawnIsOpposed = squares_in_front_of(loserSide, psq) & obsBB;
|
||||
assert(obsBB);
|
||||
|
||||
// How many plies does it take to remove all the obstacles?
|
||||
int sacptg = 0;
|
||||
int realObsCount = 0;
|
||||
int minKingDist = 256;
|
||||
|
||||
while(obsBB)
|
||||
{
|
||||
Square obSq = pop_1st_bit(&obsBB);
|
||||
int minMoves = 256;
|
||||
|
||||
// Check pawns that can give support to overcome obstacle (Eg. wp: a4,b4 bp: b2. b4 is giving support)
|
||||
if (!pawnIsOpposed && square_file(psq) != square_file(obSq))
|
||||
{
|
||||
Bitboard supBB = in_front_bb(winnerSide, Square(obSq + (winnerSide == WHITE ? 8 : -8)))
|
||||
& neighboring_files_bb(psq) & potentialBB;
|
||||
|
||||
while(supBB) // This while-loop could be replaced with supSq = LSB/MSB(supBB) (depending on color)
|
||||
{
|
||||
Square supSq = pop_1st_bit(&supBB);
|
||||
int dist = square_distance(obSq, supSq);
|
||||
minMoves = Min(minMoves, dist - 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check pawns that can be sacrifised
|
||||
Bitboard sacBB = passed_pawn_mask(winnerSide, obSq) & neighboring_files_bb(obSq) & potentialBB & ~(1ULL << psq);
|
||||
|
||||
while(sacBB) // This while-loop could be replaced with sacSq = LSB/MSB(sacBB) (depending on color)
|
||||
{
|
||||
Square sacSq = pop_1st_bit(&sacBB);
|
||||
int dist = square_distance(obSq, sacSq);
|
||||
minMoves = Min(minMoves, dist - 2);
|
||||
}
|
||||
|
||||
// If obstacle can be destroyed with immediate pawn sacrifise, it's not real obstacle
|
||||
if (minMoves <= 0)
|
||||
continue;
|
||||
|
||||
// Pawn sac calculations
|
||||
sacptg += minMoves * 2;
|
||||
|
||||
// King capture calc
|
||||
realObsCount++;
|
||||
int kingDist = square_distance(pos.king_square(loserSide), obSq);
|
||||
minKingDist = Min(minKingDist, kingDist);
|
||||
}
|
||||
|
||||
// Check if pawn sac plan _may_ save the day
|
||||
if (pliesToGo[winnerSide] + 3 > ptg + sacptg)
|
||||
return make_score(0, 0);
|
||||
|
||||
// Check if king capture plan _may_ save the day (contains some false positives)
|
||||
int kingptg = (minKingDist + realObsCount) * 2;
|
||||
if (pliesToGo[winnerSide] + 3 > ptg + kingptg)
|
||||
return make_score(0, 0);
|
||||
}
|
||||
|
||||
// Step 5. Assign bonus
|
||||
const int Sign[2] = {1, -1};
|
||||
return Sign[winnerSide] * make_score(0, (Value) 0x500 - 0x20 * pliesToGo[winnerSide]);
|
||||
}
|
||||
|
||||
|
||||
// evaluate_space() computes the space evaluation for a given side. The
|
||||
// space evaluation is a simple bonus based on the number of safe squares
|
||||
@@ -920,8 +1089,8 @@ namespace {
|
||||
Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) {
|
||||
|
||||
// Scale option value from 100 to 256
|
||||
int mg = get_option_value_int(mgOpt) * 256 / 100;
|
||||
int eg = get_option_value_int(egOpt) * 256 / 100;
|
||||
int mg = Options[mgOpt].value<int>() * 256 / 100;
|
||||
int eg = Options[egOpt].value<int>() * 256 / 100;
|
||||
|
||||
return apply_weight(make_score(mg, eg), internalWeight);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,6 @@ class Position;
|
||||
extern Value evaluate(const Position& pos, Value& margin);
|
||||
extern void init_eval(int threads);
|
||||
extern void quit_eval();
|
||||
extern void read_weights(Color sideToMove);
|
||||
extern void read_evaluation_uci_options(Color sideToMove);
|
||||
|
||||
#endif // !defined(EVALUATE_H_INCLUDED)
|
||||
|
||||
14
src/lock.h
14
src/lock.h
@@ -27,12 +27,16 @@
|
||||
# include <pthread.h>
|
||||
|
||||
typedef pthread_mutex_t Lock;
|
||||
typedef pthread_cond_t WaitCondition;
|
||||
|
||||
# define lock_init(x) pthread_mutex_init(x, NULL)
|
||||
# define lock_grab(x) pthread_mutex_lock(x)
|
||||
# define lock_release(x) pthread_mutex_unlock(x)
|
||||
# define lock_destroy(x) pthread_mutex_destroy(x)
|
||||
|
||||
# define cond_destroy(x) pthread_cond_destroy(x)
|
||||
# define cond_init(x) pthread_cond_init(x, NULL)
|
||||
# define cond_signal(x) pthread_cond_signal(x)
|
||||
# define cond_wait(x,y) pthread_cond_wait(x,y)
|
||||
|
||||
#else
|
||||
|
||||
@@ -41,12 +45,16 @@ typedef pthread_mutex_t Lock;
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
|
||||
typedef CRITICAL_SECTION Lock;
|
||||
typedef HANDLE WaitCondition;
|
||||
|
||||
# define lock_init(x) InitializeCriticalSection(x)
|
||||
# define lock_grab(x) EnterCriticalSection(x)
|
||||
# define lock_release(x) LeaveCriticalSection(x)
|
||||
# define lock_destroy(x) DeleteCriticalSection(x)
|
||||
|
||||
|
||||
# define cond_init(x) { *x = CreateEvent(0, FALSE, FALSE, 0); }
|
||||
# define cond_destroy(x) CloseHandle(*x)
|
||||
# define cond_signal(x) SetEvent(*x)
|
||||
# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(*x, INFINITE); lock_grab(y); }
|
||||
#endif
|
||||
|
||||
#endif // !defined(LOCK_H_INCLUDED)
|
||||
|
||||
59
src/main.cpp
59
src/main.cpp
@@ -28,10 +28,16 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "benchmark.h"
|
||||
#include "bitboard.h"
|
||||
#include "bitcount.h"
|
||||
#include "endgame.h"
|
||||
#include "evaluate.h"
|
||||
#include "material.h"
|
||||
#include "misc.h"
|
||||
#include "uci.h"
|
||||
#include "position.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
#include "ucioption.h"
|
||||
|
||||
#ifdef USE_CALLGRIND
|
||||
#include <valgrind/callgrind.h>
|
||||
@@ -39,19 +45,28 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
extern bool execute_uci_command(const string& cmd);
|
||||
extern void benchmark(int argc, char* argv[]);
|
||||
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
// Disable IO buffering
|
||||
cout.rdbuf()->pubsetbuf(NULL, 0);
|
||||
cin.rdbuf()->pubsetbuf(NULL, 0);
|
||||
|
||||
// Initialization through global resources manager
|
||||
Application::initialize();
|
||||
// Startup initializations
|
||||
init_bitboards();
|
||||
init_uci_options();
|
||||
Position::init_zobrist();
|
||||
Position::init_piece_square_tables();
|
||||
init_eval(1);
|
||||
init_bitbases();
|
||||
init_search();
|
||||
init_threads();
|
||||
|
||||
#ifdef USE_CALLGRIND
|
||||
CALLGRIND_START_INSTRUMENTATION;
|
||||
@@ -66,26 +81,30 @@ int main(int argc, char *argv[]) {
|
||||
if (CpuHasPOPCNT)
|
||||
cout << "Good! CPU has hardware POPCNT." << endl;
|
||||
|
||||
// Enter UCI mode
|
||||
uci_main_loop();
|
||||
// Wait for a command from the user, and passes this command to
|
||||
// execute_uci_command() and also intercepts EOF from stdin, by
|
||||
// translating EOF to the "quit" command. This ensures that we
|
||||
// exit gracefully if the GUI dies unexpectedly.
|
||||
string cmd;
|
||||
|
||||
do {
|
||||
// Wait for a command from stdin
|
||||
if (!getline(cin, cmd))
|
||||
cmd = "quit";
|
||||
|
||||
} while (execute_uci_command(cmd));
|
||||
}
|
||||
else // Process command line arguments
|
||||
{
|
||||
if (string(argv[1]) != "bench" || argc < 4 || argc > 8)
|
||||
cout << "Usage: stockfish bench <hash size> <threads> "
|
||||
<< "[time = 60s] [fen positions file = default] "
|
||||
<< "[time, depth, perft or node limited = time] "
|
||||
<< "[timing file name = none]" << endl;
|
||||
if (string(argv[1]) != "bench" || argc > 7)
|
||||
cout << "Usage: stockfish bench [hash size = 128] [threads = 1] "
|
||||
<< "[limit = 12] [fen positions file = default] "
|
||||
<< "[depth, time, perft or node limited = depth]" << endl;
|
||||
else
|
||||
{
|
||||
string time = argc > 4 ? argv[4] : "60";
|
||||
string fen = argc > 5 ? argv[5] : "default";
|
||||
string lim = argc > 6 ? argv[6] : "time";
|
||||
string tim = argc > 7 ? argv[7] : "";
|
||||
benchmark(string(argv[2]) + " " + string(argv[3]) + " " + time + " " + fen + " " + lim + " " + tim);
|
||||
}
|
||||
benchmark(argc, argv);
|
||||
}
|
||||
|
||||
Application::free_resources();
|
||||
exit_threads();
|
||||
quit_eval();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include "material.h"
|
||||
@@ -48,11 +47,11 @@ namespace {
|
||||
|
||||
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
|
||||
|
||||
const int QuadraticCoefficientsSameColor[][6] = {
|
||||
const int QuadraticCoefficientsSameColor[][8] = {
|
||||
{ 7, 7, 7, 7, 7, 7 }, { 39, 2, 7, 7, 7, 7 }, { 35, 271, -4, 7, 7, 7 },
|
||||
{ 7, 25, 4, 7, 7, 7 }, { -27, -2, 46, 100, 56, 7 }, { 58, 29, 83, 148, -3, -25 } };
|
||||
|
||||
const int QuadraticCoefficientsOppositeColor[][6] = {
|
||||
const int QuadraticCoefficientsOppositeColor[][8] = {
|
||||
{ 41, 41, 41, 41, 41, 41 }, { 37, 41, 41, 41, 41, 41 }, { 10, 62, 41, 41, 41, 41 },
|
||||
{ 57, 64, 39, 41, 41, 41 }, { 50, 40, 23, -22, 41, 41 }, { 106, 101, 3, 151, 171, 41 } };
|
||||
|
||||
@@ -144,8 +143,9 @@ MaterialInfoTable::MaterialInfoTable() {
|
||||
{
|
||||
cerr << "Failed to allocate " << MaterialTableSize * sizeof(MaterialInfo)
|
||||
<< " bytes for material hash table." << endl;
|
||||
Application::exit_with_failure();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
memset(entries, 0, MaterialTableSize * sizeof(MaterialInfo));
|
||||
}
|
||||
|
||||
MaterialInfoTable::~MaterialInfoTable() {
|
||||
@@ -289,10 +289,12 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
|
||||
}
|
||||
|
||||
// Evaluate the material balance
|
||||
const int pieceCount[2][6] = { { pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT),
|
||||
const int pieceCount[2][8] = {
|
||||
{ pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT),
|
||||
pos.piece_count(WHITE, BISHOP), pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) },
|
||||
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT),
|
||||
pos.piece_count(BLACK, BISHOP), pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } };
|
||||
|
||||
Color c, them;
|
||||
int sign, pt1, pt2, pc;
|
||||
int v, vv, matValue = 0;
|
||||
@@ -356,7 +358,7 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
|
||||
}
|
||||
|
||||
|
||||
/// EndgameFunctions member definitions.
|
||||
/// EndgameFunctions member definitions
|
||||
|
||||
EndgameFunctions::EndgameFunctions() {
|
||||
|
||||
@@ -388,10 +390,10 @@ EndgameFunctions::~EndgameFunctions() {
|
||||
|
||||
Key EndgameFunctions::buildKey(const string& keyCode) {
|
||||
|
||||
assert(keyCode.length() > 0 && keyCode[0] == 'K');
|
||||
assert(keyCode.length() < 8);
|
||||
assert(keyCode.length() > 0 && keyCode.length() < 8);
|
||||
assert(keyCode[0] == 'K');
|
||||
|
||||
stringstream s;
|
||||
string fen;
|
||||
bool upcase = false;
|
||||
|
||||
// Build up a fen string with the given pieces, note that
|
||||
@@ -401,16 +403,17 @@ Key EndgameFunctions::buildKey(const string& keyCode) {
|
||||
if (keyCode[i] == 'K')
|
||||
upcase = !upcase;
|
||||
|
||||
s << char(upcase ? toupper(keyCode[i]) : tolower(keyCode[i]));
|
||||
fen += char(upcase ? toupper(keyCode[i]) : tolower(keyCode[i]));
|
||||
}
|
||||
s << 8 - keyCode.length() << "/8/8/8/8/8/8/8 w - -";
|
||||
return Position(s.str(), 0).get_material_key();
|
||||
fen += char(8 - keyCode.length() + '0');
|
||||
fen += "/8/8/8/8/8/8/8 w - -";
|
||||
return Position(fen, 0).get_material_key();
|
||||
}
|
||||
|
||||
const string EndgameFunctions::swapColors(const string& keyCode) {
|
||||
|
||||
// Build corresponding key for the opposite color: "KBPKN" -> "KNKBP"
|
||||
size_t idx = keyCode.find("K", 1);
|
||||
size_t idx = keyCode.find('K', 1);
|
||||
return keyCode.substr(idx) + keyCode.substr(0, idx);
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,9 @@ class EndgameFunctions;
|
||||
|
||||
class MaterialInfoTable {
|
||||
|
||||
MaterialInfoTable(const MaterialInfoTable&);
|
||||
MaterialInfoTable& operator=(const MaterialInfoTable&);
|
||||
|
||||
public:
|
||||
MaterialInfoTable();
|
||||
~MaterialInfoTable();
|
||||
|
||||
171
src/mersenne.cpp
171
src/mersenne.cpp
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
A C-program for MT19937, with initialization improved 2002/1/26.
|
||||
Coded by Takuji Nishimura and Makoto Matsumoto.
|
||||
|
||||
Before using, initialize the state by using init_genrand(seed)
|
||||
or init_by_array(init_key, key_length).
|
||||
|
||||
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of its contributors may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Any feedback is very welcome.
|
||||
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
|
||||
email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
|
||||
*/
|
||||
|
||||
#include "mersenne.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Period parameters
|
||||
const int N = 624;
|
||||
const int M = 397;
|
||||
const unsigned long MATRIX_A = 0x9908b0dfUL; // Constant vector a
|
||||
const unsigned long UPPER_MASK = 0x80000000UL; // Most significant w-r bits
|
||||
const unsigned long LOWER_MASK = 0x7fffffffUL; // Least significant r bits
|
||||
|
||||
unsigned long mt[N]; // The array for the state vector
|
||||
int mti = N + 1; // mti == N+1 means mt[N] is not initialized
|
||||
|
||||
// Initializes mt[N] with a seed
|
||||
void init_genrand(unsigned long s) {
|
||||
|
||||
mt[0]= s & 0xffffffffUL;
|
||||
for (mti = 1; mti < N; mti++)
|
||||
{
|
||||
// See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier.
|
||||
// In the previous versions, MSBs of the seed affect
|
||||
// only MSBs of the array mt[].
|
||||
// 2002/01/09 modified by Makoto Matsumoto
|
||||
mt[mti] = 1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti;
|
||||
mt[mti] &= 0xffffffffUL; // For > 32 bit machines
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize by an array with array-length
|
||||
// init_key is the array for initializing keys
|
||||
// key_length is its length
|
||||
// slight change for C++, 2004/2/26
|
||||
void init_by_array(unsigned long init_key[], int key_length) {
|
||||
|
||||
int i = 1;
|
||||
int j = 0;
|
||||
int k = (N > key_length ? N : key_length);
|
||||
|
||||
init_genrand(19650218UL);
|
||||
|
||||
for ( ; k; k--)
|
||||
{
|
||||
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL)) + init_key[j] + j; // Non linear
|
||||
mt[i] &= 0xffffffffUL; // For WORDSIZE > 32 machines
|
||||
i++; j++;
|
||||
if (i >= N)
|
||||
{
|
||||
mt[0] = mt[N-1];
|
||||
i = 1;
|
||||
}
|
||||
if (j >= key_length)
|
||||
j = 0;
|
||||
}
|
||||
for (k = N-1; k; k--)
|
||||
{
|
||||
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL)) - i; // Non linear
|
||||
mt[i] &= 0xffffffffUL; // For WORDSIZE > 32 machines
|
||||
i++;
|
||||
if (i >= N)
|
||||
{
|
||||
mt[0] = mt[N-1];
|
||||
i = 1;
|
||||
}
|
||||
}
|
||||
mt[0] = 0x80000000UL; // MSB is 1; assuring non-zero initial array
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
// Generates a random number on [0,0xffffffff]-interval
|
||||
uint32_t genrand_int32() {
|
||||
|
||||
unsigned long y;
|
||||
static unsigned long mag01[2] = {0x0UL, MATRIX_A};
|
||||
/* mag01[x] = x * MATRIX_A for x=0,1 */
|
||||
|
||||
// Generate N words at one time
|
||||
if (mti >= N)
|
||||
{
|
||||
int kk;
|
||||
|
||||
if (mti == N+1) // If init_genrand() has not been called,
|
||||
init_genrand(5489UL); // a default initial seed is used.
|
||||
|
||||
for (kk = 0; kk < N-M; kk++)
|
||||
{
|
||||
y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
|
||||
mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
|
||||
}
|
||||
for ( ; kk < N-1; kk++)
|
||||
{
|
||||
y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
|
||||
mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
|
||||
}
|
||||
y = (mt[N-1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
|
||||
mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
|
||||
mti = 0;
|
||||
}
|
||||
|
||||
y = mt[mti++];
|
||||
|
||||
// Tempering
|
||||
y ^= (y >> 11);
|
||||
y ^= (y << 7) & 0x9d2c5680UL;
|
||||
y ^= (y << 15) & 0xefc60000UL;
|
||||
y ^= (y >> 18);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
uint64_t genrand_int64() {
|
||||
|
||||
uint64_t x, y;
|
||||
|
||||
x = genrand_int32();
|
||||
y = genrand_int32();
|
||||
return (x << 32) | y;
|
||||
}
|
||||
|
||||
void init_mersenne() {
|
||||
|
||||
unsigned long init[4] = {0x123, 0x234, 0x345, 0x456};
|
||||
unsigned long length = 4;
|
||||
|
||||
init_by_array(init, length);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
|
||||
Stockfish 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.
|
||||
|
||||
Stockfish 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(MERSENNE_H_INCLUDED)
|
||||
#define MERSENNE_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "types.h"
|
||||
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern uint32_t genrand_int32();
|
||||
extern uint64_t genrand_int64();
|
||||
extern void init_mersenne();
|
||||
|
||||
|
||||
#endif // !defined(MERSENNE_H_INCLUDED)
|
||||
103
src/misc.cpp
103
src/misc.cpp
@@ -58,7 +58,7 @@ using namespace std;
|
||||
/// Version number. If this is left empty, the current date (in the format
|
||||
/// YYMMDD) is used as a version number.
|
||||
|
||||
static const string EngineVersion = "1.9.1";
|
||||
static const string EngineVersion = "2.0";
|
||||
static const string AppName = "Stockfish";
|
||||
static const string AppTag = "";
|
||||
|
||||
@@ -67,8 +67,8 @@ static const string AppTag = "";
|
||||
//// Variables
|
||||
////
|
||||
|
||||
uint64_t dbg_cnt0 = 0;
|
||||
uint64_t dbg_cnt1 = 0;
|
||||
static uint64_t dbg_cnt0 = 0;
|
||||
static uint64_t dbg_cnt1 = 0;
|
||||
|
||||
bool dbg_show_mean = false;
|
||||
bool dbg_show_hit_rate = false;
|
||||
@@ -127,42 +127,29 @@ void dbg_print_mean() {
|
||||
<< (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << endl;
|
||||
}
|
||||
|
||||
void dbg_print_hit_rate(ofstream& logFile) {
|
||||
|
||||
logFile << "Total " << dbg_cnt0 << " Hit " << dbg_cnt1
|
||||
<< " hit rate (%) " << (dbg_cnt1*100)/(dbg_cnt0 ? dbg_cnt0 : 1) << endl;
|
||||
}
|
||||
|
||||
void dbg_print_mean(ofstream& logFile) {
|
||||
|
||||
logFile << "Total " << dbg_cnt0 << " Mean "
|
||||
<< (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << endl;
|
||||
}
|
||||
|
||||
/// engine_name() returns the full name of the current Stockfish version.
|
||||
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when the
|
||||
/// program was compiled) or "Stockfish <version number>", depending on whether
|
||||
/// the constant EngineVersion (defined in misc.h) is empty.
|
||||
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when
|
||||
/// the program was compiled) or "Stockfish <version number>", depending
|
||||
/// on whether the constant EngineVersion (defined in misc.h) is empty.
|
||||
|
||||
const string engine_name() {
|
||||
|
||||
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
|
||||
const string cpu64(CpuIs64Bit ? " 64bit" : "");
|
||||
|
||||
if (!EngineVersion.empty())
|
||||
return AppName + " " + EngineVersion + cpu64;
|
||||
|
||||
string date(__DATE__); // From compiler, format is "Sep 21 2008"
|
||||
string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
|
||||
stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008"
|
||||
string month, day, year;
|
||||
|
||||
size_t mon = 1 + months.find(date.substr(0, 3)) / 4;
|
||||
date >> month >> day >> year;
|
||||
|
||||
stringstream s;
|
||||
string day = (date[4] == ' ' ? date.substr(5, 1) : date.substr(4, 2));
|
||||
|
||||
string name = AppName + " " + AppTag + " ";
|
||||
|
||||
s << name << date.substr(date.length() - 2) << setfill('0')
|
||||
<< setw(2) << mon << setw(2) << day << cpu64;
|
||||
s << setfill('0') << AppName + " " + AppTag + " "
|
||||
<< year.substr(2, 2) << setw(2)
|
||||
<< (1 + months.find(month) / 4) << setw(2)
|
||||
<< day << cpu64;
|
||||
|
||||
return s.str();
|
||||
}
|
||||
@@ -218,20 +205,18 @@ int cpu_count() {
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
From Beowulf, from Olithink
|
||||
*/
|
||||
/// Check for console input. Original code from Beowulf and Olithink
|
||||
|
||||
#ifndef _WIN32
|
||||
/* Non-windows version */
|
||||
int Bioskey()
|
||||
|
||||
int data_available()
|
||||
{
|
||||
fd_set readfds;
|
||||
struct timeval timeout;
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(fileno(stdin), &readfds);
|
||||
/* Set to timeout immediately */
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_sec = 0; // Set to timeout immediately
|
||||
timeout.tv_usec = 0;
|
||||
select(16, &readfds, 0, 0, &timeout);
|
||||
|
||||
@@ -239,46 +224,36 @@ int Bioskey()
|
||||
}
|
||||
|
||||
#else
|
||||
/* Windows-version */
|
||||
#include <windows.h>
|
||||
#include <conio.h>
|
||||
int Bioskey()
|
||||
{
|
||||
static int init = 0,
|
||||
pipe;
|
||||
static HANDLE inh;
|
||||
DWORD dw;
|
||||
/* If we're running under XBoard then we can't use _kbhit() as the input
|
||||
* commands are sent to us directly over the internal pipe */
|
||||
|
||||
#if defined(FILE_CNT)
|
||||
if (stdin->_cnt > 0)
|
||||
return stdin->_cnt;
|
||||
#endif
|
||||
if (!init) {
|
||||
init = 1;
|
||||
int data_available()
|
||||
{
|
||||
static HANDLE inh = NULL;
|
||||
static bool usePipe;
|
||||
INPUT_RECORD rec[256];
|
||||
DWORD dw, recCnt;
|
||||
|
||||
if (!inh)
|
||||
{
|
||||
inh = GetStdHandle(STD_INPUT_HANDLE);
|
||||
pipe = !GetConsoleMode(inh, &dw);
|
||||
if (!pipe) {
|
||||
usePipe = !GetConsoleMode(inh, &dw);
|
||||
if (!usePipe)
|
||||
{
|
||||
SetConsoleMode(inh, dw & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
|
||||
FlushConsoleInputBuffer(inh);
|
||||
}
|
||||
}
|
||||
if (pipe) {
|
||||
if (!PeekNamedPipe(inh, NULL, 0, NULL, &dw, NULL))
|
||||
return 1;
|
||||
return dw;
|
||||
} else {
|
||||
|
||||
// If we're running under XBoard then we can't use PeekConsoleInput() as
|
||||
// the input commands are sent to us directly over the internal pipe.
|
||||
if (usePipe)
|
||||
return PeekNamedPipe(inh, NULL, 0, NULL, &dw, NULL) ? dw : 1;
|
||||
|
||||
// Count the number of unread input records, including keyboard,
|
||||
// mouse, and window-resizing input records.
|
||||
GetNumberOfConsoleInputEvents(inh, &dw);
|
||||
if (dw <= 0)
|
||||
return 0;
|
||||
|
||||
// Read data from console without removing it from the buffer
|
||||
INPUT_RECORD rec[256];
|
||||
DWORD recCnt;
|
||||
if (!PeekConsoleInput(inh, rec, Min(dw, 256), &recCnt))
|
||||
if (dw <= 0 || !PeekConsoleInput(inh, rec, Min(dw, 256), &recCnt))
|
||||
return 0;
|
||||
|
||||
// Search for at least one keyboard event
|
||||
@@ -287,11 +262,11 @@ int Bioskey()
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/// prefetch() preloads the given address in L1/L2 cache. This is a non
|
||||
/// blocking function and do not stalls the CPU waiting for data to be
|
||||
/// loaded from RAM, that can be very slow.
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "application.h"
|
||||
#include "types.h"
|
||||
|
||||
////
|
||||
@@ -47,7 +46,7 @@
|
||||
extern const std::string engine_name();
|
||||
extern int get_system_time();
|
||||
extern int cpu_count();
|
||||
extern int Bioskey();
|
||||
extern int data_available();
|
||||
extern void prefetch(char* addr);
|
||||
extern void prefetchPawn(Key, int);
|
||||
|
||||
@@ -59,9 +58,6 @@ extern void prefetchPawn(Key, int);
|
||||
extern bool dbg_show_mean;
|
||||
extern bool dbg_show_hit_rate;
|
||||
|
||||
extern uint64_t dbg_cnt0;
|
||||
extern uint64_t dbg_cnt1;
|
||||
|
||||
extern void dbg_hit_on(bool b);
|
||||
extern void dbg_hit_on_c(bool c, bool b);
|
||||
extern void dbg_before();
|
||||
@@ -69,7 +65,5 @@ extern void dbg_after();
|
||||
extern void dbg_mean_of(int v);
|
||||
extern void dbg_print_hit_rate();
|
||||
extern void dbg_print_mean();
|
||||
extern void dbg_print_hit_rate(std::ofstream& logFile);
|
||||
extern void dbg_print_mean(std::ofstream& logFile);
|
||||
|
||||
#endif // !defined(MISC_H_INCLUDED)
|
||||
|
||||
53
src/move.cpp
53
src/move.cpp
@@ -23,6 +23,7 @@
|
||||
////
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
|
||||
#include "move.h"
|
||||
#include "piece.h"
|
||||
@@ -33,13 +34,13 @@
|
||||
//// Functions
|
||||
////
|
||||
|
||||
/// move_from_string() takes a position and a string as input, and attempts to
|
||||
/// move_from_uci() takes a position and a string as input, and attempts to
|
||||
/// convert the string to a move, using simple coordinate notation (g1f3,
|
||||
/// a7a8q, etc.). In order to correctly parse en passant captures and castling
|
||||
/// moves, we need the position. This function is not robust, and expects that
|
||||
/// the input move is legal and correctly formatted.
|
||||
|
||||
Move move_from_string(const Position& pos, const std::string& str) {
|
||||
Move move_from_uci(const Position& pos, const std::string& str) {
|
||||
|
||||
Square from, to;
|
||||
Piece piece;
|
||||
@@ -49,15 +50,15 @@ Move move_from_string(const Position& pos, const std::string& str) {
|
||||
return MOVE_NONE;
|
||||
|
||||
// Read the from and to squares
|
||||
from = square_from_string(str.substr(0, 2));
|
||||
to = square_from_string(str.substr(2, 4));
|
||||
from = make_square(file_from_char(str[0]), rank_from_char(str[1]));
|
||||
to = make_square(file_from_char(str[2]), rank_from_char(str[3]));
|
||||
|
||||
// Find the moving piece
|
||||
piece = pos.piece_on(from);
|
||||
|
||||
// If the string has more than 4 characters, try to interpret the 5th
|
||||
// character as a promotion
|
||||
if (type_of_piece(piece) == PAWN && str.length() > 4)
|
||||
// character as a promotion.
|
||||
if (str.length() > 4 && piece == piece_of_color_and_type(us, PAWN))
|
||||
{
|
||||
switch (tolower(str[4])) {
|
||||
case 'n':
|
||||
@@ -71,44 +72,44 @@ Move move_from_string(const Position& pos, const std::string& str) {
|
||||
}
|
||||
}
|
||||
|
||||
// En passant move? We assume that a pawn move is an en passant move
|
||||
// if the destination square is epSquare.
|
||||
if (to == pos.ep_square() && piece == piece_of_color_and_type(us, PAWN))
|
||||
return make_ep_move(from, to);
|
||||
|
||||
// Is this a castling move? A king move is assumed to be a castling move
|
||||
// if the destination square is occupied by a friendly rook, or if the
|
||||
// distance between the source and destination squares is more than 1.
|
||||
if (piece == piece_of_color_and_type(us, KING))
|
||||
{
|
||||
// Is this a castling move? A king move is assumed to be a castling
|
||||
// move if the destination square is occupied by a friendly rook, or
|
||||
// if the distance between the source and destination squares is more
|
||||
// than 1.
|
||||
if (pos.piece_on(to) == piece_of_color_and_type(us, ROOK))
|
||||
return make_castle_move(from, to);
|
||||
|
||||
else if (square_distance(from, to) > 1)
|
||||
if (square_distance(from, to) > 1)
|
||||
{
|
||||
// This is a castling move, but we have to translate it to the
|
||||
// internal "king captures rook" representation.
|
||||
SquareDelta delta = (to > from ? DELTA_E : DELTA_W);
|
||||
Square s = from + delta;
|
||||
while (relative_rank(us, s) == RANK_1 && pos.piece_on(s) != piece_of_color_and_type(us, ROOK))
|
||||
s += delta;
|
||||
Square s = from;
|
||||
|
||||
return (relative_rank(us, s) == RANK_1 ? make_castle_move(from, s) : MOVE_NONE);
|
||||
do s += delta;
|
||||
while ( pos.piece_on(s) != piece_of_color_and_type(us, ROOK)
|
||||
&& relative_rank(us, s) == RANK_1);
|
||||
|
||||
return relative_rank(us, s) == RANK_1 ? make_castle_move(from, s) : MOVE_NONE;
|
||||
}
|
||||
}
|
||||
else if (piece == piece_of_color_and_type(us, PAWN))
|
||||
{
|
||||
// En passant move? We assume that a pawn move is an en passant move
|
||||
// without further testing if the destination square is epSquare.
|
||||
if (to == pos.ep_square())
|
||||
return make_ep_move(from, to);
|
||||
}
|
||||
|
||||
return make_move(from, to);
|
||||
}
|
||||
|
||||
|
||||
/// move_to_string() converts a move to a string in coordinate notation
|
||||
/// move_to_uci() converts a move to a string in coordinate notation
|
||||
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we
|
||||
/// print in the e1g1 notation in normal chess mode, and in e1h1 notation in
|
||||
/// Chess960 mode.
|
||||
|
||||
const std::string move_to_string(Move move, bool chess960) {
|
||||
const std::string move_to_uci(Move move, bool chess960) {
|
||||
|
||||
std::string str;
|
||||
Square from = move_from(move);
|
||||
@@ -128,7 +129,7 @@ const std::string move_to_string(Move move, bool chess960) {
|
||||
|
||||
str = square_to_string(from) + square_to_string(to);
|
||||
if (move_is_promotion(move))
|
||||
str += piece_type_to_char(move_promotion_piece(move), false);
|
||||
str += char(tolower(piece_type_to_char(move_promotion_piece(move))));
|
||||
}
|
||||
return str;
|
||||
}
|
||||
@@ -139,7 +140,7 @@ const std::string move_to_string(Move move, bool chess960) {
|
||||
std::ostream& operator << (std::ostream& os, Move m) {
|
||||
|
||||
bool chess960 = (os.iword(0) != 0); // See set960()
|
||||
return os << move_to_string(m, chess960);
|
||||
return os << move_to_uci(m, chess960);
|
||||
}
|
||||
|
||||
|
||||
|
||||
25
src/move.h
25
src/move.h
@@ -31,6 +31,8 @@
|
||||
#include "piece.h"
|
||||
#include "square.h"
|
||||
|
||||
// Maximum number of allowed moves per position
|
||||
const int MOVES_MAX = 256;
|
||||
|
||||
////
|
||||
//// Types
|
||||
@@ -62,25 +64,24 @@ struct MoveStack {
|
||||
int score;
|
||||
};
|
||||
|
||||
// Note that operator< is set up such that sorting will be in descending order
|
||||
inline bool operator<(const MoveStack& f, const MoveStack& s) { return s.score < f.score; }
|
||||
inline bool operator<(const MoveStack& f, const MoveStack& s) { return f.score < s.score; }
|
||||
|
||||
// An helper insertion sort implementation
|
||||
template<typename T>
|
||||
inline void insertion_sort(T* firstMove, T* lastMove)
|
||||
// An helper insertion sort implementation, works with pointers and iterators
|
||||
template<typename T, typename K>
|
||||
inline void insertion_sort(K firstMove, K lastMove)
|
||||
{
|
||||
T value;
|
||||
T *cur, *p, *d;
|
||||
K cur, p, d;
|
||||
|
||||
if (firstMove != lastMove)
|
||||
for (cur = firstMove + 1; cur != lastMove; cur++)
|
||||
{
|
||||
p = d = cur;
|
||||
value = *p--;
|
||||
if (value < *p)
|
||||
if (*p < value)
|
||||
{
|
||||
do *d = *p;
|
||||
while (--d != firstMove && value < *--p);
|
||||
while (--d != firstMove && *--p < value);
|
||||
*d = value;
|
||||
}
|
||||
}
|
||||
@@ -115,7 +116,7 @@ inline void sort_moves(T* firstMove, T* lastMove, T** lastPositive)
|
||||
} while (p != d);
|
||||
|
||||
// Sort just positive scored moves, remaining only when we get there
|
||||
insertion_sort<T>(firstMove, p);
|
||||
insertion_sort<T, T*>(firstMove, p);
|
||||
*lastPositive = p;
|
||||
}
|
||||
|
||||
@@ -130,7 +131,7 @@ inline T pick_best(T* curMove, T* lastMove)
|
||||
bestMove = *curMove;
|
||||
while (++curMove != lastMove)
|
||||
{
|
||||
if (*curMove < bestMove)
|
||||
if (bestMove < *curMove)
|
||||
{
|
||||
tmp = *curMove;
|
||||
*curMove = bestMove;
|
||||
@@ -202,8 +203,8 @@ inline Move make_ep_move(Square from, Square to) {
|
||||
////
|
||||
|
||||
extern std::ostream& operator<<(std::ostream& os, Move m);
|
||||
extern Move move_from_string(const Position& pos, const std::string &str);
|
||||
extern const std::string move_to_string(Move m, bool chess960);
|
||||
extern Move move_from_uci(const Position& pos, const std::string &str);
|
||||
extern const std::string move_to_uci(Move m, bool chess960);
|
||||
extern bool move_is_ok(Move m);
|
||||
|
||||
|
||||
|
||||
183
src/movegen.cpp
183
src/movegen.cpp
@@ -244,7 +244,7 @@ MoveStack* generate_evasions(const Position& pos, MoveStack* mlist) {
|
||||
case QUEEN:
|
||||
// In case of a queen remove also squares attacked in the other direction to
|
||||
// avoid possible illegal moves when queen and king are on adjacent squares.
|
||||
if (direction_is_straight(checksq, ksq))
|
||||
if (RookPseudoAttacks[checksq] & (1ULL << ksq))
|
||||
sliderAttacks |= RookPseudoAttacks[checksq] | pos.attacks_from<BISHOP>(checksq);
|
||||
else
|
||||
sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq);
|
||||
@@ -308,7 +308,7 @@ MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLega
|
||||
|
||||
bool move_is_legal(const Position& pos, const Move m) {
|
||||
|
||||
MoveStack mlist[256];
|
||||
MoveStack mlist[MOVES_MAX];
|
||||
MoveStack *cur, *last = generate_moves(pos, mlist, true);
|
||||
|
||||
for (cur = mlist; cur != last; cur++)
|
||||
@@ -444,152 +444,135 @@ namespace {
|
||||
return mlist;
|
||||
}
|
||||
|
||||
template<Color Us, SquareDelta Direction>
|
||||
template<SquareDelta Delta>
|
||||
inline Bitboard move_pawns(Bitboard p) {
|
||||
|
||||
if (Direction == DELTA_N)
|
||||
return Us == WHITE ? p << 8 : p >> 8;
|
||||
else if (Direction == DELTA_NE)
|
||||
return Us == WHITE ? p << 9 : p >> 7;
|
||||
else if (Direction == DELTA_NW)
|
||||
return Us == WHITE ? p << 7 : p >> 9;
|
||||
else
|
||||
return p;
|
||||
return Delta == DELTA_N ? p << 8 : Delta == DELTA_S ? p >> 8 :
|
||||
Delta == DELTA_NE ? p << 9 : Delta == DELTA_SE ? p >> 7 :
|
||||
Delta == DELTA_NW ? p << 7 : Delta == DELTA_SW ? p >> 9 : p;
|
||||
}
|
||||
|
||||
template<Color Us, MoveType Type, SquareDelta Diagonal>
|
||||
inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard enemyPieces) {
|
||||
template<MoveType Type, SquareDelta Delta>
|
||||
inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard target) {
|
||||
|
||||
// Calculate our parametrized parameters at compile time
|
||||
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
|
||||
const Bitboard TFileABB = (Diagonal == DELTA_NE ? FileABB : FileHBB);
|
||||
const SquareDelta TDELTA_NE = (Us == WHITE ? DELTA_NE : DELTA_SE);
|
||||
const SquareDelta TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW);
|
||||
const SquareDelta TTDELTA_NE = (Diagonal == DELTA_NE ? TDELTA_NE : TDELTA_NW);
|
||||
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB);
|
||||
|
||||
Bitboard b1, b2;
|
||||
Bitboard b;
|
||||
Square to;
|
||||
|
||||
// Captures in the a1-h8 (a8-h1 for black) diagonal or in the h1-a8 (h8-a1 for black)
|
||||
b1 = move_pawns<Us, Diagonal>(pawns) & ~TFileABB & enemyPieces;
|
||||
b = move_pawns<Delta>(pawns) & target & ~TFileABB;
|
||||
SERIALIZE_MOVES_D(b, -Delta);
|
||||
return mlist;
|
||||
}
|
||||
|
||||
// Capturing promotions and under-promotions
|
||||
if (b1 & TRank8BB)
|
||||
template<Color Us, MoveType Type, SquareDelta Delta>
|
||||
inline MoveStack* generate_promotions(const Position& pos, MoveStack* mlist, Bitboard pawnsOn7, Bitboard target) {
|
||||
|
||||
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB);
|
||||
|
||||
Bitboard b;
|
||||
Square to;
|
||||
|
||||
// Promotions and under-promotions, both captures and non-captures
|
||||
b = move_pawns<Delta>(pawnsOn7) & target;
|
||||
|
||||
if (Delta != DELTA_N && Delta != DELTA_S)
|
||||
b &= ~TFileABB;
|
||||
|
||||
while (b)
|
||||
{
|
||||
b2 = b1 & TRank8BB;
|
||||
b1 &= ~TRank8BB;
|
||||
while (b2)
|
||||
{
|
||||
to = pop_1st_bit(&b2);
|
||||
to = pop_1st_bit(&b);
|
||||
|
||||
if (Type == CAPTURE || Type == EVASION)
|
||||
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, QUEEN);
|
||||
(*mlist++).move = make_promotion_move(to - Delta, to, QUEEN);
|
||||
|
||||
if (Type == NON_CAPTURE || Type == EVASION)
|
||||
{
|
||||
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, ROOK);
|
||||
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, BISHOP);
|
||||
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT);
|
||||
(*mlist++).move = make_promotion_move(to - Delta, to, ROOK);
|
||||
(*mlist++).move = make_promotion_move(to - Delta, to, BISHOP);
|
||||
(*mlist++).move = make_promotion_move(to - Delta, to, KNIGHT);
|
||||
}
|
||||
|
||||
// This is the only possible under promotion that can give a check
|
||||
// not already included in the queen-promotion. It is not sure that
|
||||
// the promoted knight will give check, but it doesn't worth to verify.
|
||||
if (Type == CHECK)
|
||||
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT);
|
||||
// not already included in the queen-promotion.
|
||||
if ( Type == CHECK
|
||||
&& bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(opposite_color(Us))))
|
||||
(*mlist++).move = make_promotion_move(to - Delta, to, KNIGHT);
|
||||
else (void)pos; // Silence a warning under MSVC
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize standard captures
|
||||
if (Type == CAPTURE || Type == EVASION)
|
||||
SERIALIZE_MOVES_D(b1, -TTDELTA_NE);
|
||||
|
||||
return mlist;
|
||||
}
|
||||
|
||||
template<Color Us, MoveType Type>
|
||||
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) {
|
||||
|
||||
// Calculate our parametrized parameters at compile time
|
||||
// Calculate our parametrized parameters at compile time, named
|
||||
// according to the point of view of white side.
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
|
||||
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
|
||||
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
|
||||
const SquareDelta TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S);
|
||||
const SquareDelta TDELTA_NE = (Us == WHITE ? DELTA_NE : DELTA_SE);
|
||||
const SquareDelta TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW);
|
||||
|
||||
Square to;
|
||||
Bitboard b1, b2, enemyPieces, emptySquares;
|
||||
Bitboard b1, b2, dc1, dc2, pawnPushes, emptySquares;
|
||||
Bitboard pawns = pos.pieces(PAWN, Us);
|
||||
Bitboard pawnsOn7 = pawns & TRank7BB;
|
||||
Bitboard enemyPieces = (Type == CAPTURE ? target : pos.pieces_of_color(Them));
|
||||
|
||||
// Standard captures and capturing promotions and underpromotions
|
||||
if (Type == CAPTURE || Type == EVASION || (pawns & TRank7BB))
|
||||
{
|
||||
enemyPieces = (Type == CAPTURE ? target : pos.pieces_of_color(opposite_color(Us)));
|
||||
|
||||
if (Type == EVASION)
|
||||
enemyPieces &= target; // Capture only the checker piece
|
||||
|
||||
mlist = generate_pawn_captures<Us, Type, DELTA_NE>(mlist, pawns, enemyPieces);
|
||||
mlist = generate_pawn_captures<Us, Type, DELTA_NW>(mlist, pawns, enemyPieces);
|
||||
}
|
||||
|
||||
// Non-capturing promotions and underpromotions
|
||||
if (pawns & TRank7BB)
|
||||
{
|
||||
b1 = move_pawns<Us, DELTA_N>(pawns) & TRank8BB & pos.empty_squares();
|
||||
|
||||
if (Type == EVASION)
|
||||
b1 &= target; // Only blocking promotion pushes
|
||||
|
||||
while (b1)
|
||||
{
|
||||
to = pop_1st_bit(&b1);
|
||||
|
||||
if (Type == CAPTURE || Type == EVASION)
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, QUEEN);
|
||||
|
||||
if (Type == NON_CAPTURE || Type == EVASION)
|
||||
{
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, ROOK);
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, BISHOP);
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, KNIGHT);
|
||||
}
|
||||
|
||||
// This is the only possible under promotion that can give a check
|
||||
// not already included in the queen-promotion.
|
||||
if (Type == CHECK && bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(Them)))
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, KNIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
// Standard pawn pushes and double pushes
|
||||
// Pre-calculate pawn pushes before changing emptySquares definition
|
||||
if (Type != CAPTURE)
|
||||
{
|
||||
emptySquares = (Type == NON_CAPTURE ? target : pos.empty_squares());
|
||||
pawnPushes = move_pawns<TDELTA_N>(pawns & ~TRank7BB) & emptySquares;
|
||||
}
|
||||
|
||||
// Single and double pawn pushes
|
||||
b1 = move_pawns<Us, DELTA_N>(pawns) & emptySquares & ~TRank8BB;
|
||||
b2 = move_pawns<Us, DELTA_N>(b1 & TRank3BB) & emptySquares;
|
||||
|
||||
// Filter out unwanted pushes according to the move type
|
||||
if (Type == EVASION)
|
||||
{
|
||||
b1 &= target;
|
||||
b2 &= target;
|
||||
emptySquares &= target; // Only blocking squares
|
||||
enemyPieces &= target; // Capture only the checker piece
|
||||
}
|
||||
else if (Type == CHECK)
|
||||
|
||||
// Promotions and underpromotions
|
||||
if (pawnsOn7)
|
||||
{
|
||||
// Pawn moves which give direct cheks
|
||||
if (Type == CAPTURE)
|
||||
emptySquares = pos.empty_squares();
|
||||
|
||||
pawns &= ~TRank7BB;
|
||||
mlist = generate_promotions<Us, Type, TDELTA_NE>(pos, mlist, pawnsOn7, enemyPieces);
|
||||
mlist = generate_promotions<Us, Type, TDELTA_NW>(pos, mlist, pawnsOn7, enemyPieces);
|
||||
mlist = generate_promotions<Us, Type, TDELTA_N >(pos, mlist, pawnsOn7, emptySquares);
|
||||
}
|
||||
|
||||
// Standard captures
|
||||
if (Type == CAPTURE || Type == EVASION)
|
||||
{
|
||||
mlist = generate_pawn_captures<Type, TDELTA_NE>(mlist, pawns, enemyPieces);
|
||||
mlist = generate_pawn_captures<Type, TDELTA_NW>(mlist, pawns, enemyPieces);
|
||||
}
|
||||
|
||||
// Single and double pawn pushes
|
||||
if (Type != CAPTURE)
|
||||
{
|
||||
b1 = pawnPushes & emptySquares;
|
||||
b2 = move_pawns<TDELTA_N>(pawnPushes & TRank3BB) & emptySquares;
|
||||
|
||||
if (Type == CHECK)
|
||||
{
|
||||
// Condider only pawn moves which give direct checks
|
||||
b1 &= pos.attacks_from<PAWN>(ksq, Them);
|
||||
b2 &= pos.attacks_from<PAWN>(ksq, Them);
|
||||
|
||||
// Pawn moves which gives discovered check. This is possible only if
|
||||
// the pawn is not on the same file as the enemy king, because we
|
||||
// Add pawn moves which gives discovered check. This is possible only
|
||||
// if the pawn is not on the same file as the enemy king, because we
|
||||
// don't generate captures.
|
||||
if (pawns & target) // For CHECK type target is dc bitboard
|
||||
{
|
||||
Bitboard dc1 = move_pawns<Us, DELTA_N>(pawns & target & ~file_bb(ksq)) & emptySquares & ~TRank8BB;
|
||||
Bitboard dc2 = move_pawns<Us, DELTA_N>(dc1 & TRank3BB) & emptySquares;
|
||||
dc1 = move_pawns<TDELTA_N>(pawns & target & ~file_bb(ksq)) & emptySquares;
|
||||
dc2 = move_pawns<TDELTA_N>(dc1 & TRank3BB) & emptySquares;
|
||||
|
||||
b1 |= dc1;
|
||||
b2 |= dc2;
|
||||
|
||||
@@ -75,7 +75,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
||||
int searchTT = ttm;
|
||||
ttMoves[0].move = ttm;
|
||||
badCaptureThreshold = 0;
|
||||
badCaptures = moves + 256;
|
||||
badCaptures = moves + MOVES_MAX;
|
||||
|
||||
pinned = p.pinned_pieces(pos.side_to_move());
|
||||
|
||||
@@ -99,7 +99,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
||||
|
||||
phasePtr = MainSearchPhaseTable;
|
||||
}
|
||||
else if (d == DEPTH_ZERO)
|
||||
else if (d >= DEPTH_QS_CHECKS)
|
||||
phasePtr = QsearchWithChecksPhaseTable;
|
||||
else
|
||||
{
|
||||
@@ -151,7 +151,7 @@ void MovePicker::go_next_phase() {
|
||||
// Bad captures SEE value is already calculated so just pick
|
||||
// them in order to get SEE move ordering.
|
||||
curMove = badCaptures;
|
||||
lastMove = moves + 256;
|
||||
lastMove = moves + MOVES_MAX;
|
||||
return;
|
||||
|
||||
case PH_EVASIONS:
|
||||
@@ -313,7 +313,7 @@ Move MovePicker::get_next_move() {
|
||||
|
||||
// Sort negative scored moves only when we get there
|
||||
if (curMove == lastGoodNonCapture)
|
||||
insertion_sort(lastGoodNonCapture, lastMove);
|
||||
insertion_sort<MoveStack>(lastGoodNonCapture, lastMove);
|
||||
|
||||
move = (curMove++)->move;
|
||||
if ( move != ttMoves[0].move
|
||||
|
||||
@@ -66,7 +66,7 @@ private:
|
||||
int badCaptureThreshold, phase;
|
||||
const uint8_t* phasePtr;
|
||||
MoveStack *curMove, *lastMove, *lastGoodNonCapture, *badCaptures;
|
||||
MoveStack moves[256];
|
||||
MoveStack moves[MOVES_MAX];
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -40,23 +40,26 @@ namespace {
|
||||
|
||||
#define S(mg, eg) make_score(mg, eg)
|
||||
|
||||
// Doubled pawn penalty by file
|
||||
const Score DoubledPawnPenalty[8] = {
|
||||
S(13, 43), S(20, 48), S(23, 48), S(23, 48),
|
||||
S(23, 48), S(23, 48), S(20, 48), S(13, 43)
|
||||
};
|
||||
// Doubled pawn penalty by opposed flag and file
|
||||
const Score DoubledPawnPenalty[2][8] = {
|
||||
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48),
|
||||
S(23, 48), S(23, 48), S(20, 48), S(13, 43) },
|
||||
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48),
|
||||
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }};
|
||||
|
||||
// Isolated pawn penalty by file
|
||||
const Score IsolatedPawnPenalty[8] = {
|
||||
S(25, 30), S(36, 35), S(40, 35), S(40, 35),
|
||||
S(40, 35), S(40, 35), S(36, 35), S(25, 30)
|
||||
};
|
||||
// Isolated pawn penalty by opposed flag and file
|
||||
const Score IsolatedPawnPenalty[2][8] = {
|
||||
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52),
|
||||
S(60, 52), S(60, 52), S(54, 52), S(37, 45) },
|
||||
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35),
|
||||
S(40, 35), S(40, 35), S(36, 35), S(25, 30) }};
|
||||
|
||||
// Backward pawn penalty by file
|
||||
const Score BackwardPawnPenalty[8] = {
|
||||
S(20, 28), S(29, 31), S(33, 31), S(33, 31),
|
||||
S(33, 31), S(33, 31), S(29, 31), S(20, 28)
|
||||
};
|
||||
// Backward pawn penalty by opposed flag and file
|
||||
const Score BackwardPawnPenalty[2][8] = {
|
||||
{ S(30, 42), S(43, 46), S(49, 46), S(49, 46),
|
||||
S(49, 46), S(49, 46), S(43, 46), S(30, 42) },
|
||||
{ S(20, 28), S(29, 31), S(33, 31), S(33, 31),
|
||||
S(33, 31), S(33, 31), S(29, 31), S(20, 28) }};
|
||||
|
||||
// Pawn chain membership bonus by file
|
||||
const Score ChainBonus[8] = {
|
||||
@@ -88,8 +91,9 @@ PawnInfoTable::PawnInfoTable() {
|
||||
{
|
||||
std::cerr << "Failed to allocate " << (PawnTableSize * sizeof(PawnInfo))
|
||||
<< " bytes for pawn hash table." << std::endl;
|
||||
Application::exit_with_failure();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
memset(entries, 0, PawnTableSize * sizeof(PawnInfo));
|
||||
}
|
||||
|
||||
|
||||
@@ -120,18 +124,19 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const {
|
||||
|
||||
// Clear the PawnInfo object, and set the key
|
||||
memset(pi, 0, sizeof(PawnInfo));
|
||||
pi->halfOpenFiles[WHITE] = pi->halfOpenFiles[BLACK] = 0xFF;
|
||||
pi->kingSquares[WHITE] = pi->kingSquares[BLACK] = SQ_NONE;
|
||||
pi->key = key;
|
||||
|
||||
// Calculate pawn attacks
|
||||
Bitboard whitePawns = pos.pieces(PAWN, WHITE);
|
||||
Bitboard blackPawns = pos.pieces(PAWN, BLACK);
|
||||
pi->pawnAttacks[WHITE] = ((whitePawns << 9) & ~FileABB) | ((whitePawns << 7) & ~FileHBB);
|
||||
pi->pawnAttacks[BLACK] = ((blackPawns >> 7) & ~FileABB) | ((blackPawns >> 9) & ~FileHBB);
|
||||
Bitboard wPawns = pos.pieces(PAWN, WHITE);
|
||||
Bitboard bPawns = pos.pieces(PAWN, BLACK);
|
||||
pi->pawnAttacks[WHITE] = ((wPawns << 9) & ~FileABB) | ((wPawns << 7) & ~FileHBB);
|
||||
pi->pawnAttacks[BLACK] = ((bPawns >> 7) & ~FileABB) | ((bPawns >> 9) & ~FileHBB);
|
||||
|
||||
// Evaluate pawns for both colors
|
||||
pi->value = evaluate_pawns<WHITE>(pos, whitePawns, blackPawns, pi)
|
||||
- evaluate_pawns<BLACK>(pos, blackPawns, whitePawns, pi);
|
||||
pi->value = evaluate_pawns<WHITE>(pos, wPawns, bPawns, pi)
|
||||
- evaluate_pawns<BLACK>(pos, bPawns, wPawns, pi);
|
||||
return pi;
|
||||
}
|
||||
|
||||
@@ -150,11 +155,6 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
|
||||
const BitCountType Max15 = CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
|
||||
const Square* ptr = pos.piece_list_begin(Us, PAWN);
|
||||
|
||||
// Initialize halfOpenFiles[]
|
||||
for (f = FILE_A; f <= FILE_H; f++)
|
||||
if (!(ourPawns & file_bb(f)))
|
||||
pi->halfOpenFiles[Us] |= (1 << f);
|
||||
|
||||
// Loop through all pawns of the current color and score each pawn
|
||||
while ((s = *ptr++) != SQ_NONE)
|
||||
{
|
||||
@@ -163,13 +163,16 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
|
||||
f = square_file(s);
|
||||
r = square_rank(s);
|
||||
|
||||
// This file cannot be half open
|
||||
pi->halfOpenFiles[Us] &= ~(1 << f);
|
||||
|
||||
// Our rank plus previous one. Used for chain detection.
|
||||
b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1));
|
||||
|
||||
// Passed, isolated, doubled or member of a pawn
|
||||
// chain (but not the backward one) ?
|
||||
passed = !(theirPawns & passed_pawn_mask(Us, s));
|
||||
doubled = ourPawns & squares_behind(Us, s);
|
||||
doubled = ourPawns & squares_in_front_of(Us, s);
|
||||
opposed = theirPawns & squares_in_front_of(Us, s);
|
||||
isolated = !(ourPawns & neighboring_files_bb(f));
|
||||
chain = ourPawns & neighboring_files_bb(f) & b;
|
||||
@@ -209,40 +212,27 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
|
||||
&& (b = attack_span_mask(opposite_color(Us), s + pawn_push(Us)) & ourPawns) != EmptyBoardBB
|
||||
&& count_1s<Max15>(b) >= count_1s<Max15>(attack_span_mask(Us, s) & theirPawns);
|
||||
|
||||
// In order to prevent doubled passed pawns from receiving a too big
|
||||
// bonus, only the frontmost passed pawn on each file is considered as
|
||||
// a true passed pawn.
|
||||
if (passed && (ourPawns & squares_in_front_of(Us, s)))
|
||||
passed = false;
|
||||
|
||||
// Mark the pawn as passed. Pawn will be properly scored in evaluation
|
||||
// because we need full attack info to evaluate passed pawns.
|
||||
if (passed)
|
||||
// because we need full attack info to evaluate passed pawns. Only the
|
||||
// frontmost passed pawn on each file is considered a true passed pawn.
|
||||
if (passed && !doubled)
|
||||
set_bit(&(pi->passedPawns[Us]), s);
|
||||
|
||||
// Score this pawn
|
||||
if (isolated)
|
||||
{
|
||||
value -= IsolatedPawnPenalty[f];
|
||||
if (!opposed)
|
||||
value -= IsolatedPawnPenalty[f] / 2;
|
||||
}
|
||||
value -= IsolatedPawnPenalty[opposed][f];
|
||||
|
||||
if (doubled)
|
||||
value -= DoubledPawnPenalty[f];
|
||||
value -= DoubledPawnPenalty[opposed][f];
|
||||
|
||||
if (backward)
|
||||
{
|
||||
value -= BackwardPawnPenalty[f];
|
||||
if (!opposed)
|
||||
value -= BackwardPawnPenalty[f] / 2;
|
||||
}
|
||||
value -= BackwardPawnPenalty[opposed][f];
|
||||
|
||||
if (chain)
|
||||
value += ChainBonus[f];
|
||||
|
||||
if (candidate)
|
||||
value += CandidateBonus[relative_rank(Us, s)];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,6 +78,9 @@ class PawnInfoTable {
|
||||
|
||||
enum SideType { KingSide, QueenSide };
|
||||
|
||||
PawnInfoTable(const PawnInfoTable&);
|
||||
PawnInfoTable& operator=(const PawnInfoTable&);
|
||||
|
||||
public:
|
||||
PawnInfoTable();
|
||||
~PawnInfoTable();
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
|
||||
Stockfish 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.
|
||||
|
||||
Stockfish 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/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "piece.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Tables indexed by Piece
|
||||
|
||||
const Value PieceValueMidgame[17] = {
|
||||
VALUE_ZERO,
|
||||
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
|
||||
RookValueMidgame, QueenValueMidgame,
|
||||
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
|
||||
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
|
||||
RookValueMidgame, QueenValueMidgame,
|
||||
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO
|
||||
};
|
||||
|
||||
const Value PieceValueEndgame[17] = {
|
||||
VALUE_ZERO,
|
||||
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
|
||||
RookValueEndgame, QueenValueEndgame,
|
||||
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
|
||||
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
|
||||
RookValueEndgame, QueenValueEndgame,
|
||||
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO
|
||||
};
|
||||
|
||||
const int SlidingArray[18] = {
|
||||
0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
/// Translating piece types to/from English piece letters
|
||||
|
||||
static const string PieceChars(" pnbrqk PNBRQK");
|
||||
|
||||
char piece_type_to_char(PieceType pt, bool upcase) {
|
||||
|
||||
return PieceChars[pt + int(upcase) * 7];
|
||||
}
|
||||
|
||||
PieceType piece_type_from_char(char c) {
|
||||
|
||||
size_t idx = PieceChars.find(c);
|
||||
|
||||
return idx != string::npos ? PieceType(idx % 7) : PIECE_TYPE_NONE;
|
||||
}
|
||||
31
src/piece.h
31
src/piece.h
@@ -25,6 +25,8 @@
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "color.h"
|
||||
#include "square.h"
|
||||
#include "value.h"
|
||||
@@ -69,10 +71,6 @@ const Value RookValueEndgame = Value(0x4FE);
|
||||
const Value QueenValueMidgame = Value(0x9D9);
|
||||
const Value QueenValueEndgame = Value(0x9FE);
|
||||
|
||||
extern const Value PieceValueMidgame[17];
|
||||
extern const Value PieceValueEndgame[17];
|
||||
extern const int SlidingArray[18];
|
||||
|
||||
|
||||
////
|
||||
//// Inline functions
|
||||
@@ -90,29 +88,24 @@ inline Piece piece_of_color_and_type(Color c, PieceType pt) {
|
||||
return Piece((int(c) << 3) | int(pt));
|
||||
}
|
||||
|
||||
inline int piece_is_slider(Piece p) {
|
||||
return SlidingArray[p];
|
||||
}
|
||||
|
||||
inline SquareDelta pawn_push(Color c) {
|
||||
return (c == WHITE ? DELTA_N : DELTA_S);
|
||||
}
|
||||
|
||||
inline bool piece_type_is_ok(PieceType pc) {
|
||||
return pc >= PAWN && pc <= KING;
|
||||
inline bool piece_type_is_ok(PieceType pt) {
|
||||
return pt >= PAWN && pt <= KING;
|
||||
}
|
||||
|
||||
inline bool piece_is_ok(Piece pc) {
|
||||
return piece_type_is_ok(type_of_piece(pc)) && color_is_ok(color_of_piece(pc));
|
||||
inline bool piece_is_ok(Piece p) {
|
||||
return piece_type_is_ok(type_of_piece(p)) && color_is_ok(color_of_piece(p));
|
||||
}
|
||||
|
||||
inline char piece_type_to_char(PieceType pt) {
|
||||
return std::string(" PNBRQK")[pt];
|
||||
}
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern char piece_type_to_char(PieceType pt, bool upcase = false);
|
||||
extern PieceType piece_type_from_char(char c);
|
||||
|
||||
inline PieceType piece_type_from_char(char c) {
|
||||
return PieceType(std::string(" PNBRQK").find(c));
|
||||
}
|
||||
|
||||
#endif // !defined(PIECE_H_INCLUDED)
|
||||
|
||||
311
src/position.cpp
311
src/position.cpp
@@ -31,11 +31,11 @@
|
||||
#include <sstream>
|
||||
|
||||
#include "bitcount.h"
|
||||
#include "mersenne.h"
|
||||
#include "movegen.h"
|
||||
#include "movepick.h"
|
||||
#include "position.h"
|
||||
#include "psqtab.h"
|
||||
#include "rkiss.h"
|
||||
#include "san.h"
|
||||
#include "tt.h"
|
||||
#include "ucioption.h"
|
||||
@@ -44,9 +44,54 @@ using std::string;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
static inline bool isZero(char c) { return c == '0'; }
|
||||
|
||||
struct PieceLetters : public std::map<char, Piece> {
|
||||
////
|
||||
//// Position's static data definitions
|
||||
////
|
||||
|
||||
Key Position::zobrist[2][8][64];
|
||||
Key Position::zobEp[64];
|
||||
Key Position::zobCastle[16];
|
||||
Key Position::zobSideToMove;
|
||||
Key Position::zobExclusion;
|
||||
|
||||
Score Position::PieceSquareTable[16][64];
|
||||
|
||||
// Material values arrays, indexed by Piece
|
||||
const Value Position::PieceValueMidgame[17] = {
|
||||
VALUE_ZERO,
|
||||
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
|
||||
RookValueMidgame, QueenValueMidgame, VALUE_ZERO,
|
||||
VALUE_ZERO, VALUE_ZERO,
|
||||
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
|
||||
RookValueMidgame, QueenValueMidgame
|
||||
};
|
||||
|
||||
const Value Position::PieceValueEndgame[17] = {
|
||||
VALUE_ZERO,
|
||||
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
|
||||
RookValueEndgame, QueenValueEndgame, VALUE_ZERO,
|
||||
VALUE_ZERO, VALUE_ZERO,
|
||||
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
|
||||
RookValueEndgame, QueenValueEndgame
|
||||
};
|
||||
|
||||
// Material values array used by SEE, indexed by PieceType
|
||||
const Value Position::seeValues[] = {
|
||||
VALUE_ZERO,
|
||||
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
|
||||
RookValueMidgame, QueenValueMidgame, QueenValueMidgame*10
|
||||
};
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
// Bonus for having the side to move (modified by Joona Kiiski)
|
||||
const Score TempoValue = make_score(48, 22);
|
||||
|
||||
bool isZero(char c) { return c == '0'; }
|
||||
|
||||
struct PieceLetters : public std::map<char, Piece> {
|
||||
|
||||
PieceLetters() {
|
||||
|
||||
@@ -56,7 +101,8 @@ struct PieceLetters : public std::map<char, Piece> {
|
||||
operator[]('B') = WB; operator[]('b') = BB;
|
||||
operator[]('N') = WN; operator[]('n') = BN;
|
||||
operator[]('P') = WP; operator[]('p') = BP;
|
||||
operator[](' ') = PIECE_NONE; operator[]('.') = PIECE_NONE_DARK_SQ;
|
||||
operator[](' ') = PIECE_NONE;
|
||||
operator[]('.') = PIECE_NONE_DARK_SQ;
|
||||
}
|
||||
|
||||
char from_piece(Piece p) const {
|
||||
@@ -69,36 +115,13 @@ struct PieceLetters : public std::map<char, Piece> {
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
PieceLetters pieceLetters;
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
//// Constants and variables
|
||||
////
|
||||
|
||||
/// Bonus for having the side to move (modified by Joona Kiiski)
|
||||
|
||||
static const Score TempoValue = make_score(48, 22);
|
||||
|
||||
|
||||
Key Position::zobrist[2][8][64];
|
||||
Key Position::zobEp[64];
|
||||
Key Position::zobCastle[16];
|
||||
Key Position::zobSideToMove;
|
||||
Key Position::zobExclusion;
|
||||
|
||||
Score Position::PieceSquareTable[16][64];
|
||||
|
||||
static PieceLetters pieceLetters;
|
||||
|
||||
// Material values used by SEE, indexed by PieceType
|
||||
const Value Position::seeValues[] = {
|
||||
VALUE_ZERO, PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
|
||||
RookValueMidgame, QueenValueMidgame, QueenValueMidgame*10
|
||||
};
|
||||
|
||||
|
||||
/// Constructors
|
||||
/// CheckInfo c'tor
|
||||
|
||||
CheckInfo::CheckInfo(const Position& pos) {
|
||||
|
||||
@@ -121,13 +144,12 @@ CheckInfo::CheckInfo(const Position& pos) {
|
||||
/// or the FEN string, we want the new born Position object do not depend
|
||||
/// on any external data so we detach state pointer from the source one.
|
||||
|
||||
Position::Position(int th) : threadID(th) {}
|
||||
|
||||
Position::Position(const Position& pos, int th) {
|
||||
|
||||
memcpy(this, &pos, sizeof(Position));
|
||||
detach(); // Always detach() in copy c'tor to avoid surprises
|
||||
threadID = th;
|
||||
nodes = 0;
|
||||
}
|
||||
|
||||
Position::Position(const string& fen, int th) {
|
||||
@@ -293,7 +315,7 @@ bool Position::set_castling_rights(char token) {
|
||||
for (Square sq = sqH; sq >= sqA; sq--)
|
||||
if (piece_on(sq) == rook)
|
||||
{
|
||||
allow_oo(c);
|
||||
do_allow_oo(c);
|
||||
initialKRFile = square_file(sq);
|
||||
break;
|
||||
}
|
||||
@@ -303,7 +325,7 @@ bool Position::set_castling_rights(char token) {
|
||||
for (Square sq = sqA; sq <= sqH; sq++)
|
||||
if (piece_on(sq) == rook)
|
||||
{
|
||||
allow_ooo(c);
|
||||
do_allow_ooo(c);
|
||||
initialQRFile = square_file(sq);
|
||||
break;
|
||||
}
|
||||
@@ -313,12 +335,12 @@ bool Position::set_castling_rights(char token) {
|
||||
File rookFile = File(token - 'A') + FILE_A;
|
||||
if (rookFile < initialKFile)
|
||||
{
|
||||
allow_ooo(c);
|
||||
do_allow_ooo(c);
|
||||
initialQRFile = rookFile;
|
||||
}
|
||||
else
|
||||
{
|
||||
allow_oo(c);
|
||||
do_allow_oo(c);
|
||||
initialKRFile = rookFile;
|
||||
}
|
||||
}
|
||||
@@ -382,7 +404,7 @@ const string Position::to_fen() const {
|
||||
|
||||
|
||||
/// Position::print() prints an ASCII representation of the position to
|
||||
/// the standard output. If a move is given then also the san is print.
|
||||
/// the standard output. If a move is given then also the san is printed.
|
||||
|
||||
void Position::print(Move move) const {
|
||||
|
||||
@@ -502,16 +524,24 @@ Bitboard Position::attacks_from(Piece p, Square s) const {
|
||||
|
||||
switch (p)
|
||||
{
|
||||
case WP: return attacks_from<PAWN>(s, WHITE);
|
||||
case BP: return attacks_from<PAWN>(s, BLACK);
|
||||
case WN: case BN: return attacks_from<KNIGHT>(s);
|
||||
case WB: case BB: return attacks_from<BISHOP>(s);
|
||||
case WR: case BR: return attacks_from<ROOK>(s);
|
||||
case WQ: case BQ: return attacks_from<QUEEN>(s);
|
||||
case WK: case BK: return attacks_from<KING>(s);
|
||||
default: break;
|
||||
default: return StepAttackBB[p][s];
|
||||
}
|
||||
}
|
||||
|
||||
Bitboard Position::attacks_from(Piece p, Square s, Bitboard occ) {
|
||||
|
||||
assert(square_is_ok(s));
|
||||
|
||||
switch (p)
|
||||
{
|
||||
case WB: case BB: return bishop_attacks_bb(s, occ);
|
||||
case WR: case BR: return rook_attacks_bb(s, occ);
|
||||
case WQ: case BQ: return bishop_attacks_bb(s, occ) | rook_attacks_bb(s, occ);
|
||||
default: return StepAttackBB[p][s];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -523,6 +553,7 @@ bool Position::move_attacks_square(Move m, Square s) const {
|
||||
assert(move_is_ok(m));
|
||||
assert(square_is_ok(s));
|
||||
|
||||
Bitboard occ, xray;
|
||||
Square f = move_from(m), t = move_to(m);
|
||||
|
||||
assert(square_is_occupied(f));
|
||||
@@ -531,12 +562,11 @@ bool Position::move_attacks_square(Move m, Square s) const {
|
||||
return true;
|
||||
|
||||
// Move the piece and scan for X-ray attacks behind it
|
||||
Bitboard occ = occupied_squares();
|
||||
Color us = color_of_piece_on(f);
|
||||
clear_bit(&occ, f);
|
||||
set_bit(&occ, t);
|
||||
Bitboard xray = ( (rook_attacks_bb(s, occ) & pieces(ROOK, QUEEN))
|
||||
|(bishop_attacks_bb(s, occ) & pieces(BISHOP, QUEEN))) & pieces_of_color(us);
|
||||
occ = occupied_squares();
|
||||
do_move_bb(&occ, make_move_bb(f, t));
|
||||
xray = ( (rook_attacks_bb(s, occ) & pieces(ROOK, QUEEN))
|
||||
|(bishop_attacks_bb(s, occ) & pieces(BISHOP, QUEEN)))
|
||||
& pieces_of_color(color_of_piece_on(f));
|
||||
|
||||
// If we have attacks we need to verify that are caused by our move
|
||||
// and are not already existent ones.
|
||||
@@ -569,22 +599,18 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
|
||||
if (move_is_castle(m))
|
||||
return true;
|
||||
|
||||
Color us = side_to_move();
|
||||
Square from = move_from(m);
|
||||
|
||||
assert(color_of_piece_on(from) == us);
|
||||
assert(piece_on(king_square(us)) == piece_of_color_and_type(us, KING));
|
||||
|
||||
// En passant captures are a tricky special case. Because they are
|
||||
// rather uncommon, we do it simply by testing whether the king is attacked
|
||||
// after the move is made
|
||||
if (move_is_ep(m))
|
||||
{
|
||||
Color us = side_to_move();
|
||||
Color them = opposite_color(us);
|
||||
Square from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
Square capsq = make_square(square_file(to), square_rank(from));
|
||||
Bitboard b = occupied_squares();
|
||||
Square ksq = king_square(us);
|
||||
Bitboard b = occupied_squares();
|
||||
|
||||
assert(to == ep_square());
|
||||
assert(piece_on(from) == piece_of_color_and_type(us, PAWN));
|
||||
@@ -599,6 +625,12 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
|
||||
&& !(bishop_attacks_bb(ksq, b) & pieces(BISHOP, QUEEN, them));
|
||||
}
|
||||
|
||||
Color us = side_to_move();
|
||||
Square from = move_from(m);
|
||||
|
||||
assert(color_of_piece_on(from) == us);
|
||||
assert(piece_on(king_square(us)) == piece_of_color_and_type(us, KING));
|
||||
|
||||
// If the moving piece is a king, check whether the destination
|
||||
// square is attacked by the opponent.
|
||||
if (type_of_piece_on(from) == KING)
|
||||
@@ -606,9 +638,9 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
|
||||
|
||||
// A non-king move is legal if and only if it is not pinned or it
|
||||
// is moving along the ray towards or away from the king.
|
||||
return ( !pinned
|
||||
return !pinned
|
||||
|| !bit_is_set(pinned, from)
|
||||
|| (direction_between_squares(from, king_square(us)) == direction_between_squares(move_to(m), king_square(us))));
|
||||
|| squares_aligned(from, move_to(m), king_square(us));
|
||||
}
|
||||
|
||||
|
||||
@@ -666,7 +698,7 @@ bool Position::move_is_check(Move m, const CheckInfo& ci) const {
|
||||
{
|
||||
// For pawn and king moves we need to verify also direction
|
||||
if ( (pt != PAWN && pt != KING)
|
||||
||(direction_between_squares(from, ci.ksq) != direction_between_squares(to, ci.ksq)))
|
||||
|| !squares_aligned(from, to, ci.ksq))
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -752,6 +784,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||
assert(is_ok());
|
||||
assert(move_is_ok(m));
|
||||
|
||||
nodes++;
|
||||
Key key = st->key;
|
||||
|
||||
// Copy some fields of old state to our new StateInfo object except the
|
||||
@@ -765,7 +798,9 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||
Value npMaterial[2];
|
||||
};
|
||||
|
||||
if (&newSt != st)
|
||||
memcpy(&newSt, st, sizeof(ReducedStateInfo));
|
||||
|
||||
newSt.previous = st;
|
||||
st = &newSt;
|
||||
|
||||
@@ -1369,7 +1404,7 @@ int Position::see_sign(Move m) const {
|
||||
|
||||
int Position::see(Square from, Square to) const {
|
||||
|
||||
Bitboard occ, attackers, stmAttackers, b;
|
||||
Bitboard occupied, attackers, stmAttackers, b;
|
||||
int swapList[32], slIndex = 1;
|
||||
PieceType capturedType, pt;
|
||||
Color stm;
|
||||
@@ -1383,26 +1418,26 @@ int Position::see(Square from, Square to) const {
|
||||
if (capturedType == KING)
|
||||
return seeValues[capturedType];
|
||||
|
||||
occ = occupied_squares();
|
||||
occupied = occupied_squares();
|
||||
|
||||
// Handle en passant moves
|
||||
if (st->epSquare == to && type_of_piece_on(from) == PAWN)
|
||||
{
|
||||
Square capQq = (side_to_move() == WHITE) ? (to - DELTA_N) : (to - DELTA_S);
|
||||
Square capQq = (side_to_move() == WHITE ? to - DELTA_N : to - DELTA_S);
|
||||
|
||||
assert(capturedType == PIECE_TYPE_NONE);
|
||||
assert(type_of_piece_on(capQq) == PAWN);
|
||||
|
||||
// Remove the captured pawn
|
||||
clear_bit(&occ, capQq);
|
||||
clear_bit(&occupied, capQq);
|
||||
capturedType = PAWN;
|
||||
}
|
||||
|
||||
// Find all attackers to the destination square, with the moving piece
|
||||
// removed, but possibly an X-ray attacker added behind it.
|
||||
clear_bit(&occ, from);
|
||||
attackers = (rook_attacks_bb(to, occ) & pieces(ROOK, QUEEN))
|
||||
| (bishop_attacks_bb(to, occ) & pieces(BISHOP, QUEEN))
|
||||
clear_bit(&occupied, from);
|
||||
attackers = (rook_attacks_bb(to, occupied) & pieces(ROOK, QUEEN))
|
||||
| (bishop_attacks_bb(to, occupied)& pieces(BISHOP, QUEEN))
|
||||
| (attacks_from<KNIGHT>(to) & pieces(KNIGHT))
|
||||
| (attacks_from<KING>(to) & pieces(KING))
|
||||
| (attacks_from<PAWN>(to, WHITE) & pieces(PAWN, BLACK))
|
||||
@@ -1430,28 +1465,28 @@ int Position::see(Square from, Square to) const {
|
||||
for (pt = PAWN; !(stmAttackers & pieces(pt)); pt++)
|
||||
assert(pt < KING);
|
||||
|
||||
// Remove the attacker we just found from the 'attackers' bitboard,
|
||||
// Remove the attacker we just found from the 'occupied' bitboard,
|
||||
// and scan for new X-ray attacks behind the attacker.
|
||||
b = stmAttackers & pieces(pt);
|
||||
occ ^= (b & (~b + 1));
|
||||
attackers |= (rook_attacks_bb(to, occ) & pieces(ROOK, QUEEN))
|
||||
| (bishop_attacks_bb(to, occ) & pieces(BISHOP, QUEEN));
|
||||
occupied ^= (b & (~b + 1));
|
||||
attackers |= (rook_attacks_bb(to, occupied) & pieces(ROOK, QUEEN))
|
||||
| (bishop_attacks_bb(to, occupied) & pieces(BISHOP, QUEEN));
|
||||
|
||||
attackers &= occ; // Cut out pieces we've already done
|
||||
attackers &= occupied; // Cut out pieces we've already done
|
||||
|
||||
// Add the new entry to the swap list
|
||||
assert(slIndex < 32);
|
||||
swapList[slIndex] = -swapList[slIndex - 1] + seeValues[capturedType];
|
||||
slIndex++;
|
||||
|
||||
// Remember the value of the capturing piece, and change the side to move
|
||||
// before beginning the next iteration
|
||||
// Remember the value of the capturing piece, and change the side to
|
||||
// move before beginning the next iteration.
|
||||
capturedType = pt;
|
||||
stm = opposite_color(stm);
|
||||
stmAttackers = attackers & pieces_of_color(stm);
|
||||
|
||||
// Stop after a king capture
|
||||
if (pt == KING && stmAttackers)
|
||||
// Stop before processing a king capture
|
||||
if (capturedType == KING && stmAttackers)
|
||||
{
|
||||
assert(slIndex < 32);
|
||||
swapList[slIndex++] = QueenValueMidgame*10;
|
||||
@@ -1460,7 +1495,7 @@ int Position::see(Square from, Square to) const {
|
||||
} while (stmAttackers);
|
||||
|
||||
// Having built the swap list, we negamax through it to find the best
|
||||
// achievable score from the point of view of the side to move
|
||||
// achievable score from the point of view of the side to move.
|
||||
while (--slIndex)
|
||||
swapList[slIndex-1] = Min(-swapList[slIndex], swapList[slIndex-1]);
|
||||
|
||||
@@ -1477,6 +1512,7 @@ void Position::clear() {
|
||||
memset(st, 0, sizeof(StateInfo));
|
||||
st->epSquare = SQ_NONE;
|
||||
startPosPlyCounter = 0;
|
||||
nodes = 0;
|
||||
|
||||
memset(byColorBB, 0, sizeof(Bitboard) * 2);
|
||||
memset(byTypeBB, 0, sizeof(Bitboard) * 8);
|
||||
@@ -1517,7 +1553,7 @@ void Position::inc_startpos_ply_counter() {
|
||||
}
|
||||
|
||||
/// Position::put_piece() puts a piece on the given square of the board,
|
||||
/// updating the board array, bitboards, and piece counts.
|
||||
/// updating the board array, pieces list, bitboards, and piece counts.
|
||||
|
||||
void Position::put_piece(Piece p, Square s) {
|
||||
|
||||
@@ -1525,32 +1561,12 @@ void Position::put_piece(Piece p, Square s) {
|
||||
PieceType pt = type_of_piece(p);
|
||||
|
||||
board[s] = p;
|
||||
index[s] = pieceCount[c][pt];
|
||||
index[s] = pieceCount[c][pt]++;
|
||||
pieceList[c][pt][index[s]] = s;
|
||||
|
||||
set_bit(&(byTypeBB[pt]), s);
|
||||
set_bit(&(byColorBB[c]), s);
|
||||
set_bit(&byTypeBB[0], s); // HACK: byTypeBB[0] contains all occupied squares.
|
||||
|
||||
pieceCount[c][pt]++;
|
||||
}
|
||||
|
||||
|
||||
/// Position::allow_oo() gives the given side the right to castle kingside.
|
||||
/// Used when setting castling rights during parsing of FEN strings.
|
||||
|
||||
void Position::allow_oo(Color c) {
|
||||
|
||||
st->castleRights |= (1 + int(c));
|
||||
}
|
||||
|
||||
|
||||
/// Position::allow_ooo() gives the given side the right to castle queenside.
|
||||
/// Used when setting castling rights during parsing of FEN strings.
|
||||
|
||||
void Position::allow_ooo(Color c) {
|
||||
|
||||
st->castleRights |= (4 + 4*int(c));
|
||||
set_bit(&(byTypeBB[0]), s); // HACK: byTypeBB[0] contains all occupied squares.
|
||||
}
|
||||
|
||||
|
||||
@@ -1561,7 +1577,7 @@ void Position::allow_ooo(Color c) {
|
||||
|
||||
Key Position::compute_key() const {
|
||||
|
||||
Key result = Key(0ULL);
|
||||
Key result = zobCastle[st->castleRights];
|
||||
|
||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
||||
if (square_is_occupied(s))
|
||||
@@ -1570,7 +1586,6 @@ Key Position::compute_key() const {
|
||||
if (ep_square() != SQ_NONE)
|
||||
result ^= zobEp[ep_square()];
|
||||
|
||||
result ^= zobCastle[st->castleRights];
|
||||
if (side_to_move() == BLACK)
|
||||
result ^= zobSideToMove;
|
||||
|
||||
@@ -1586,18 +1601,14 @@ Key Position::compute_key() const {
|
||||
|
||||
Key Position::compute_pawn_key() const {
|
||||
|
||||
Key result = Key(0ULL);
|
||||
Bitboard b;
|
||||
Square s;
|
||||
Key result = 0;
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; c++)
|
||||
{
|
||||
b = pieces(PAWN, c);
|
||||
while (b)
|
||||
{
|
||||
s = pop_1st_bit(&b);
|
||||
result ^= zobrist[c][PAWN][s];
|
||||
}
|
||||
result ^= zobrist[c][PAWN][pop_1st_bit(&b)];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1611,11 +1622,13 @@ Key Position::compute_pawn_key() const {
|
||||
|
||||
Key Position::compute_material_key() const {
|
||||
|
||||
Key result = Key(0ULL);
|
||||
int count;
|
||||
Key result = 0;
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; c++)
|
||||
for (PieceType pt = PAWN; pt <= QUEEN; pt++)
|
||||
{
|
||||
int count = piece_count(c, pt);
|
||||
count = piece_count(c, pt);
|
||||
for (int i = 0; i < count; i++)
|
||||
result ^= zobrist[c][pt][i];
|
||||
}
|
||||
@@ -1629,20 +1642,15 @@ Key Position::compute_material_key() const {
|
||||
/// updated by do_move and undo_move when the program is running in debug mode.
|
||||
Score Position::compute_value() const {
|
||||
|
||||
Score result = SCORE_ZERO;
|
||||
Bitboard b;
|
||||
Square s;
|
||||
Score result = SCORE_ZERO;
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; c++)
|
||||
for (PieceType pt = PAWN; pt <= KING; pt++)
|
||||
{
|
||||
b = pieces(pt, c);
|
||||
while (b)
|
||||
{
|
||||
s = pop_1st_bit(&b);
|
||||
assert(piece_on(s) == piece_of_color_and_type(c, pt));
|
||||
result += pst(c, pt, s);
|
||||
}
|
||||
result += pst(c, pt, pop_1st_bit(&b));
|
||||
}
|
||||
|
||||
result += (side_to_move() == WHITE ? TempoValue / 2 : -TempoValue / 2);
|
||||
@@ -1651,7 +1659,7 @@ Score Position::compute_value() const {
|
||||
|
||||
|
||||
/// Position::compute_non_pawn_material() computes the total non-pawn middle
|
||||
/// game material score for the given side. Material scores are updated
|
||||
/// game material value for the given side. Material values are updated
|
||||
/// incrementally during the search, this function is only used while
|
||||
/// initializing a new Position object.
|
||||
|
||||
@@ -1660,16 +1668,8 @@ Value Position::compute_non_pawn_material(Color c) const {
|
||||
Value result = VALUE_ZERO;
|
||||
|
||||
for (PieceType pt = KNIGHT; pt <= QUEEN; pt++)
|
||||
{
|
||||
Bitboard b = pieces(pt, c);
|
||||
while (b)
|
||||
{
|
||||
assert(piece_on(first_1(b)) == piece_of_color_and_type(c, pt));
|
||||
result += piece_count(c, pt) * PieceValueMidgame[pt];
|
||||
|
||||
pop_1st_bit(&b);
|
||||
result += PieceValueMidgame[pt];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1703,8 +1703,8 @@ bool Position::is_draw() const {
|
||||
|
||||
bool Position::is_mate() const {
|
||||
|
||||
MoveStack moves[256];
|
||||
return is_check() && (generate_moves(*this, moves) == moves);
|
||||
MoveStack moves[MOVES_MAX];
|
||||
return is_check() && generate_moves(*this, moves) == moves;
|
||||
}
|
||||
|
||||
|
||||
@@ -1713,7 +1713,7 @@ bool Position::is_mate() const {
|
||||
|
||||
bool Position::has_mate_threat() {
|
||||
|
||||
MoveStack mlist[256], *last, *cur;
|
||||
MoveStack mlist[MOVES_MAX], *last, *cur;
|
||||
StateInfo st1, st2;
|
||||
bool mateFound = false;
|
||||
|
||||
@@ -1724,7 +1724,7 @@ bool Position::has_mate_threat() {
|
||||
// First pass the move to our opponent doing a null move
|
||||
do_null_move(st1);
|
||||
|
||||
// Then generate pseudo-legal moves that give check
|
||||
// Then generate pseudo-legal moves that could give check
|
||||
last = generate_non_capture_checks(*this, mlist);
|
||||
last = generate_captures(*this, last);
|
||||
|
||||
@@ -1757,18 +1757,19 @@ bool Position::has_mate_threat() {
|
||||
void Position::init_zobrist() {
|
||||
|
||||
int i,j, k;
|
||||
RKISS rk;
|
||||
|
||||
for (i = 0; i < 2; i++) for (j = 0; j < 8; j++) for (k = 0; k < 64; k++)
|
||||
zobrist[i][j][k] = Key(genrand_int64());
|
||||
zobrist[i][j][k] = rk.rand<Key>();
|
||||
|
||||
for (i = 0; i < 64; i++)
|
||||
zobEp[i] = Key(genrand_int64());
|
||||
zobEp[i] = rk.rand<Key>();
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
zobCastle[i] = Key(genrand_int64());
|
||||
zobCastle[i] = rk.rand<Key>();
|
||||
|
||||
zobSideToMove = Key(genrand_int64());
|
||||
zobExclusion = Key(genrand_int64());
|
||||
zobSideToMove = rk.rand<Key>();
|
||||
zobExclusion = rk.rand<Key>();
|
||||
}
|
||||
|
||||
|
||||
@@ -1804,16 +1805,16 @@ void Position::flipped_copy(const Position& pos) {
|
||||
// Board
|
||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
||||
if (!pos.square_is_empty(s))
|
||||
put_piece(Piece(int(pos.piece_on(s)) ^ 8), flip_square(s));
|
||||
put_piece(Piece(pos.piece_on(s) ^ 8), flip_square(s));
|
||||
|
||||
// Side to move
|
||||
sideToMove = opposite_color(pos.side_to_move());
|
||||
|
||||
// Castling rights
|
||||
if (pos.can_castle_kingside(WHITE)) allow_oo(BLACK);
|
||||
if (pos.can_castle_queenside(WHITE)) allow_ooo(BLACK);
|
||||
if (pos.can_castle_kingside(BLACK)) allow_oo(WHITE);
|
||||
if (pos.can_castle_queenside(BLACK)) allow_ooo(WHITE);
|
||||
if (pos.can_castle_kingside(WHITE)) do_allow_oo(BLACK);
|
||||
if (pos.can_castle_queenside(WHITE)) do_allow_ooo(BLACK);
|
||||
if (pos.can_castle_kingside(BLACK)) do_allow_oo(WHITE);
|
||||
if (pos.can_castle_queenside(BLACK)) do_allow_ooo(WHITE);
|
||||
|
||||
initialKFile = pos.initialKFile;
|
||||
initialKRFile = pos.initialKRFile;
|
||||
@@ -1855,18 +1856,20 @@ void Position::flipped_copy(const Position& pos) {
|
||||
bool Position::is_ok(int* failedStep) const {
|
||||
|
||||
// What features of the position should be verified?
|
||||
static const bool debugBitboards = false;
|
||||
static const bool debugKingCount = false;
|
||||
static const bool debugKingCapture = false;
|
||||
static const bool debugCheckerCount = false;
|
||||
static const bool debugKey = false;
|
||||
static const bool debugMaterialKey = false;
|
||||
static const bool debugPawnKey = false;
|
||||
static const bool debugIncrementalEval = false;
|
||||
static const bool debugNonPawnMaterial = false;
|
||||
static const bool debugPieceCounts = false;
|
||||
static const bool debugPieceList = false;
|
||||
static const bool debugCastleSquares = false;
|
||||
const bool debugAll = false;
|
||||
|
||||
const bool debugBitboards = debugAll || false;
|
||||
const bool debugKingCount = debugAll || false;
|
||||
const bool debugKingCapture = debugAll || false;
|
||||
const bool debugCheckerCount = debugAll || false;
|
||||
const bool debugKey = debugAll || false;
|
||||
const bool debugMaterialKey = debugAll || false;
|
||||
const bool debugPawnKey = debugAll || false;
|
||||
const bool debugIncrementalEval = debugAll || false;
|
||||
const bool debugNonPawnMaterial = debugAll || false;
|
||||
const bool debugPieceCounts = debugAll || false;
|
||||
const bool debugPieceList = debugAll || false;
|
||||
const bool debugCastleSquares = debugAll || false;
|
||||
|
||||
if (failedStep) *failedStep = 1;
|
||||
|
||||
|
||||
@@ -21,25 +21,12 @@
|
||||
#if !defined(POSITION_H_INCLUDED)
|
||||
#define POSITION_H_INCLUDED
|
||||
|
||||
// Disable some silly and noisy warning from MSVC compiler
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
// Forcing value to bool 'true' or 'false' (performance warning)
|
||||
#pragma warning(disable: 4800)
|
||||
|
||||
// Conditional expression is constant
|
||||
#pragma warning(disable: 4127)
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "color.h"
|
||||
#include "direction.h"
|
||||
#include "move.h"
|
||||
#include "piece.h"
|
||||
#include "square.h"
|
||||
@@ -146,7 +133,6 @@ public:
|
||||
};
|
||||
|
||||
// Constructors
|
||||
explicit Position(int threadID);
|
||||
Position(const Position& pos, int threadID);
|
||||
Position(const std::string& fen, int threadID);
|
||||
|
||||
@@ -210,6 +196,7 @@ public:
|
||||
// Information about attacks to or from a given square
|
||||
Bitboard attackers_to(Square s) const;
|
||||
Bitboard attacks_from(Piece p, Square s) const;
|
||||
static Bitboard attacks_from(Piece p, Square s, Bitboard occ);
|
||||
template<PieceType> Bitboard attacks_from(Square s) const;
|
||||
template<PieceType> Bitboard attacks_from(Square s, Color c) const;
|
||||
|
||||
@@ -278,8 +265,9 @@ public:
|
||||
|
||||
// Reset the gamePly variable to 0
|
||||
void reset_game_ply();
|
||||
|
||||
void inc_startpos_ply_counter();
|
||||
int64_t nodes_searched() const;
|
||||
void set_nodes_searched(int64_t n);
|
||||
|
||||
// Position consistency check, for debugging
|
||||
bool is_ok(int* failedStep = NULL) const;
|
||||
@@ -293,8 +281,8 @@ private:
|
||||
// Initialization helper functions (used while setting up a position)
|
||||
void clear();
|
||||
void put_piece(Piece p, Square s);
|
||||
void allow_oo(Color c);
|
||||
void allow_ooo(Color c);
|
||||
void do_allow_oo(Color c);
|
||||
void do_allow_ooo(Color c);
|
||||
bool set_castling_rights(char token);
|
||||
|
||||
// Helper functions for doing and undoing moves
|
||||
@@ -338,6 +326,7 @@ private:
|
||||
bool isChess960;
|
||||
int startPosPlyCounter;
|
||||
int threadID;
|
||||
int64_t nodes;
|
||||
StateInfo* st;
|
||||
|
||||
// Static variables
|
||||
@@ -348,6 +337,8 @@ private:
|
||||
static Score PieceSquareTable[16][64];
|
||||
static Key zobExclusion;
|
||||
static const Value seeValues[8];
|
||||
static const Value PieceValueMidgame[17];
|
||||
static const Value PieceValueEndgame[17];
|
||||
};
|
||||
|
||||
|
||||
@@ -355,6 +346,14 @@ private:
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
inline int64_t Position::nodes_searched() const {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
inline void Position::set_nodes_searched(int64_t n) {
|
||||
nodes = n;
|
||||
}
|
||||
|
||||
inline Piece Position::piece_on(Square s) const {
|
||||
return board[s];
|
||||
}
|
||||
@@ -392,7 +391,7 @@ inline Bitboard Position::occupied_squares() const {
|
||||
}
|
||||
|
||||
inline Bitboard Position::empty_squares() const {
|
||||
return ~(occupied_squares());
|
||||
return ~occupied_squares();
|
||||
}
|
||||
|
||||
inline Bitboard Position::pieces_of_color(Color c) const {
|
||||
@@ -536,12 +535,10 @@ inline bool Position::move_is_passed_pawn_push(Move m) const {
|
||||
}
|
||||
|
||||
inline int Position::rule_50_counter() const {
|
||||
|
||||
return st->rule50;
|
||||
}
|
||||
|
||||
inline int Position::startpos_ply_counter() const {
|
||||
|
||||
return startPosPlyCounter;
|
||||
}
|
||||
|
||||
@@ -553,12 +550,10 @@ inline bool Position::opposite_colored_bishops() const {
|
||||
}
|
||||
|
||||
inline bool Position::has_pawn_on_7th(Color c) const {
|
||||
|
||||
return pieces(PAWN, c) & relative_rank_bb(c, RANK_7);
|
||||
}
|
||||
|
||||
inline bool Position::is_chess960() const {
|
||||
|
||||
return isChess960;
|
||||
}
|
||||
|
||||
@@ -582,4 +577,12 @@ inline int Position::thread() const {
|
||||
return threadID;
|
||||
}
|
||||
|
||||
inline void Position::do_allow_oo(Color c) {
|
||||
st->castleRights |= (1 + int(c));
|
||||
}
|
||||
|
||||
inline void Position::do_allow_ooo(Color c) {
|
||||
st->castleRights |= (4 + 4*int(c));
|
||||
}
|
||||
|
||||
#endif // !defined(POSITION_H_INCLUDED)
|
||||
|
||||
28
src/psqtab.h
28
src/psqtab.h
@@ -27,18 +27,25 @@
|
||||
|
||||
#include "value.h"
|
||||
|
||||
namespace {
|
||||
|
||||
////
|
||||
//// Constants modified by Joona Kiiski
|
||||
////
|
||||
|
||||
static const Value MP = PawnValueMidgame;
|
||||
static const Value MK = KnightValueMidgame;
|
||||
static const Value MB = BishopValueMidgame;
|
||||
static const Value MR = RookValueMidgame;
|
||||
static const Value MQ = QueenValueMidgame;
|
||||
const Value MP = PawnValueMidgame;
|
||||
const Value MK = KnightValueMidgame;
|
||||
const Value MB = BishopValueMidgame;
|
||||
const Value MR = RookValueMidgame;
|
||||
const Value MQ = QueenValueMidgame;
|
||||
|
||||
static const int MgPST[][64] = {
|
||||
const Value EP = PawnValueEndgame;
|
||||
const Value EK = KnightValueEndgame;
|
||||
const Value EB = BishopValueEndgame;
|
||||
const Value ER = RookValueEndgame;
|
||||
const Value EQ = QueenValueEndgame;
|
||||
|
||||
const int MgPST[][64] = {
|
||||
{ },
|
||||
{// Pawn
|
||||
// A B C D E F G H
|
||||
@@ -108,13 +115,7 @@ static const int MgPST[][64] = {
|
||||
}
|
||||
};
|
||||
|
||||
static const Value EP = PawnValueEndgame;
|
||||
static const Value EK = KnightValueEndgame;
|
||||
static const Value EB = BishopValueEndgame;
|
||||
static const Value ER = RookValueEndgame;
|
||||
static const Value EQ = QueenValueEndgame;
|
||||
|
||||
static const int EgPST[][64] = {
|
||||
const int EgPST[][64] = {
|
||||
{ },
|
||||
{// Pawn
|
||||
// A B C D E F G H
|
||||
@@ -184,5 +185,6 @@ static const int EgPST[][64] = {
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // !defined(PSQTAB_H_INCLUDED)
|
||||
|
||||
84
src/rkiss.h
Normal file
84
src/rkiss.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
|
||||
Stockfish 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.
|
||||
|
||||
Stockfish 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/>.
|
||||
|
||||
This file is based on original code by Heinz van Saanen and is
|
||||
available under 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.
|
||||
|
||||
** A small "keep it simple and stupid" RNG with some fancy merits:
|
||||
**
|
||||
** Quite platform independent
|
||||
** Passes ALL dieharder tests! Here *nix sys-rand() e.g. fails miserably:-)
|
||||
** ~12 times faster than my *nix sys-rand()
|
||||
** ~4 times faster than SSE2-version of Mersenne twister
|
||||
** Average cycle length: ~2^126
|
||||
** 64 bit seed
|
||||
** Return doubles with a full 53 bit mantissa
|
||||
** Thread safe
|
||||
**
|
||||
** (c) Heinz van Saanen
|
||||
|
||||
*/
|
||||
|
||||
#if !defined(RKISS_H_INCLUDED)
|
||||
#define RKISS_H_INCLUDED
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "types.h"
|
||||
|
||||
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
class RKISS {
|
||||
|
||||
// Keep variables always together
|
||||
struct S { uint64_t a, b, c, d; } s;
|
||||
|
||||
// Return 64 bit unsigned integer in between [0,2^64-1]
|
||||
uint64_t rand64() {
|
||||
|
||||
const uint64_t
|
||||
e = s.a - ((s.b << 7) | (s.b >> 57));
|
||||
s.a = s.b ^ ((s.c << 13) | (s.c >> 51));
|
||||
s.b = s.c + ((s.d << 37) | (s.d >> 27));
|
||||
s.c = s.d + e;
|
||||
return s.d = e + s.a;
|
||||
}
|
||||
|
||||
// Init seed and scramble a few rounds
|
||||
void raninit() {
|
||||
|
||||
s.a = 0xf1ea5eed;
|
||||
s.b = s.c = s.d = 0xd4e12c77;
|
||||
for (int i = 0; i < 73; i++)
|
||||
rand64();
|
||||
}
|
||||
|
||||
public:
|
||||
RKISS() { raninit(); }
|
||||
template<typename T> T rand() { return T(rand64()); }
|
||||
};
|
||||
|
||||
#endif // !defined(RKISS_H_INCLUDED)
|
||||
161
src/san.cpp
161
src/san.cpp
@@ -28,8 +28,7 @@
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include "history.h"
|
||||
#include "movepick.h"
|
||||
#include "movegen.h"
|
||||
#include "san.h"
|
||||
|
||||
using std::string;
|
||||
@@ -41,14 +40,9 @@ using std::string;
|
||||
namespace {
|
||||
|
||||
enum Ambiguity {
|
||||
AMBIGUITY_NONE,
|
||||
AMBIGUITY_FILE,
|
||||
AMBIGUITY_RANK,
|
||||
AMBIGUITY_BOTH
|
||||
AMBIGUITY_NONE, AMBIGUITY_FILE, AMBIGUITY_RANK, AMBIGUITY_BOTH
|
||||
};
|
||||
|
||||
const History H; // Used as dummy argument for MovePicker c'tor
|
||||
|
||||
Ambiguity move_ambiguity(const Position& pos, Move m);
|
||||
const string time_string(int milliseconds);
|
||||
const string score_string(Value v);
|
||||
@@ -71,21 +65,23 @@ const string move_to_san(Position& pos, Move m) {
|
||||
string san;
|
||||
Square from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
PieceType pt = type_of_piece(pos.piece_on(move_from(m)));
|
||||
PieceType pt = type_of_piece(pos.piece_on(from));
|
||||
|
||||
if (m == MOVE_NONE)
|
||||
return "(none)";
|
||||
else if (m == MOVE_NULL)
|
||||
|
||||
if (m == MOVE_NULL)
|
||||
return "(null)";
|
||||
else if (move_is_long_castle(m) || (int(to - from) == -2 && pt == KING))
|
||||
|
||||
if (move_is_long_castle(m))
|
||||
san = "O-O-O";
|
||||
else if (move_is_short_castle(m) || (int(to - from) == 2 && pt == KING))
|
||||
else if (move_is_short_castle(m))
|
||||
san = "O-O";
|
||||
else
|
||||
{
|
||||
if (pt != PAWN)
|
||||
{
|
||||
san += piece_type_to_char(pt, true);
|
||||
san += piece_type_to_char(pt);
|
||||
|
||||
switch (move_ambiguity(pos, m)) {
|
||||
case AMBIGUITY_NONE:
|
||||
@@ -103,17 +99,20 @@ const string move_to_san(Position& pos, Move m) {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (pos.move_is_capture(m))
|
||||
{
|
||||
if (pt == PAWN)
|
||||
san += file_to_char(square_file(move_from(m)));
|
||||
san += "x";
|
||||
san += file_to_char(square_file(from));
|
||||
|
||||
san += 'x';
|
||||
}
|
||||
san += square_to_string(move_to(m));
|
||||
san += square_to_string(to);
|
||||
|
||||
if (move_is_promotion(m))
|
||||
{
|
||||
san += "=";
|
||||
san += piece_type_to_char(move_promotion_piece(m), true);
|
||||
san += '=';
|
||||
san += piece_type_to_char(move_promotion_piece(m));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,41 +137,43 @@ Move move_from_san(const Position& pos, const string& movestr) {
|
||||
|
||||
assert(pos.is_ok());
|
||||
|
||||
MovePicker mp = MovePicker(pos, MOVE_NONE, ONE_PLY, H);
|
||||
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
|
||||
enum { START, TO_FILE, TO_RANK, PROMOTION_OR_CHECK, PROMOTION, CHECK, END };
|
||||
static const string pieceLetters = "KQRBN";
|
||||
|
||||
MoveStack mlist[MOVES_MAX], *last;
|
||||
PieceType pt = PIECE_TYPE_NONE, promotion = PIECE_TYPE_NONE;
|
||||
File fromFile = FILE_NONE, toFile = FILE_NONE;
|
||||
Rank fromRank = RANK_NONE, toRank = RANK_NONE;
|
||||
Move move = MOVE_NONE;
|
||||
Square from, to;
|
||||
int matches, state = START;
|
||||
|
||||
// Generate all legal moves for the given position
|
||||
last = generate_moves(pos, mlist);
|
||||
|
||||
// Castling moves
|
||||
if (movestr == "O-O-O" || movestr == "O-O-O+")
|
||||
{
|
||||
Move m;
|
||||
while ((m = mp.get_next_move()) != MOVE_NONE)
|
||||
if (move_is_long_castle(m) && pos.pl_move_is_legal(m, pinned))
|
||||
return m;
|
||||
for (MoveStack* cur = mlist; cur != last; cur++)
|
||||
if (move_is_long_castle(cur->move))
|
||||
return cur->move;
|
||||
|
||||
return MOVE_NONE;
|
||||
}
|
||||
else if (movestr == "O-O" || movestr == "O-O+")
|
||||
{
|
||||
Move m;
|
||||
while ((m = mp.get_next_move()) != MOVE_NONE)
|
||||
if (move_is_short_castle(m) && pos.pl_move_is_legal(m, pinned))
|
||||
return m;
|
||||
for (MoveStack* cur = mlist; cur != last; cur++)
|
||||
if (move_is_short_castle(cur->move))
|
||||
return cur->move;
|
||||
|
||||
return MOVE_NONE;
|
||||
}
|
||||
|
||||
// Normal moves. We use a simple FSM to parse the san string.
|
||||
enum { START, TO_FILE, TO_RANK, PROMOTION_OR_CHECK, PROMOTION, CHECK, END };
|
||||
static const string pieceLetters = "KQRBN";
|
||||
PieceType pt = PIECE_TYPE_NONE, promotion = PIECE_TYPE_NONE;
|
||||
File fromFile = FILE_NONE, toFile = FILE_NONE;
|
||||
Rank fromRank = RANK_NONE, toRank = RANK_NONE;
|
||||
Square to;
|
||||
int state = START;
|
||||
|
||||
// Normal moves. We use a simple FSM to parse the san string
|
||||
for (size_t i = 0; i < movestr.length(); i++)
|
||||
{
|
||||
char type, c = movestr[i];
|
||||
|
||||
if (pieceLetters.find(c) != string::npos)
|
||||
type = 'P';
|
||||
else if (c >= 'a' && c <= 'h')
|
||||
@@ -192,7 +193,7 @@ Move move_from_san(const Position& pos, const string& movestr) {
|
||||
else if (state == PROMOTION)
|
||||
{
|
||||
promotion = piece_type_from_char(c);
|
||||
state = (i < movestr.length() - 1) ? CHECK : END;
|
||||
state = (i < movestr.length() - 1 ? CHECK : END);
|
||||
}
|
||||
else
|
||||
return MOVE_NONE;
|
||||
@@ -232,7 +233,8 @@ Move move_from_san(const Position& pos, const string& movestr) {
|
||||
else
|
||||
return MOVE_NONE;
|
||||
break;
|
||||
case 'x': case 'X':
|
||||
case 'x':
|
||||
case 'X':
|
||||
if (state == TO_RANK)
|
||||
{
|
||||
// Previous file was for disambiguation, or it's a pawn capture
|
||||
@@ -248,7 +250,8 @@ Move move_from_san(const Position& pos, const string& movestr) {
|
||||
else
|
||||
return MOVE_NONE;
|
||||
break;
|
||||
case '+': case '#':
|
||||
case '+':
|
||||
case '#':
|
||||
if (state == PROMOTION_OR_CHECK || state == CHECK)
|
||||
state = END;
|
||||
else
|
||||
@@ -263,22 +266,25 @@ Move move_from_san(const Position& pos, const string& movestr) {
|
||||
if (state != END)
|
||||
return MOVE_NONE;
|
||||
|
||||
// Look for a matching move
|
||||
Move m, move = MOVE_NONE;
|
||||
// Look for an unambiguous matching move
|
||||
to = make_square(toFile, toRank);
|
||||
int matches = 0;
|
||||
matches = 0;
|
||||
|
||||
while ((m = mp.get_next_move()) != MOVE_NONE)
|
||||
if ( pos.type_of_piece_on(move_from(m)) == pt
|
||||
&& move_to(m) == to
|
||||
&& move_promotion_piece(m) == promotion
|
||||
&& (fromFile == FILE_NONE || fromFile == square_file(move_from(m)))
|
||||
&& (fromRank == RANK_NONE || fromRank == square_rank(move_from(m))))
|
||||
for (MoveStack* cur = mlist; cur != last; cur++)
|
||||
{
|
||||
move = m;
|
||||
from = move_from(cur->move);
|
||||
|
||||
if ( pos.type_of_piece_on(from) == pt
|
||||
&& move_to(cur->move) == to
|
||||
&& move_promotion_piece(cur->move) == promotion
|
||||
&& (fromFile == FILE_NONE || fromFile == square_file(from))
|
||||
&& (fromRank == RANK_NONE || fromRank == square_rank(from)))
|
||||
{
|
||||
move = cur->move;
|
||||
matches++;
|
||||
}
|
||||
return (matches == 1 ? move : MOVE_NONE);
|
||||
}
|
||||
return matches == 1 ? move : MOVE_NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -322,11 +328,11 @@ const string line_to_san(const Position& pos, Move line[], int startColumn, bool
|
||||
/// It is used to write search information to the log file (which is created
|
||||
/// when the UCI parameter "Use Search Log" is "true").
|
||||
|
||||
const string pretty_pv(const Position& pos, int time, int depth, uint64_t nodes,
|
||||
const string pretty_pv(const Position& pos, int time, int depth,
|
||||
Value score, ValueType type, Move pv[]) {
|
||||
|
||||
const uint64_t K = 1000;
|
||||
const uint64_t M = 1000000;
|
||||
const int64_t K = 1000;
|
||||
const int64_t M = 1000000;
|
||||
|
||||
std::stringstream s;
|
||||
|
||||
@@ -341,12 +347,12 @@ const string pretty_pv(const Position& pos, int time, int depth, uint64_t nodes,
|
||||
s << std::setw(8) << time_string(time) << " ";
|
||||
|
||||
// Nodes
|
||||
if (nodes < M)
|
||||
s << std::setw(8) << nodes / 1 << " ";
|
||||
else if (nodes < K * M)
|
||||
s << std::setw(7) << nodes / K << "K ";
|
||||
if (pos.nodes_searched() < M)
|
||||
s << std::setw(8) << pos.nodes_searched() / 1 << " ";
|
||||
else if (pos.nodes_searched() < K * M)
|
||||
s << std::setw(7) << pos.nodes_searched() / K << "K ";
|
||||
else
|
||||
s << std::setw(7) << nodes / M << "M ";
|
||||
s << std::setw(7) << pos.nodes_searched() / M << "M ";
|
||||
|
||||
// PV
|
||||
s << line_to_san(pos, pv, 30, true);
|
||||
@@ -359,43 +365,36 @@ namespace {
|
||||
|
||||
Ambiguity move_ambiguity(const Position& pos, Move m) {
|
||||
|
||||
MoveStack mlist[MOVES_MAX], *last;
|
||||
Move candidates[8];
|
||||
Square from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
Piece pc = pos.piece_on(from);
|
||||
int matches = 0, f = 0, r = 0;
|
||||
|
||||
// King moves are never ambiguous, because there is never two kings of
|
||||
// the same color.
|
||||
if (type_of_piece(pc) == KING)
|
||||
// If there is only one piece 'pc' then move cannot be ambiguous
|
||||
if (pos.piece_count(pos.side_to_move(), type_of_piece(pc)) == 1)
|
||||
return AMBIGUITY_NONE;
|
||||
|
||||
MovePicker mp = MovePicker(pos, MOVE_NONE, ONE_PLY, H);
|
||||
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
|
||||
Move mv, moveList[8];
|
||||
// Collect all legal moves of piece 'pc' with destination 'to'
|
||||
last = generate_moves(pos, mlist);
|
||||
for (MoveStack* cur = mlist; cur != last; cur++)
|
||||
if (move_to(cur->move) == to && pos.piece_on(move_from(cur->move)) == pc)
|
||||
candidates[matches++] = cur->move;
|
||||
|
||||
int n = 0;
|
||||
while ((mv = mp.get_next_move()) != MOVE_NONE)
|
||||
if (move_to(mv) == to && pos.piece_on(move_from(mv)) == pc && pos.pl_move_is_legal(mv, pinned))
|
||||
moveList[n++] = mv;
|
||||
|
||||
if (n == 1)
|
||||
if (matches == 1)
|
||||
return AMBIGUITY_NONE;
|
||||
|
||||
int f = 0, r = 0;
|
||||
for (int i = 0; i < n; i++)
|
||||
for (int i = 0; i < matches; i++)
|
||||
{
|
||||
if (square_file(move_from(moveList[i])) == square_file(from))
|
||||
if (square_file(move_from(candidates[i])) == square_file(from))
|
||||
f++;
|
||||
|
||||
if (square_rank(move_from(moveList[i])) == square_rank(from))
|
||||
if (square_rank(move_from(candidates[i])) == square_rank(from))
|
||||
r++;
|
||||
}
|
||||
if (f == 1)
|
||||
return AMBIGUITY_FILE;
|
||||
|
||||
if (r == 1)
|
||||
return AMBIGUITY_RANK;
|
||||
|
||||
return AMBIGUITY_BOTH;
|
||||
return f == 1 ? AMBIGUITY_FILE : r == 1 ? AMBIGUITY_RANK : AMBIGUITY_BOTH;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -39,6 +39,6 @@
|
||||
extern const std::string move_to_san(Position& pos, Move m);
|
||||
extern Move move_from_san(const Position& pos, const std::string& str);
|
||||
extern const std::string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines);
|
||||
extern const std::string pretty_pv(const Position& pos, int time, int depth, uint64_t nodes, Value score, ValueType type, Move pv[]);
|
||||
extern const std::string pretty_pv(const Position& pos, int time, int depth, Value score, ValueType type, Move pv[]);
|
||||
|
||||
#endif // !defined(SAN_H_INCLUDED)
|
||||
|
||||
1662
src/search.cpp
1662
src/search.cpp
File diff suppressed because it is too large
Load Diff
@@ -35,7 +35,7 @@
|
||||
////
|
||||
|
||||
const int PLY_MAX = 100;
|
||||
const int PLY_MAX_PLUS_2 = 102;
|
||||
const int PLY_MAX_PLUS_2 = PLY_MAX + 2;
|
||||
|
||||
|
||||
////
|
||||
@@ -47,6 +47,7 @@ const int PLY_MAX_PLUS_2 = 102;
|
||||
/// search thread has its own array of SearchStack objects, indexed by the
|
||||
/// current ply.
|
||||
struct EvalInfo;
|
||||
struct SplitPoint;
|
||||
|
||||
struct SearchStack {
|
||||
Move currentMove;
|
||||
@@ -56,7 +57,9 @@ struct SearchStack {
|
||||
Move killers[2];
|
||||
Depth reduction;
|
||||
Value eval;
|
||||
Value evalMargin;
|
||||
bool skipNullMove;
|
||||
SplitPoint* sp;
|
||||
};
|
||||
|
||||
|
||||
@@ -68,8 +71,7 @@ extern void init_search();
|
||||
extern void init_threads();
|
||||
extern void exit_threads();
|
||||
extern int perft(Position& pos, Depth depth);
|
||||
extern int64_t nodes_searched();
|
||||
extern bool think(const Position& pos, bool infinite, bool ponder, int time[], int increment[],
|
||||
extern bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[],
|
||||
int movesToGo, int maxDepth, int maxNodes, int maxTime, Move searchMoves[]);
|
||||
|
||||
#endif // !defined(SEARCH_H_INCLUDED)
|
||||
|
||||
23
src/square.h
23
src/square.h
@@ -57,11 +57,15 @@ enum Rank {
|
||||
};
|
||||
|
||||
enum SquareDelta {
|
||||
DELTA_SSW = -021, DELTA_SS = -020, DELTA_SSE = -017, DELTA_SWW = -012,
|
||||
DELTA_SW = -011, DELTA_S = -010, DELTA_SE = -07, DELTA_SEE = -06,
|
||||
DELTA_W = -01, DELTA_ZERO = 0, DELTA_E = 01, DELTA_NWW = 06, DELTA_NW = 07,
|
||||
DELTA_N = 010, DELTA_NE = 011, DELTA_NEE = 012, DELTA_NNW = 017,
|
||||
DELTA_NN = 020, DELTA_NNE = 021
|
||||
|
||||
DELTA_N = 8, DELTA_E = 1, DELTA_S = -8, DELTA_W = -1, DELTA_NONE = 0,
|
||||
|
||||
DELTA_NN = DELTA_N + DELTA_N,
|
||||
DELTA_NE = DELTA_N + DELTA_E,
|
||||
DELTA_SE = DELTA_S + DELTA_E,
|
||||
DELTA_SS = DELTA_S + DELTA_S,
|
||||
DELTA_SW = DELTA_S + DELTA_W,
|
||||
DELTA_NW = DELTA_N + DELTA_W
|
||||
};
|
||||
|
||||
ENABLE_OPERATORS_ON(Square);
|
||||
@@ -74,9 +78,8 @@ ENABLE_OPERATORS_ON(SquareDelta);
|
||||
//// Constants
|
||||
////
|
||||
|
||||
const int FlipMask = 070;
|
||||
const int FlopMask = 07;
|
||||
|
||||
const int FlipMask = 56;
|
||||
const int FlopMask = 7;
|
||||
|
||||
////
|
||||
//// Inline functions
|
||||
@@ -160,10 +163,6 @@ inline char rank_to_char(Rank r) {
|
||||
return char(r - RANK_1 + int('1'));
|
||||
}
|
||||
|
||||
inline Square square_from_string(const std::string& str) {
|
||||
return make_square(file_from_char(str[0]), rank_from_char(str[1]));
|
||||
}
|
||||
|
||||
inline const std::string square_to_string(Square s) {
|
||||
return std::string(1, file_to_char(square_file(s)))
|
||||
+ std::string(1, rank_to_char(square_rank(s)));
|
||||
|
||||
11
src/thread.h
11
src/thread.h
@@ -38,7 +38,7 @@
|
||||
//// Constants and variables
|
||||
////
|
||||
|
||||
const int MAX_THREADS = 8;
|
||||
const int MAX_THREADS = 16;
|
||||
const int MAX_ACTIVE_SPLIT_POINTS = 8;
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ struct SplitPoint {
|
||||
bool pvNode, mateThreat;
|
||||
Value beta;
|
||||
int ply;
|
||||
int master;
|
||||
Move threatMove;
|
||||
SearchStack sstack[MAX_THREADS][PLY_MAX_PLUS_2];
|
||||
|
||||
@@ -64,10 +65,11 @@ struct SplitPoint {
|
||||
|
||||
// Shared data
|
||||
Lock lock;
|
||||
volatile int64_t nodes;
|
||||
volatile Value alpha;
|
||||
volatile Value bestValue;
|
||||
volatile int moveCount;
|
||||
volatile bool stopRequest;
|
||||
volatile bool betaCutoff;
|
||||
volatile int slaves[MAX_THREADS];
|
||||
};
|
||||
|
||||
@@ -75,16 +77,15 @@ struct SplitPoint {
|
||||
|
||||
enum ThreadState
|
||||
{
|
||||
THREAD_INITIALIZING, // thread is initializing itself
|
||||
THREAD_SEARCHING, // thread is performing work
|
||||
THREAD_AVAILABLE, // thread is polling for work
|
||||
THREAD_SLEEPING, // we are not thinking, so thread is sleeping
|
||||
THREAD_AVAILABLE, // thread is waiting for work
|
||||
THREAD_BOOKED, // other thread (master) has booked us as a slave
|
||||
THREAD_WORKISWAITING, // master has ordered us to start
|
||||
THREAD_TERMINATED // we are quitting and thread is terminated
|
||||
};
|
||||
|
||||
struct Thread {
|
||||
uint64_t nodes;
|
||||
volatile ThreadState state;
|
||||
SplitPoint* volatile splitPoint;
|
||||
volatile int activeSplitPoints;
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace {
|
||||
//// Functions
|
||||
////
|
||||
|
||||
void TimeManager::pv_unstability(int curChanges, int prevChanges) {
|
||||
void TimeManager::pv_instability(int curChanges, int prevChanges) {
|
||||
|
||||
unstablePVExtraTime = curChanges * (optimumSearchTime / 2)
|
||||
+ prevChanges * (optimumSearchTime / 3);
|
||||
@@ -114,10 +114,10 @@ void TimeManager::init(int myTime, int myInc, int movesToGo, int currentPly)
|
||||
int hypMTG, hypMyTime, t1, t2;
|
||||
|
||||
// Read uci parameters
|
||||
int emergencyMoveHorizon = get_option_value_int("Emergency Move Horizon");
|
||||
int emergencyBaseTime = get_option_value_int("Emergency Base Time");
|
||||
int emergencyMoveTime = get_option_value_int("Emergency Move Time");
|
||||
int minThinkingTime = get_option_value_int("Minimum Thinking Time");
|
||||
int emergencyMoveHorizon = Options["Emergency Move Horizon"].value<int>();
|
||||
int emergencyBaseTime = Options["Emergency Base Time"].value<int>();
|
||||
int emergencyMoveTime = Options["Emergency Move Time"].value<int>();
|
||||
int minThinkingTime = Options["Minimum Thinking Time"].value<int>();
|
||||
|
||||
// Initialize to maximum values but unstablePVExtraTime that is reset
|
||||
unstablePVExtraTime = 0;
|
||||
@@ -137,7 +137,7 @@ void TimeManager::init(int myTime, int myInc, int movesToGo, int currentPly)
|
||||
maximumSearchTime = Min(maximumSearchTime, t2);
|
||||
}
|
||||
|
||||
if (get_option_value_bool("Ponder"))
|
||||
if (Options["Ponder"].value<bool>())
|
||||
optimumSearchTime += optimumSearchTime / 4;
|
||||
|
||||
// Make sure that maxSearchTime is not over absoluteMaxSearchTime
|
||||
|
||||
@@ -29,7 +29,7 @@ class TimeManager {
|
||||
public:
|
||||
|
||||
void init(int myTime, int myInc, int movesToGo, int currentPly);
|
||||
void pv_unstability(int curChanges, int prevChanges);
|
||||
void pv_instability(int curChanges, int prevChanges);
|
||||
int available_time() const { return optimumSearchTime + unstablePVExtraTime; }
|
||||
int maximum_time() const { return maximumSearchTime; }
|
||||
|
||||
|
||||
@@ -70,8 +70,9 @@ void TranspositionTable::set_size(size_t mbSize) {
|
||||
{
|
||||
std::cerr << "Failed to allocate " << mbSize
|
||||
<< " MB for transposition table." << std::endl;
|
||||
Application::exit_with_failure();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
16
src/tt.h
16
src/tt.h
@@ -64,6 +64,7 @@ public:
|
||||
staticValue = int16_t(statV);
|
||||
staticValueMargin = int16_t(kd);
|
||||
}
|
||||
void set_generation(int g) { data = move() | (type() << 21) | (g << 23); }
|
||||
|
||||
uint32_t key() const { return key32; }
|
||||
Depth depth() const { return Depth(depth16); }
|
||||
@@ -102,6 +103,9 @@ struct TTCluster {
|
||||
|
||||
class TranspositionTable {
|
||||
|
||||
TranspositionTable(const TranspositionTable&);
|
||||
TranspositionTable& operator=(const TranspositionTable&);
|
||||
|
||||
public:
|
||||
TranspositionTable();
|
||||
~TranspositionTable();
|
||||
@@ -111,11 +115,12 @@ public:
|
||||
TTEntry* retrieve(const Key posKey) const;
|
||||
void new_search();
|
||||
TTEntry* first_entry(const Key posKey) const;
|
||||
void refresh(const TTEntry* tte) const;
|
||||
|
||||
private:
|
||||
size_t size;
|
||||
TTCluster* entries;
|
||||
uint8_t generation;
|
||||
uint8_t generation; // To properly compare, size must be smaller then TT stored value
|
||||
};
|
||||
|
||||
extern TranspositionTable TT;
|
||||
@@ -130,4 +135,13 @@ inline TTEntry* TranspositionTable::first_entry(const Key posKey) const {
|
||||
return entries[uint32_t(posKey) & (size - 1)].data;
|
||||
}
|
||||
|
||||
|
||||
/// TranspositionTable::refresh updates the 'generation' value of the TTEntry
|
||||
/// to avoid aging. Normally called after a TT hit, before to return.
|
||||
|
||||
inline void TranspositionTable::refresh(const TTEntry* tte) const {
|
||||
|
||||
const_cast<TTEntry*>(tte)->set_generation(generation);
|
||||
}
|
||||
|
||||
#endif // !defined(TT_H_INCLUDED)
|
||||
|
||||
10
src/types.h
10
src/types.h
@@ -17,7 +17,6 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(TYPES_H_INCLUDED)
|
||||
#define TYPES_H_INCLUDED
|
||||
|
||||
@@ -27,6 +26,10 @@
|
||||
|
||||
#else
|
||||
|
||||
// Disable some silly and noisy warning from MSVC compiler
|
||||
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
|
||||
#pragma warning(disable: 4127) // Conditional expression is constant
|
||||
|
||||
typedef __int8 int8_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef __int16 int16;
|
||||
@@ -47,6 +50,7 @@ typedef uint64_t Key;
|
||||
// Bitboard type
|
||||
typedef uint64_t Bitboard;
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
////
|
||||
//// Configuration
|
||||
@@ -150,10 +154,10 @@ template<typename T>
|
||||
inline T operator- (const T d) { OK(T); return T(-int(d)); }
|
||||
|
||||
template<typename T>
|
||||
inline void operator++ (T& d, int) { OK(T); d = T(int(d) + 1); }
|
||||
inline T operator++ (T& d, int) { OK(T); d = T(int(d) + 1); return d; }
|
||||
|
||||
template<typename T>
|
||||
inline void operator-- (T& d, int) { OK(T); d = T(int(d) - 1); }
|
||||
inline T operator-- (T& d, int) { OK(T); d = T(int(d) - 1); return d; }
|
||||
|
||||
template<typename T>
|
||||
inline void operator+= (T& d1, const T d2) { OK(T); d1 = d1 + d2; }
|
||||
|
||||
247
src/uci.cpp
247
src/uci.cpp
@@ -27,7 +27,6 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "book.h"
|
||||
#include "evaluate.h"
|
||||
#include "misc.h"
|
||||
#include "move.h"
|
||||
@@ -35,89 +34,47 @@
|
||||
#include "position.h"
|
||||
#include "san.h"
|
||||
#include "search.h"
|
||||
#include "uci.h"
|
||||
#include "ucioption.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
////
|
||||
//// Local definitions:
|
||||
////
|
||||
|
||||
namespace {
|
||||
|
||||
// FEN string for the initial position
|
||||
const string StartPositionFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
|
||||
// UCIInputParser is a class for parsing UCI input. The class
|
||||
// UCIParser is a class for parsing UCI input. The class
|
||||
// is actually a string stream built on a given input string.
|
||||
|
||||
typedef istringstream UCIInputParser;
|
||||
|
||||
// The root position. This is set up when the user (or in practice, the GUI)
|
||||
// sends the "position" UCI command. The root position is sent to the think()
|
||||
// function when the program receives the "go" command.
|
||||
Position RootPosition(0);
|
||||
typedef istringstream UCIParser;
|
||||
|
||||
// Local functions
|
||||
bool handle_command(const string& command);
|
||||
void set_option(UCIInputParser& uip);
|
||||
void set_position(UCIInputParser& uip);
|
||||
bool go(UCIInputParser& uip);
|
||||
void perft(UCIInputParser& uip);
|
||||
void set_option(UCIParser& uip);
|
||||
void set_position(Position& pos, UCIParser& uip);
|
||||
bool go(Position& pos, UCIParser& uip);
|
||||
void perft(Position& pos, UCIParser& uip);
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
/// execute_uci_command() takes a string as input, uses a UCIParser
|
||||
/// object to parse this text string as a UCI command, and calls
|
||||
/// the appropriate functions. In addition to the UCI commands,
|
||||
/// the function also supports a few debug commands.
|
||||
|
||||
/// uci_main_loop() is the only global function in this file. It is
|
||||
/// called immediately after the program has finished initializing.
|
||||
/// The program remains in this loop until it receives the "quit" UCI
|
||||
/// command. It waits for a command from the user, and passes this
|
||||
/// command to handle_command and also intercepts EOF from stdin,
|
||||
/// by translating EOF to the "quit" command. This ensures that Stockfish
|
||||
/// exits gracefully if the GUI dies unexpectedly.
|
||||
bool execute_uci_command(const string& cmd) {
|
||||
|
||||
void uci_main_loop() {
|
||||
|
||||
RootPosition.from_fen(StartPositionFEN);
|
||||
string command;
|
||||
|
||||
do {
|
||||
// Wait for a command from stdin
|
||||
if (!getline(cin, command))
|
||||
command = "quit";
|
||||
|
||||
} while (handle_command(command));
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
//// Local functions
|
||||
////
|
||||
|
||||
namespace {
|
||||
|
||||
// handle_command() takes a text string as input, uses a
|
||||
// UCIInputParser object to parse this text string as a UCI command,
|
||||
// and calls the appropriate functions. In addition to the UCI
|
||||
// commands, the function also supports a few debug commands.
|
||||
|
||||
bool handle_command(const string& command) {
|
||||
|
||||
UCIInputParser uip(command);
|
||||
static Position pos(StartPositionFEN, 0); // The root position
|
||||
UCIParser up(cmd);
|
||||
string token;
|
||||
|
||||
if (!(uip >> token)) // operator>>() skips any whitespace
|
||||
if (!(up >> token)) // operator>>() skips any whitespace
|
||||
return true;
|
||||
|
||||
if (token == "quit")
|
||||
return false;
|
||||
|
||||
if (token == "go")
|
||||
return go(uip);
|
||||
return go(pos, up);
|
||||
|
||||
if (token == "uci")
|
||||
{
|
||||
@@ -127,131 +84,151 @@ namespace {
|
||||
cout << "uciok" << endl;
|
||||
}
|
||||
else if (token == "ucinewgame")
|
||||
{
|
||||
push_button("New Game");
|
||||
RootPosition.from_fen(StartPositionFEN);
|
||||
}
|
||||
pos.from_fen(StartPositionFEN);
|
||||
|
||||
else if (token == "isready")
|
||||
cout << "readyok" << endl;
|
||||
else if (token == "position")
|
||||
set_position(uip);
|
||||
else if (token == "setoption")
|
||||
set_option(uip);
|
||||
|
||||
// The remaining commands are for debugging purposes only.
|
||||
// Perhaps they should be removed later in order to reduce the
|
||||
// size of the program binary.
|
||||
else if (token == "position")
|
||||
set_position(pos, up);
|
||||
|
||||
else if (token == "setoption")
|
||||
set_option(up);
|
||||
|
||||
// The remaining commands are for debugging purposes only
|
||||
else if (token == "d")
|
||||
RootPosition.print();
|
||||
pos.print();
|
||||
|
||||
else if (token == "flip")
|
||||
{
|
||||
Position p(RootPosition, RootPosition.thread());
|
||||
RootPosition.flipped_copy(p);
|
||||
Position p(pos, pos.thread());
|
||||
pos.flipped_copy(p);
|
||||
}
|
||||
else if (token == "eval")
|
||||
{
|
||||
Value evalMargin;
|
||||
cout << "Incremental mg: " << mg_value(RootPosition.value())
|
||||
<< "\nIncremental eg: " << eg_value(RootPosition.value())
|
||||
<< "\nFull eval: " << evaluate(RootPosition, evalMargin) << endl;
|
||||
cout << "Incremental mg: " << mg_value(pos.value())
|
||||
<< "\nIncremental eg: " << eg_value(pos.value())
|
||||
<< "\nFull eval: " << evaluate(pos, evalMargin) << endl;
|
||||
}
|
||||
else if (token == "key")
|
||||
cout << "key: " << hex << RootPosition.get_key()
|
||||
<< "\nmaterial key: " << RootPosition.get_material_key()
|
||||
<< "\npawn key: " << RootPosition.get_pawn_key() << endl;
|
||||
cout << "key: " << hex << pos.get_key()
|
||||
<< "\nmaterial key: " << pos.get_material_key()
|
||||
<< "\npawn key: " << pos.get_pawn_key() << endl;
|
||||
|
||||
else if (token == "perft")
|
||||
perft(uip);
|
||||
perft(pos, up);
|
||||
|
||||
else
|
||||
cout << "Unknown command: " << command << endl;
|
||||
cout << "Unknown command: " << cmd << endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
//// Local functions
|
||||
////
|
||||
|
||||
namespace {
|
||||
|
||||
// set_position() is called when Stockfish receives the "position" UCI
|
||||
// command. The input parameter is a UCIInputParser. It is assumed
|
||||
// command. The input parameter is a UCIParser. It is assumed
|
||||
// that this parser has consumed the first token of the UCI command
|
||||
// ("position"), and is ready to read the second token ("startpos"
|
||||
// or "fen", if the input is well-formed).
|
||||
|
||||
void set_position(UCIInputParser& uip) {
|
||||
void set_position(Position& pos, UCIParser& up) {
|
||||
|
||||
string token;
|
||||
|
||||
if (!(uip >> token)) // operator>>() skips any whitespace
|
||||
if (!(up >> token) || (token != "startpos" && token != "fen"))
|
||||
return;
|
||||
|
||||
if (token == "startpos")
|
||||
RootPosition.from_fen(StartPositionFEN);
|
||||
else if (token == "fen")
|
||||
{
|
||||
pos.from_fen(StartPositionFEN);
|
||||
if (!(up >> token))
|
||||
return;
|
||||
}
|
||||
else // fen
|
||||
{
|
||||
string fen;
|
||||
while (uip >> token && token != "moves")
|
||||
while (up >> token && token != "moves")
|
||||
{
|
||||
fen += token;
|
||||
fen += ' ';
|
||||
}
|
||||
RootPosition.from_fen(fen);
|
||||
pos.from_fen(fen);
|
||||
}
|
||||
|
||||
if (uip.good())
|
||||
{
|
||||
if (token != "moves")
|
||||
uip >> token;
|
||||
return;
|
||||
|
||||
if (token == "moves")
|
||||
{
|
||||
// Parse optional move list
|
||||
Move move;
|
||||
StateInfo st;
|
||||
while (uip >> token)
|
||||
while (up >> token)
|
||||
{
|
||||
move = move_from_string(RootPosition, token);
|
||||
RootPosition.do_move(move, st);
|
||||
if (RootPosition.rule_50_counter() == 0)
|
||||
RootPosition.reset_game_ply();
|
||||
move = move_from_uci(pos, token);
|
||||
pos.do_move(move, st);
|
||||
if (pos.rule_50_counter() == 0)
|
||||
pos.reset_game_ply();
|
||||
|
||||
RootPosition.inc_startpos_ply_counter(); //FIXME: make from_fen to support this and rule50
|
||||
pos.inc_startpos_ply_counter(); //FIXME: make from_fen to support this and rule50
|
||||
}
|
||||
// Our StateInfo st is about going out of scope so copy
|
||||
// its content inside RootPosition before it disappears.
|
||||
RootPosition.detach();
|
||||
}
|
||||
}
|
||||
// its content inside pos before it disappears.
|
||||
pos.detach();
|
||||
}
|
||||
|
||||
|
||||
// set_option() is called when Stockfish receives the "setoption" UCI
|
||||
// command. The input parameter is a UCIInputParser. It is assumed
|
||||
// command. The input parameter is a UCIParser. It is assumed
|
||||
// that this parser has consumed the first token of the UCI command
|
||||
// ("setoption"), and is ready to read the second token ("name", if
|
||||
// the input is well-formed).
|
||||
|
||||
void set_option(UCIInputParser& uip) {
|
||||
void set_option(UCIParser& up) {
|
||||
|
||||
string token, name, value;
|
||||
|
||||
if (!(uip >> token)) // operator>>() skips any whitespace
|
||||
if (!(up >> token) || token != "name") // operator>>() skips any whitespace
|
||||
return;
|
||||
|
||||
if (token == "name" && uip >> name)
|
||||
{
|
||||
while (uip >> token && token != "value")
|
||||
if (!(up >> name))
|
||||
return;
|
||||
|
||||
// Handle names with included spaces
|
||||
while (up >> token && token != "value")
|
||||
name += (" " + token);
|
||||
|
||||
if (token == "value" && uip >> value)
|
||||
if (Options.find(name) == Options.end())
|
||||
{
|
||||
while (uip >> token)
|
||||
cout << "No such option: " << name << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Is a button ?
|
||||
if (token != "value")
|
||||
{
|
||||
Options[name].set_value("true");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(up >> value))
|
||||
return;
|
||||
|
||||
// Handle values with included spaces
|
||||
while (up >> token)
|
||||
value += (" " + token);
|
||||
|
||||
set_option_value(name, value);
|
||||
} else
|
||||
push_button(name);
|
||||
}
|
||||
Options[name].set_value(value);
|
||||
}
|
||||
|
||||
|
||||
// go() is called when Stockfish receives the "go" UCI command. The
|
||||
// input parameter is a UCIInputParser. It is assumed that this
|
||||
// input parameter is a UCIParser. It is assumed that this
|
||||
// parser has consumed the first token of the UCI command ("go"),
|
||||
// and is ready to read the second token. The function sets the
|
||||
// thinking time and other parameters from the input string, and
|
||||
@@ -259,62 +236,60 @@ namespace {
|
||||
// parameters. Returns false if a quit command is received while
|
||||
// thinking, returns true otherwise.
|
||||
|
||||
bool go(UCIInputParser& uip) {
|
||||
bool go(Position& pos, UCIParser& up) {
|
||||
|
||||
string token;
|
||||
|
||||
int time[2] = {0, 0}, inc[2] = {0, 0};
|
||||
int movesToGo = 0, depth = 0, nodes = 0, moveTime = 0;
|
||||
bool infinite = false, ponder = false;
|
||||
Move searchMoves[500];
|
||||
Move searchMoves[MOVES_MAX];
|
||||
|
||||
searchMoves[0] = MOVE_NONE;
|
||||
|
||||
while (uip >> token)
|
||||
while (up >> token)
|
||||
{
|
||||
if (token == "infinite")
|
||||
infinite = true;
|
||||
else if (token == "ponder")
|
||||
ponder = true;
|
||||
else if (token == "wtime")
|
||||
uip >> time[0];
|
||||
up >> time[0];
|
||||
else if (token == "btime")
|
||||
uip >> time[1];
|
||||
up >> time[1];
|
||||
else if (token == "winc")
|
||||
uip >> inc[0];
|
||||
up >> inc[0];
|
||||
else if (token == "binc")
|
||||
uip >> inc[1];
|
||||
up >> inc[1];
|
||||
else if (token == "movestogo")
|
||||
uip >> movesToGo;
|
||||
up >> movesToGo;
|
||||
else if (token == "depth")
|
||||
uip >> depth;
|
||||
up >> depth;
|
||||
else if (token == "nodes")
|
||||
uip >> nodes;
|
||||
up >> nodes;
|
||||
else if (token == "movetime")
|
||||
uip >> moveTime;
|
||||
up >> moveTime;
|
||||
else if (token == "searchmoves")
|
||||
{
|
||||
int numOfMoves = 0;
|
||||
while (uip >> token)
|
||||
searchMoves[numOfMoves++] = move_from_string(RootPosition, token);
|
||||
while (up >> token)
|
||||
searchMoves[numOfMoves++] = move_from_uci(pos, token);
|
||||
|
||||
searchMoves[numOfMoves] = MOVE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
assert(RootPosition.is_ok());
|
||||
assert(pos.is_ok());
|
||||
|
||||
return think(RootPosition, infinite, ponder, time, inc, movesToGo,
|
||||
return think(pos, infinite, ponder, time, inc, movesToGo,
|
||||
depth, nodes, moveTime, searchMoves);
|
||||
}
|
||||
|
||||
void perft(UCIInputParser& uip) {
|
||||
void perft(Position& pos, UCIParser& up) {
|
||||
|
||||
string token;
|
||||
int depth, tm, n;
|
||||
Position pos(RootPosition, RootPosition.thread());
|
||||
|
||||
if (!(uip >> depth))
|
||||
if (!(up >> depth))
|
||||
return;
|
||||
|
||||
tm = get_system_time();
|
||||
@@ -324,6 +299,6 @@ namespace {
|
||||
tm = get_system_time() - tm;
|
||||
std::cout << "\nNodes " << n
|
||||
<< "\nTime (ms) " << tm
|
||||
<< "\nNodes/second " << (int)(n/(tm/1000.0)) << std::endl;
|
||||
<< "\nNodes/second " << int(n / (tm / 1000.0)) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
31
src/uci.h
31
src/uci.h
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
|
||||
Stockfish 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.
|
||||
|
||||
Stockfish 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(UCI_H_INCLUDED)
|
||||
#define UCI_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern void uci_main_loop();
|
||||
|
||||
|
||||
#endif // !defined(UCI_H_INCLUDED)
|
||||
@@ -17,17 +17,9 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "misc.h"
|
||||
#include "thread.h"
|
||||
@@ -37,150 +29,87 @@ using std::string;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
////
|
||||
//// Local definitions
|
||||
////
|
||||
OptionsMap Options; // Global object
|
||||
|
||||
namespace {
|
||||
|
||||
enum OptionType { SPIN, COMBO, CHECK, STRING, BUTTON };
|
||||
// Our case insensitive less() function as required by UCI protocol
|
||||
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
|
||||
|
||||
typedef std::vector<string> StrVector;
|
||||
int c1, c2;
|
||||
size_t i = 0;
|
||||
|
||||
struct Option {
|
||||
while (i < s1.size() && i < s2.size())
|
||||
{
|
||||
c1 = tolower(s1[i]);
|
||||
c2 = tolower(s2[i++]);
|
||||
|
||||
string name, defaultValue, currentValue;
|
||||
OptionType type;
|
||||
size_t idx;
|
||||
int minValue, maxValue;
|
||||
StrVector comboValues;
|
||||
if (c1 != c2)
|
||||
return c1 < c2;
|
||||
}
|
||||
return s1.size() < s2.size();
|
||||
}
|
||||
|
||||
Option();
|
||||
Option(const char* defaultValue, OptionType = STRING);
|
||||
Option(bool defaultValue, OptionType = CHECK);
|
||||
Option(int defaultValue, int minValue, int maxValue);
|
||||
|
||||
bool operator<(const Option& o) const { return idx < o.idx; }
|
||||
};
|
||||
|
||||
typedef std::vector<Option> OptionsVector;
|
||||
typedef std::map<string, Option> Options;
|
||||
|
||||
Options options;
|
||||
|
||||
// stringify() converts a value of type T to a std::string
|
||||
template<typename T>
|
||||
string stringify(const T& v) {
|
||||
// stringify() converts a numeric value of type T to a std::string
|
||||
template<typename T>
|
||||
static string stringify(const T& v) {
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << v;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
Option::Option() {} // To allow insertion in a std::map
|
||||
|
||||
Option::Option(const char* def, OptionType t)
|
||||
: defaultValue(def), currentValue(def), type(t), idx(options.size()), minValue(0), maxValue(0) {}
|
||||
|
||||
Option::Option(bool def, OptionType t)
|
||||
: defaultValue(stringify(def)), currentValue(stringify(def)), type(t), idx(options.size()), minValue(0), maxValue(0) {}
|
||||
|
||||
Option::Option(int def, int minv, int maxv)
|
||||
: defaultValue(stringify(def)), currentValue(stringify(def)), type(SPIN), idx(options.size()), minValue(minv), maxValue(maxv) {}
|
||||
|
||||
// load_defaults() populates the options map with the hard
|
||||
// coded names and default values.
|
||||
|
||||
void load_defaults(Options& o) {
|
||||
|
||||
o["Use Search Log"] = Option(false);
|
||||
o["Search Log Filename"] = Option("SearchLog.txt");
|
||||
o["Book File"] = Option("book.bin");
|
||||
o["Best Book Move"] = Option(false);
|
||||
o["Mobility (Middle Game)"] = Option(100, 0, 200);
|
||||
o["Mobility (Endgame)"] = Option(100, 0, 200);
|
||||
o["Pawn Structure (Middle Game)"] = Option(100, 0, 200);
|
||||
o["Pawn Structure (Endgame)"] = Option(100, 0, 200);
|
||||
o["Passed Pawns (Middle Game)"] = Option(100, 0, 200);
|
||||
o["Passed Pawns (Endgame)"] = Option(100, 0, 200);
|
||||
o["Space"] = Option(100, 0, 200);
|
||||
o["Aggressiveness"] = Option(100, 0, 200);
|
||||
o["Cowardice"] = Option(100, 0, 200);
|
||||
o["Check Extension (PV nodes)"] = Option(2, 0, 2);
|
||||
o["Check Extension (non-PV nodes)"] = Option(1, 0, 2);
|
||||
o["Single Evasion Extension (PV nodes)"] = Option(2, 0, 2);
|
||||
o["Single Evasion Extension (non-PV nodes)"] = Option(2, 0, 2);
|
||||
o["Mate Threat Extension (PV nodes)"] = Option(2, 0, 2);
|
||||
o["Mate Threat Extension (non-PV nodes)"] = Option(2, 0, 2);
|
||||
o["Pawn Push to 7th Extension (PV nodes)"] = Option(1, 0, 2);
|
||||
o["Pawn Push to 7th Extension (non-PV nodes)"] = Option(1, 0, 2);
|
||||
o["Passed Pawn Extension (PV nodes)"] = Option(1, 0, 2);
|
||||
o["Passed Pawn Extension (non-PV nodes)"] = Option(0, 0, 2);
|
||||
o["Pawn Endgame Extension (PV nodes)"] = Option(2, 0, 2);
|
||||
o["Pawn Endgame Extension (non-PV nodes)"] = Option(2, 0, 2);
|
||||
o["Minimum Split Depth"] = Option(4, 4, 7);
|
||||
o["Maximum Number of Threads per Split Point"] = Option(5, 4, 8);
|
||||
o["Threads"] = Option(1, 1, MAX_THREADS);
|
||||
o["Hash"] = Option(32, 4, 8192);
|
||||
o["Clear Hash"] = Option(false, BUTTON);
|
||||
o["New Game"] = Option(false, BUTTON);
|
||||
o["Ponder"] = Option(true);
|
||||
o["OwnBook"] = Option(true);
|
||||
o["MultiPV"] = Option(1, 1, 500);
|
||||
o["Emergency Move Horizon"] = Option(40, 0, 50);
|
||||
o["Emergency Base Time"] = Option(200, 0, 60000);
|
||||
o["Emergency Move Time"] = Option(70, 0, 5000);
|
||||
o["Minimum Thinking Time"] = Option(20, 0, 5000);
|
||||
o["UCI_Chess960"] = Option(false); // Just a dummy but needed by GUIs
|
||||
o["UCI_AnalyseMode"] = Option(false);
|
||||
|
||||
// Any option should know its name so to be easily printed
|
||||
for (Options::iterator it = o.begin(); it != o.end(); ++it)
|
||||
it->second.name = it->first;
|
||||
}
|
||||
|
||||
// get_option_value() implements the various get_option_value_<type>
|
||||
// functions defined later.
|
||||
|
||||
template<typename T>
|
||||
T get_option_value(const string& optionName) {
|
||||
|
||||
T ret = T();
|
||||
if (options.find(optionName) == options.end())
|
||||
return ret;
|
||||
|
||||
std::istringstream ss(options[optionName].currentValue);
|
||||
ss >> ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Specialization for std::string where instruction 'ss >> ret'
|
||||
// would erroneusly tokenize a string with spaces.
|
||||
template<>
|
||||
string get_option_value<string>(const string& optionName) {
|
||||
|
||||
if (options.find(optionName) == options.end())
|
||||
return string();
|
||||
|
||||
return options[optionName].currentValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// init_uci_options() initializes the UCI options. Currently, the only thing
|
||||
/// this function does is to initialize the default value of "Threads" and
|
||||
/// "Minimum Split Depth" parameters according to the number of CPU cores.
|
||||
/// init_uci_options() initializes the UCI options to their hard coded default
|
||||
/// values and initializes the default value of "Threads" and "Minimum Split Depth"
|
||||
/// parameters according to the number of CPU cores.
|
||||
|
||||
void init_uci_options() {
|
||||
|
||||
load_defaults(options);
|
||||
Options["Use Search Log"] = Option(false);
|
||||
Options["Search Log Filename"] = Option("SearchLog.txt");
|
||||
Options["Book File"] = Option("book.bin");
|
||||
Options["Best Book Move"] = Option(false);
|
||||
Options["Mobility (Middle Game)"] = Option(100, 0, 200);
|
||||
Options["Mobility (Endgame)"] = Option(100, 0, 200);
|
||||
Options["Pawn Structure (Middle Game)"] = Option(100, 0, 200);
|
||||
Options["Pawn Structure (Endgame)"] = Option(100, 0, 200);
|
||||
Options["Passed Pawns (Middle Game)"] = Option(100, 0, 200);
|
||||
Options["Passed Pawns (Endgame)"] = Option(100, 0, 200);
|
||||
Options["Space"] = Option(100, 0, 200);
|
||||
Options["Aggressiveness"] = Option(100, 0, 200);
|
||||
Options["Cowardice"] = Option(100, 0, 200);
|
||||
Options["Check Extension (PV nodes)"] = Option(2, 0, 2);
|
||||
Options["Check Extension (non-PV nodes)"] = Option(1, 0, 2);
|
||||
Options["Single Evasion Extension (PV nodes)"] = Option(2, 0, 2);
|
||||
Options["Single Evasion Extension (non-PV nodes)"] = Option(2, 0, 2);
|
||||
Options["Mate Threat Extension (PV nodes)"] = Option(2, 0, 2);
|
||||
Options["Mate Threat Extension (non-PV nodes)"] = Option(2, 0, 2);
|
||||
Options["Pawn Push to 7th Extension (PV nodes)"] = Option(1, 0, 2);
|
||||
Options["Pawn Push to 7th Extension (non-PV nodes)"] = Option(1, 0, 2);
|
||||
Options["Passed Pawn Extension (PV nodes)"] = Option(1, 0, 2);
|
||||
Options["Passed Pawn Extension (non-PV nodes)"] = Option(0, 0, 2);
|
||||
Options["Pawn Endgame Extension (PV nodes)"] = Option(2, 0, 2);
|
||||
Options["Pawn Endgame Extension (non-PV nodes)"] = Option(2, 0, 2);
|
||||
Options["Minimum Split Depth"] = Option(4, 4, 7);
|
||||
Options["Maximum Number of Threads per Split Point"] = Option(5, 4, 8);
|
||||
Options["Threads"] = Option(1, 1, MAX_THREADS);
|
||||
Options["Use Sleeping Threads"] = Option(false);
|
||||
Options["Hash"] = Option(32, 4, 8192);
|
||||
Options["Clear Hash"] = Option(false, "button");
|
||||
Options["Ponder"] = Option(true);
|
||||
Options["OwnBook"] = Option(true);
|
||||
Options["MultiPV"] = Option(1, 1, 500);
|
||||
Options["Emergency Move Horizon"] = Option(40, 0, 50);
|
||||
Options["Emergency Base Time"] = Option(200, 0, 60000);
|
||||
Options["Emergency Move Time"] = Option(70, 0, 5000);
|
||||
Options["Minimum Thinking Time"] = Option(20, 0, 5000);
|
||||
Options["UCI_Chess960"] = Option(false); // Just a dummy but needed by GUIs
|
||||
Options["UCI_AnalyseMode"] = Option(false);
|
||||
|
||||
assert(options.find("Threads") != options.end());
|
||||
assert(options.find("Minimum Split Depth") != options.end());
|
||||
|
||||
Option& thr = options["Threads"];
|
||||
Option& msd = options["Minimum Split Depth"];
|
||||
// Set some SMP parameters accordingly to the detected CPU count
|
||||
Option& thr = Options["Threads"];
|
||||
Option& msd = Options["Minimum Split Depth"];
|
||||
|
||||
thr.defaultValue = thr.currentValue = stringify(cpu_count());
|
||||
|
||||
@@ -190,130 +119,60 @@ void init_uci_options() {
|
||||
|
||||
|
||||
/// print_uci_options() prints all the UCI options to the standard output,
|
||||
/// in the format defined by the UCI protocol.
|
||||
/// in chronological insertion order (the idx field) and in the format
|
||||
/// defined by the UCI protocol.
|
||||
|
||||
void print_uci_options() {
|
||||
|
||||
const char OptTypeName[][16] = {
|
||||
"spin", "combo", "check", "string", "button"
|
||||
};
|
||||
|
||||
// Build up a vector out of the options map and sort it according to idx
|
||||
// field, that is the chronological insertion order in options map.
|
||||
OptionsVector vec;
|
||||
for (Options::const_iterator it = options.begin(); it != options.end(); ++it)
|
||||
vec.push_back(it->second);
|
||||
|
||||
std::sort(vec.begin(), vec.end());
|
||||
|
||||
for (OptionsVector::const_iterator it = vec.begin(); it != vec.end(); ++it)
|
||||
for (size_t i = 0; i <= Options.size(); i++)
|
||||
for (OptionsMap::const_iterator it = Options.begin(); it != Options.end(); ++it)
|
||||
if (it->second.idx == i)
|
||||
{
|
||||
cout << "\noption name " << it->name << " type " << OptTypeName[it->type];
|
||||
const Option& o = it->second;
|
||||
cout << "\noption name " << it->first << " type " << o.type;
|
||||
|
||||
if (it->type == BUTTON)
|
||||
continue;
|
||||
if (o.type != "button")
|
||||
cout << " default " << o.defaultValue;
|
||||
|
||||
if (it->type == CHECK)
|
||||
cout << " default " << (it->defaultValue == "1" ? "true" : "false");
|
||||
else
|
||||
cout << " default " << it->defaultValue;
|
||||
if (o.type == "spin")
|
||||
cout << " min " << o.minValue << " max " << o.maxValue;
|
||||
|
||||
if (it->type == SPIN)
|
||||
cout << " min " << it->minValue << " max " << it->maxValue;
|
||||
else if (it->type == COMBO)
|
||||
{
|
||||
StrVector::const_iterator itc;
|
||||
for (itc = it->comboValues.begin(); itc != it->comboValues.end(); ++itc)
|
||||
cout << " var " << *itc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
|
||||
/// get_option_value_bool() returns the current value of a UCI parameter of
|
||||
/// type "check".
|
||||
/// Option class c'tors
|
||||
|
||||
bool get_option_value_bool(const string& optionName) {
|
||||
Option::Option(const char* def) : type("string"), idx(Options.size()), minValue(0), maxValue(0)
|
||||
{ defaultValue = currentValue = def; }
|
||||
|
||||
return get_option_value<bool>(optionName);
|
||||
}
|
||||
Option::Option(bool def, string t) : type(t), idx(Options.size()), minValue(0), maxValue(0)
|
||||
{ defaultValue = currentValue = (def ? "true" : "false"); }
|
||||
|
||||
Option::Option(int def, int minv, int maxv) : type("spin"), idx(Options.size()), minValue(minv), maxValue(maxv)
|
||||
{ defaultValue = currentValue = stringify(def); }
|
||||
|
||||
|
||||
/// get_option_value_int() returns the value of a UCI parameter as an integer.
|
||||
/// Normally, this function will be used for a parameter of type "spin", but
|
||||
/// it could also be used with a "combo" parameter, where all the available
|
||||
/// values are integers.
|
||||
/// set_value() updates currentValue of the Option object. Normally it's up to
|
||||
/// the GUI to check for option's limits, but we could receive the new value
|
||||
/// directly from the user by teminal window. So let's check the bounds anyway.
|
||||
|
||||
int get_option_value_int(const string& optionName) {
|
||||
void Option::set_value(const string& value) {
|
||||
|
||||
return get_option_value<int>(optionName);
|
||||
}
|
||||
assert(!type.empty());
|
||||
|
||||
if ( (type == "check" || type == "button")
|
||||
&& !(value == "true" || value == "false"))
|
||||
return;
|
||||
|
||||
/// get_option_value_string() returns the current value of a UCI parameter as
|
||||
/// a string. It is used with parameters of type "combo" and "string".
|
||||
|
||||
string get_option_value_string(const string& optionName) {
|
||||
|
||||
return get_option_value<string>(optionName);
|
||||
}
|
||||
|
||||
|
||||
/// set_option_value() inserts a new value for a UCI parameter
|
||||
|
||||
void set_option_value(const string& name, const string& value) {
|
||||
|
||||
if (options.find(name) == options.end())
|
||||
if (type == "spin")
|
||||
{
|
||||
cout << "No such option: " << name << endl;
|
||||
int v = atoi(value.c_str());
|
||||
if (v < minValue || v > maxValue)
|
||||
return;
|
||||
}
|
||||
|
||||
// UCI protocol uses "true" and "false" instead of "1" and "0", so convert
|
||||
// value according to standard C++ convention before to store it.
|
||||
string v(value);
|
||||
if (v == "true")
|
||||
v = "1";
|
||||
else if (v == "false")
|
||||
v = "0";
|
||||
|
||||
// Normally it's up to the GUI to check for option's limits,
|
||||
// but we could receive the new value directly from the user
|
||||
// by teminal window. So let's check the bounds anyway.
|
||||
Option& opt = options[name];
|
||||
|
||||
if (opt.type == CHECK && v != "0" && v != "1")
|
||||
return;
|
||||
|
||||
else if (opt.type == SPIN)
|
||||
{
|
||||
int val = atoi(v.c_str());
|
||||
if (val < opt.minValue || val > opt.maxValue)
|
||||
return;
|
||||
}
|
||||
opt.currentValue = v;
|
||||
}
|
||||
|
||||
|
||||
/// push_button() is used to tell the engine that a UCI parameter of type
|
||||
/// "button" has been selected:
|
||||
|
||||
void push_button(const string& buttonName) {
|
||||
|
||||
set_option_value(buttonName, "true");
|
||||
}
|
||||
|
||||
|
||||
/// button_was_pressed() tests whether a UCI parameter of type "button" has
|
||||
/// been selected since the last time the function was called, in this case
|
||||
/// it also resets the button.
|
||||
|
||||
bool button_was_pressed(const string& buttonName) {
|
||||
|
||||
if (!get_option_value<bool>(buttonName))
|
||||
return false;
|
||||
|
||||
set_option_value(buttonName, "false");
|
||||
return true;
|
||||
currentValue = value;
|
||||
}
|
||||
|
||||
@@ -17,28 +17,64 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(UCIOPTION_H_INCLUDED)
|
||||
#define UCIOPTION_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
class Option {
|
||||
public:
|
||||
Option() {} // To allow insertion in a std::map
|
||||
Option(const char* defaultValue);
|
||||
Option(bool defaultValue, std::string type = "check");
|
||||
Option(int defaultValue, int minValue, int maxValue);
|
||||
|
||||
void set_value(const std::string& value);
|
||||
template<typename T> T value() const;
|
||||
|
||||
private:
|
||||
friend void init_uci_options();
|
||||
friend void print_uci_options();
|
||||
|
||||
std::string defaultValue, currentValue, type;
|
||||
size_t idx;
|
||||
int minValue, maxValue;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline T Option::value() const {
|
||||
|
||||
assert(type == "spin");
|
||||
return T(atoi(currentValue.c_str()));
|
||||
}
|
||||
|
||||
template<>
|
||||
inline std::string Option::value<std::string>() const {
|
||||
|
||||
assert(type == "string");
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Option::value<bool>() const {
|
||||
|
||||
assert(type == "check" || type == "button");
|
||||
return currentValue == "true";
|
||||
}
|
||||
|
||||
|
||||
// Custom comparator because UCI options should not be case sensitive
|
||||
struct CaseInsensitiveLess {
|
||||
bool operator() (const std::string&, const std::string&) const;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, Option, CaseInsensitiveLess> OptionsMap;
|
||||
|
||||
extern OptionsMap Options;
|
||||
extern void init_uci_options();
|
||||
extern void print_uci_options();
|
||||
extern bool get_option_value_bool(const std::string& optionName);
|
||||
extern int get_option_value_int(const std::string& optionName);
|
||||
extern std::string get_option_value_string(const std::string& optionName);
|
||||
extern bool button_was_pressed(const std::string& buttonName);
|
||||
extern void set_option_value(const std::string& optionName,const std::string& newValue);
|
||||
extern void push_button(const std::string& buttonName);
|
||||
|
||||
|
||||
#endif // !defined(UCIOPTION_H_INCLUDED)
|
||||
|
||||
Reference in New Issue
Block a user