Compare commits

..

23 Commits

Author SHA1 Message Date
Marco Costalba 48cfdfcc46 Fix threads count setting
Was broken after "Optimal tune for 8 cores" patch.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 20:57:33 +01:00
Marco Costalba fa7b244dc9 Optimal tune for 8 cores
After deep tests Louis Zulli found on his OCTAL machine that
best setup for an 8 core CPU is as following

"Threads" = 8
"Minimum Split Depth" = 6 or 7 (mSD)
"Maximum Number of Threads per Split Point" = not important (MNTpSP)

Here are testing results:

mSD7 (8 threads) vs mSD4 (8 threads): 291 - 120 - 589
mSD6 vs mSD7: 168 - 188 - 644
mSD6-MNTpSP5 vs mSD6-MNTpSP6: 172 - 172 - 656
SF-7threads vs SF-8threads: 179 - 204 - 617

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 20:57:17 +01:00
Marco Costalba 29ad6a73fc Fix duplicated scaling function
We erroneusly added two times the same scaling function
to endgame's map.

Fix detected by valgrind becasue resulted in a memleak
of the first added scaling function.

Bug introduced by 30e8f0c9ad6a473 of 13/02/2009

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 19:40:43 +01:00
Marco Costalba ac48b16708 Update release number
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 14:59:55 +01:00
Marco Costalba 38b1c4b6b8 Another TT size limit fix attempt
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 14:17:00 +01:00
Marco Costalba 162dbeaee8 Remove a bogus assert
It is not true with old 1.6.xx code

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 14:09:23 +01:00
Marco Costalba 85146ca0a9 Check bounds in set_option_value()
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.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 14:06:59 +01:00
Joona Kiiski 02e12a69a7 Remove InfiniteSearch hack
With current search control system, I can see absolutely no
reason to classify fixed time search as infinite search.

So remove old dated hack

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 13:15:28 +01:00
Tord Romstad 6e8116e38f Make sure we make a move at the end of the search when reaching
maximum depth during a "go movetime ..." search. This prevents
Stockfish from hanging forever after finding a mate in two or
three while running a test suite at a level of a few seconds
per move.

No functional change when playing games at normal time controls.
2010-02-01 13:13:58 +01:00
Marco Costalba 29f7fab2a9 Do not wait when AbortSearch is set
It means we have already received "stop" or "quit" commands.

This fixes an hang in tactical test in Fritz GUI. Bug
introduced by previous bug fix :-(

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 13:13:20 +01:00
Marco Costalba 2af986bf31 Fix sending of best move during an infinite search
According to UCI standard once engine receives 'go infinite'
command it should search until the "stop" command and do not exit
the search without being told so, even if PLY_MAX has been reached.

Patch is quite invasive because it cleanups some hacks used
by fixed depth and fixed nodes modes, mainly during benchmarks.

Bug found by Pascal Georges.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 13:10:21 +01:00
Marco Costalba b67146b100 Add hardware POPCNT support for gcc
With new target 'make gcc-popcnt' it is now
possible to compile with enabled hardware POPCNT
support also with gcc. Until now was possible only
for Intel and MSVC compilers.

When this instruction is supported by CPU, for instance
on Intel i7 or i5 family, produced binary is a bit faster.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:48:49 +01:00
Joona Kiiski c1b1a94d81 Standardize set_option function
Previously input like "setoption name Use Search Log value true "
(note space at the end of the line) didn't work.

Now parse value same way as option name. This way we implicitly
left- and right-trim value.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:48:11 +01:00
Joona Kiiski 17212e5fcc Remove last use of uip.eof()
Value of uip.eof() should not be trusted.
input like "go infinite searchmoves " (note space in the end of line)
causes problems.

Check the return value of (uip >> token) instead

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:46:03 +01:00
Marco Costalba 46921dff27 Fix a couple of MSVC casting warnings
Also removed some trailing whitespaces and aligned
indentation to current standard.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:45:04 +01:00
Marco Costalba 941016e7a2 Check for thread creation successful completion
It is a good programming practice to verify a system
call has indeed succeed.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:44:11 +01:00
Tord Romstad 290caf9960 Fixes a Chess960 bug when playing with more than one search thread.
The init_eval() function corrupted the static array castleRightsMask[]
in the Position class, resulting in instant crashes in most Chess960
games. Fixed by repairing the damage directly after the function is
called. Also modified the Position::to_fen() function to display
castle rights correctly for Chess960 positions, and added sanity checks
for uncastled rook files in Position::is_ok().
2010-02-01 12:40:09 +01:00
Marco Costalba 43fa3a4d64 Fix some races in SMP code
When a search fails high then sp->alpha is increased and
slave threads are requested to stop.

So we have to check for a stop request before to start a search
otherwise we could end up with sp->alpha >= sp->beta
leading to an assert in debug run in search_pv().

This patch fixes the assert and get rid of some of possible races.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:39:53 +01:00
Marco Costalba 64b4836d12 Fix enum Value issue with gcc 4.4
Louis Zulli reports a miscompile with g++-4.4 from MacPorts.

Namely enum Value is compiled as unsigned instead of signed integer
and this yields an issue in score_string() where float(v) is incorrectly
casted when Value v is negative.

This patch ensure that compiler choses a signed variable to store a Value.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:39:21 +01:00
Marco Costalba 5df7d62eb9 Fix 'position ..... moves ' parsing bug
If after 'moves' there is a space then we crash.

The problem is that operator>>() trims whitespaces so that
after 'moves' has been extract we are still not at eof()
but remaining string contains only spaces. So that the next
extarction operation uip >> token ends up with unchanged token
value that remains 'moves', this garbage value is then feeded
to RootPosition.do_move() through move_from_string() that does
not detect the invalid move value leading to a crash.

This bug is triggered by Shredder 12 interface under Mac that
puts a space after 'moves' without any actual move list.

Bug fixed by Justin Blanchard

After reviewing UCI parsing code I spotted other possible weak
points due to the fact that we don't test if the last extract
operation has been succesful. So I have extended Justing patch
to fix the remaining possible holes in uci.cpp

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:36:30 +01:00
Marco Costalba 82179c70dc Fix en-passant parsing from fen string
According to standard en-passant is recorded in fen string regardless
of whether there is a pawn in position to make an en passant capture.

Instead internally we set ep square only if the pawn can be captured.
So teach from_fen() to correctly handle this difference.

Bug reported and fixed by Justin Blanchard.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:36:06 +01:00
Marco Costalba de17652e47 Fix a possible crash in thread_is_available()
When we have more then 2 threads then we do an array
access with index 'Threads[slave].activeSplitPoints - 1'
This should be >= 0 because we tested the variable just
few statements before, but because is a shared variable
it could be that the 'slave' thread set the value to zero
just after we test it, so that when we use the decremented
variable for array access we crash.

Bug spotted by Bruno Causse.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:34:31 +01:00
Marco Costalba 647b79b556 Extend maximum hash size to 8 GB
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:14:37 +01:00
63 changed files with 7648 additions and 6308 deletions
+12 -10
View File
@@ -3,13 +3,13 @@
Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is
not a complete chess program, but requires some UCI compatible GUI not a complete chess program, but requires some UCI compatible GUI
(like XBoard with PolyGlot, eboard, Josè, Arena, Sigma Chess, Shredder, (like XBoard with PolyGlot, eboard, Jos, Arena, Sigma Chess, Shredder,
Chess Partner, or Fritz) in order to be used comfortably. Read the Chess Partner, or Fritz) in order to be used comfortably. Read the
documentation for your GUI of choice for information about how to use documentation for your GUI of choice for information about how to use
Stockfish with your GUI. Stockfish with your GUI.
This version of Stockfish supports up to 16 CPUs, but has not been This version of Stockfish supports up to 8 CPUs, but has not been
tested thoroughly with more than 4. The program tries to detect the tested thoroughly with more than 2. The program tries to detect the
number of CPUs on your computer and set the number of search threads number of CPUs on your computer and set the number of search threads
accordingly, but please be aware that the detection is not always accordingly, but please be aware that the detection is not always
correct. It is therefore recommended to inspect the value of the correct. It is therefore recommended to inspect the value of the
@@ -41,10 +41,10 @@ This distribution of Stockfish consists of the following files:
3. Opening books 3. Opening books
---------------- ----------------
This version of Stockfish has support for PolyGlot opening books. This version of Stockfish has experimental support for PolyGlot opening
For information about how to create such books, consult the PolyGlot books. For information about how to create such books, consult the
documentation. The book file can be selected by setting the UCI PolyGlot documentation. The book file can be selected by setting the
parameter "Book File". UCI parameter "Book File".
4. Compiling it yourself 4. Compiling it yourself
@@ -64,9 +64,6 @@ On 64 bit Unix-like systems the 'bsfq' assembly instruction will be used
for bit counting. Detection is automatic at compile time, but in case you for bit counting. Detection is automatic at compile time, but in case you
experience compile problems you can comment out #define USE_BSFQ line in types.h experience compile problems you can comment out #define USE_BSFQ line in types.h
In general is recommended to run 'make help' to see a list of make targets
with corresponding descriptions.
5. Terms of use 5. Terms of use
--------------- ---------------
@@ -86,3 +83,8 @@ source code, these changes must also be made available under the GPL.
For full details, read the copy of the GPL found in the file named For full details, read the copy of the GPL found in the file named
Copying.txt. Copying.txt.
6. Feedback
-----------
The author's e-mail address is mcostalba@gmail.com
+20 -9
View File
@@ -7,7 +7,7 @@ EngineCommand = ./stockfish
Book = false Book = false
BookFile = book.bin BookFile = book.bin
Log = false Log = true
LogFile = stockfish.log LogFile = stockfish.log
Resign = true Resign = true
@@ -19,18 +19,28 @@ Hash = 128
Threads = 1 Threads = 1
OwnBook = false OwnBook = false
Book File = book.bin Book File = book.bin
Best Book Move = false
Use Search Log = false Use Search Log = false
Search Log Filename = SearchLog.txt
Mobility (Middle Game) = 100 Mobility (Middle Game) = 100
Mobility (Endgame) = 100 Mobility (Endgame) = 100
Pawn Structure (Middle Game) = 100 Pawn Structure (Middle Game) = 100
Pawn Structure (Endgame) = 100 Pawn Structure (Endgame) = 100
Passed Pawns (Middle Game) = 100 Passed Pawns (Middle Game) = 100
Passed Pawns (Endgame) = 100 Passed Pawns (Endgame) = 100
Space = 100
Aggressiveness = 100 Aggressiveness = 100
Cowardice = 100 Cowardice = 100
King Safety Curve = Quadratic
Quadratic = Linear
King Safety Coefficient = 40
King Safety X Intercept = 0
King Safety Max Slope = 30
King Safety Max Value = 500
Queen Contact Check Bonus = 3
Queen Check Bonus = 2
Rook Check Bonus = 1
Bishop Check Bonus = 1
Knight Check Bonus = 1
Discovered Check Bonus = 3
Mate Threat Bonus = 3
Check Extension (PV nodes) = 2 Check Extension (PV nodes) = 2
Check Extension (non-PV nodes) = 1 Check Extension (non-PV nodes) = 1
Single Reply Extension (PV nodes) = 2 Single Reply Extension (PV nodes) = 2
@@ -43,10 +53,11 @@ Passed Pawn Extension (PV nodes) = 1
Passed Pawn Extension (non-PV nodes) = 0 Passed Pawn Extension (non-PV nodes) = 0
Pawn Endgame Extension (PV nodes) = 2 Pawn Endgame Extension (PV nodes) = 2
Pawn Endgame Extension (non-PV nodes) = 2 Pawn Endgame Extension (non-PV nodes) = 2
Full Depth Moves (PV nodes) = 14
Full Depth Moves (non-PV nodes) = 3
Threat Depth = 5
Futility Pruning (Main Search) = true
Futility Pruning (Quiescence Search) = true
Randomness = 0
Minimum Split Depth = 4 Minimum Split Depth = 4
Maximum Number of Threads per Split Point = 5 Maximum Number of Threads per Split Point = 5
Use Sleeping Threads = false
Emergency Move Horizon = 40
Emergency Base Time = 200
Emergency Move Time = 70
Minimum Thinking Time = 20
+257 -471
View File
@@ -1,7 +1,7 @@
# Stockfish, a UCI chess playing engine derived from Glaurung 2.1 # Stockfish, a UCI chess playing engine derived from Glaurung 2.1
# Copyright (C) 2004-2008 Tord Romstad (Glaurung author) # Copyright (C) 2004-2007 Tord Romstad
# Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad # Copyright (C) 2008 Marco Costalba
#
# This file is part of Stockfish. # This file is part of Stockfish.
# #
# Stockfish is free software: you can redistribute it and/or modify # Stockfish is free software: you can redistribute it and/or modify
@@ -18,511 +18,297 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
### ========================================================================== ### Executable name. Do not change
### Section 1. General Configuration
### ==========================================================================
### Executable name
EXE = stockfish EXE = stockfish
### Installation dir definitions
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
### Built-in benchmark for pgo-builds ### ==========================================================================
### Compiler speed switches for both GCC and ICC. These settings are generally
### fast on a broad range of systems, but may be changed experimentally
### ==========================================================================
GCCFLAGS = -O3 -msse
ICCFLAGS = -fast -msse
ICCFLAGS-OSX = -fast -mdynamic-no-pic
### ==========================================================================
### Enable/disable debugging, disabled by default
### ==========================================================================
GCCFLAGS += -DNDEBUG
ICCFLAGS += -DNDEBUG
ICCFLAGS-OSX += -DNDEBUG
### ==========================================================================
### Remove below comments to compile for a big-endian machine
### ==========================================================================
#GCCFLAGS += -DBIGENDIAN
#ICCFLAGS += -DBIGENDIAN
#ICCFLAGS-OSX += -DBIGENDIAN
### ==========================================================================
### Run built-in benchmark for pgo-builds with: 32MB hash 1 thread 10 depth
### These settings are generally fast, but may be changed experimentally
### ==========================================================================
PGOBENCH = ./$(EXE) bench 32 1 10 default depth PGOBENCH = ./$(EXE) bench 32 1 10 default depth
### Object files
OBJS = bitboard.o pawns.o material.o endgame.o evaluate.o main.o \ ### General compiler settings. Do not change
misc.o move.o movegen.o history.o movepick.o search.o position.o \ GCCFLAGS += -g -Wall -fno-exceptions -fno-rtti
tt.o uci.o ucioption.o book.o bitbase.o san.o benchmark.o timeman.o ICCFLAGS += -g -Wall -fno-exceptions -fno-rtti -wd383,869,981,10187,10188,11505,11503
ICCFLAGS-OSX += -g -Wall -fno-exceptions -fno-rtti -wd383,869,981,10187,10188,11505,11503
### ========================================================================== ### General linker settings. Do not change
### Section 2. High-level Configuration LDFLAGS = -lpthread
### ==========================================================================
#
# flag --- Comp switch --- Description
# ----------------------------------------------------------------------------
#
# debug = no/yes --- -DNDEBUG --- Enable/Disable debug mode
# optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations
# arch = (name) --- (-arch) --- Target architecture
# os = (name) --- --- Target operating system
# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system
# bigendian = no/yes --- -DBIGENDIAN --- big/little-endian byte order
# prefetch = no/yes --- -DUSE_PREFETCH --- Use prefetch x86 asm-instruction
# bsfq = no/yes --- -DUSE_BSFQ --- Use bsfq x86_64 asm-instruction
# --- (Works only with GCC and ICC 64-bit)
# popcnt = no/yes --- -DUSE_POPCNT --- Use popcnt x86_64 asm-instruction
#
# Note that Makefile is space sensitive, so when adding new architectures
# or modifying existing flags, you have to make sure there are no extra spaces
# at the end of the line for flag values.
### 2.1. General
debug = no
optimize = yes
### 2.2 Architecture specific
# General-section
ifeq ($(ARCH),general-64)
arch = any
os = any
bits = 64
bigendian = no
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),general-32)
arch = any
os = any
bits = 32
bigendian = no
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),bigendian-64)
arch = any
os = any
bits = 64
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),bigendian-32)
arch = any
os = any
bits = 32
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
endif
# x86-section
ifeq ($(ARCH),x86-64)
arch = x86_64
os = any
bits = 64
bigendian = no
prefetch = yes
bsfq = yes
popcnt = no
endif
ifeq ($(ARCH),x86-64-modern)
arch = x86_64
os = any
bits = 64
bigendian = no
prefetch = yes
bsfq = yes
popcnt = yes
endif
ifeq ($(ARCH),x86-32)
arch = i386
os = any
bits = 32
bigendian = no
prefetch = yes
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),x86-32-old)
arch = i386
os = any
bits = 32
bigendian = no
prefetch = no
bsfq = no
popcnt = no
endif
# osx-section
ifeq ($(ARCH),osx-ppc-64)
arch = ppc64
os = osx
bits = 64
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),osx-ppc-32)
arch = ppc
os = osx
bits = 32
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),osx-x86-64)
arch = x86_64
os = osx
bits = 64
bigendian = no
prefetch = yes
bsfq = yes
popcnt = no
endif
ifeq ($(ARCH),osx-x86-32)
arch = i386
os = osx
bits = 32
bigendian = no
prefetch = yes
bsfq = no
popcnt = no
endif
### ========================================================================== ### Object files. Do not change
### Section 3. Low-level configuration 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 value.o uci.o ucioption.o \
mersenne.o book.o bitbase.o san.o benchmark.o
### 3.1 Selecting compiler (default = gcc)
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++
profile_prepare = gcc-profile-prepare
profile_make = gcc-profile-make
profile_use = gcc-profile-use
profile_clean = gcc-profile-clean
endif
ifeq ($(COMP),icc)
comp=icc
CXX=icpc
profile_prepare = icc-profile-prepare
profile_make = icc-profile-make
profile_use = icc-profile-use
profile_clean = icc-profile-clean
endif
### 3.2 General compiler settings
CXXFLAGS = -g -Wall -Wcast-qual -fno-exceptions -fno-rtti $(EXTRACXXFLAGS)
ifeq ($(comp),gcc)
CXXFLAGS += -ansi -pedantic -Wno-long-long -Wextra
endif
ifeq ($(comp),mingw)
CXXFLAGS += -Wno-long-long -Wextra
endif
ifeq ($(comp),icc)
CXXFLAGS += -wd383,981,1418,1419,10187,10188,11505,11503 -Wcheck -Wabi -Wdeprecated -strict-ansi
endif
ifeq ($(os),osx)
CXXFLAGS += -arch $(arch)
endif
### 3.3 General linker settings
LDFLAGS = -lpthread $(EXTRALDFLAGS)
ifeq ($(os),osx)
LDFLAGS += -arch $(arch)
endif
### 3.4 Debugging
ifeq ($(debug),no)
CXXFLAGS += -DNDEBUG
endif
### 3.5 Optimization
ifeq ($(optimize),yes)
ifeq ($(comp),gcc)
CXXFLAGS += -O3
ifeq ($(os),osx)
ifeq ($(arch),i386)
CXXFLAGS += -mdynamic-no-pic
endif
ifeq ($(arch),x86_64)
CXXFLAGS += -mdynamic-no-pic
endif
endif
endif
ifeq ($(comp),mingw)
CXXFLAGS += -O3
endif
ifeq ($(comp),icc)
CXXFLAGS += -fast
ifeq ($(os),osx)
CXXFLAGS += -mdynamic-no-pic
endif
endif
endif
### 3.6. Bits
ifeq ($(bits),64)
CXXFLAGS += -DIS_64BIT
endif
### 3.7 Endianess
ifeq ($(bigendian),yes)
CXXFLAGS += -DBIGENDIAN
endif
### 3.8 prefetch
ifeq ($(prefetch),yes)
CXXFLAGS += -msse
DEPENDFLAGS += -msse
else
CXXFLAGS += -DNO_PREFETCH
endif
### 3.9 bsfq
ifeq ($(bsfq),yes)
CXXFLAGS += -DUSE_BSFQ
endif
### 3.10 popcnt
ifeq ($(popcnt),yes)
CXXFLAGS += -DUSE_POPCNT
endif
### ==========================================================================
### Section 4. Public targets
### ==========================================================================
### General rules. Do not change
default: default:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) build $(MAKE) gcc
help: help:
@echo "" @echo ""
@echo "To compile stockfish, type: " @echo "Makefile options:"
@echo "" @echo ""
@echo "make target ARCH=arch [COMP=comp]" @echo "make > Default: Compiler = g++"
@echo "" @echo "make gcc-popcnt > Compiler = g++ + popcnt-support"
@echo "Supported targets:" @echo "make icc > Compiler = icpc"
@echo "" @echo "make icc-profile > Compiler = icpc + automatic pgo-build"
@echo "build > Build unoptimized version" @echo "make icc-profile-popcnt > Compiler = icpc + automatic pgo-build + popcnt-support"
@echo "profile-build > Build PGO-optimized version" @echo "make osx-ppc32 > PPC-Mac OS X 32 bit. Compiler = g++"
@echo "popcnt-profile-build > Build PGO-optimized version with optional popcnt-support" @echo "make osx-ppc64 > PPC-Mac OS X 64 bit. Compiler = g++"
@echo "strip > Strip executable" @echo "make osx-x86 > x86-Mac OS X 32 bit. Compiler = g++"
@echo "install > Install executable" @echo "make osx-x86_64 > x86-Mac OS X 64 bit. Compiler = g++"
@echo "clean > Clean up" @echo "make osx-icc32 > x86-Mac OS X 32 bit. Compiler = icpc"
@echo "testrun > Make sample run" @echo "make osx-icc64 > x86-Mac OS X 64 bit. Compiler = icpc"
@echo "" @echo "make osx-icc32-profile > OSX 32 bit. Compiler = icpc + automatic pgo-build"
@echo "Supported archs:" @echo "make osx-icc64-profile > OSX 64 bit. Compiler = icpc + automatic pgo-build"
@echo "" @echo "make strip > Strip executable"
@echo "x86-64 > x86 64-bit" @echo "make clean > Clean up"
@echo "x86-64-modern > x86 64-bit with runtime support for popcnt-instruction"
@echo "x86-32 > x86 32-bit excluding very old hardware without SSE-support"
@echo "x86-32-old > x86 32-bit including also very old hardware"
@echo "osx-ppc-64 > PPC-Mac OS X 64 bit"
@echo "osx-ppc-32 > PPC-Mac OS X 32 bit"
@echo "osx-x86-64 > x86-Mac OS X 64 bit"
@echo "osx-x86-32 > x86-Mac OS X 32 bit"
@echo "general-64 > unspecified 64-bit"
@echo "general-32 > unspecified 32-bit"
@echo "bigendian-64 > unspecified 64-bit with bigendian byte order"
@echo "bigendian-32 > unspecified 32-bit with bigendian byte order"
@echo ""
@echo "Supported comps:"
@echo ""
@echo "gcc > Gnu compiler (default)"
@echo "icc > Intel compiler"
@echo "mingw > Gnu compiler with MinGW under Windows"
@echo ""
@echo "Non-standard targets:"
@echo ""
@echo "make hpux > Compile for HP-UX. Compiler = aCC"
@echo ""
@echo "Examples. If you don't know what to do, you likely want to run: "
@echo ""
@echo "make profile-build ARCH=x86-64 (This is for 64-bit systems)"
@echo "make profile-build ARCH=x86-32 (This is for 32-bit systems)"
@echo "" @echo ""
build: all: $(EXE) .depend
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) all
profile-build: clean:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity $(RM) *.o .depend *~ $(EXE)
@echo ""
@echo "Step 0/4. Preparing for profile build."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_prepare)
@echo ""
@echo "Step 1/4. Building executable for benchmark ..."
@touch *.cpp *.h
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make)
@echo ""
@echo "Step 2/4. Running benchmark for pgo-build ..."
@$(PGOBENCH) > /dev/null
@echo ""
@echo "Step 3/4. Building final executable ..."
@touch *.cpp
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use)
@echo ""
@echo "Step 4/4. Deleting profile data ..."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean)
popcnt-profile-build:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity ### Possible targets. You may add your own ones here
@echo "" gcc:
@echo "Step 0/6. Preparing for profile build." $(MAKE) \
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_prepare) CXX='g++' \
@echo "" CXXFLAGS="$(GCCFLAGS)" \
@echo "Step 1/6. Building executable for benchmark (popcnt disabled)..." all
gcc-popcnt:
$(MAKE) \
CXX='g++' \
CXXFLAGS="$(GCCFLAGS) -DUSE_POPCNT" \
all
icc:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS)" \
all
icc-profile-make:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS)" \
CXXFLAGS+='-prof-gen=srcpos -prof_dir ./profdir' \
all
icc-profile-use:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS)" \
CXXFLAGS+='-prof_use -prof_dir ./profdir' \
all
icc-profile:
@rm -rf profdir
@mkdir profdir
@touch *.cpp *.h @touch *.cpp *.h
$(MAKE) ARCH=x86-64 COMP=$(COMP) $(profile_make) $(MAKE) icc-profile-make
@echo "" @echo ""
@echo "Step 2/6. Running benchmark for pgo-build (popcnt disabled)..." @echo "Running benchmark for pgo-build ..."
@$(PGOBENCH) > /dev/null @$(PGOBENCH) > /dev/null
@echo "Benchmark finished. Build final executable now ..."
@echo "" @echo ""
@echo "Step 3/6. Building executable for benchmark (popcnt enabled)..."
@touch *.cpp *.h @touch *.cpp *.h
$(MAKE) ARCH=x86-64-modern COMP=$(COMP) $(profile_make) $(MAKE) icc-profile-use
@rm -rf profdir bench.txt
icc-profile-make-with-popcnt:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS) -DUSE_POPCNT" \
CXXFLAGS+='-prof-gen=srcpos -prof_dir ./profdir' \
all
icc-profile-use-with-popcnt:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS) -DUSE_POPCNT" \
CXXFLAGS+='-prof_use -prof_dir ./profdir' \
all
icc-profile-popcnt:
@rm -rf profdir
@mkdir profdir
@touch *.cpp *.h
$(MAKE) icc-profile-make
@echo "" @echo ""
@echo "Step 4/6. Running benchmark for pgo-build (popcnt enabled)..." @echo "Running benchmark for pgo-build (popcnt disabled)..."
@$(PGOBENCH) > /dev/null @$(PGOBENCH) > /dev/null
@echo ""
@echo "Step 5/6. Building final executable ..."
@touch *.cpp *.h @touch *.cpp *.h
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use) $(MAKE) icc-profile-make-with-popcnt
@echo "" @echo ""
@echo "Step 6/6. Deleting profile data ..." @echo "Running benchmark for pgo-build (popcnt enabled)..."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean) @$(PGOBENCH) > /dev/null
@echo "Benchmarks finished. Build final executable now ..."
@echo "" @echo ""
@touch *.cpp *.h
$(MAKE) icc-profile-use-with-popcnt
@rm -rf profdir bench.txt
osx-ppc32:
$(MAKE) \
CXX='g++' \
CXXFLAGS="$(GCCFLAGS)" \
CXXFLAGS+='-arch ppc' \
LDFLAGS+='-arch ppc' \
all
osx-ppc64:
$(MAKE) \
CXX='g++' \
CXXFLAGS="$(GCCFLAGS)" \
CXXFLAGS+='-arch ppc64' \
LDFLAGS+='-arch ppc64' \
all
osx-x86:
$(MAKE) \
CXX='g++' \
CXXFLAGS="$(GCCFLAGS)" \
CXXFLAGS+='-arch i386' \
LDFLAGS+='-arch i386' \
all
osx-x86_64:
$(MAKE) \
CXX='g++' \
CXXFLAGS="$(GCCFLAGS)" \
CXXFLAGS+='-arch x86_64' \
LDFLAGS+='-arch x86_64' \
all
osx-icc32:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch i386' \
LDFLAGS+='-arch i386' \
all
osx-icc64:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch x86_64' \
LDFLAGS+='-arch x86_64' \
all
osx-icc32-profile-make:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch i386' \
CXXFLAGS+='-prof_gen -prof_dir ./profdir' \
LDFLAGS+='-arch i386' \
all
osx-icc32-profile-use:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch i386' \
CXXFLAGS+='-prof_use -prof_dir ./profdir' \
LDFLAGS+='-arch i386' \
all
osx-icc32-profile:
@rm -rf profdir
@mkdir profdir
@touch *.cpp *.h
$(MAKE) osx-icc32-profile-make
@echo ""
@echo "Running benchmark for pgo-build ..."
@$(PGOBENCH) > /dev/null
@echo "Benchmark finished. Build final executable now ..."
@echo ""
@touch *.cpp *.h
$(MAKE) osx-icc32-profile-use
@rm -rf profdir bench.txt
osx-icc64-profile-make:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch x86_64' \
CXXFLAGS+='-prof_gen -prof_dir ./profdir' \
LDFLAGS+='-arch x86_64' \
all
osx-icc64-profile-use:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch x86_64' \
CXXFLAGS+='-prof_use -prof_dir ./profdir' \
LDFLAGS+='-arch x86_64' \
all
osx-icc64-profile:
@rm -rf profdir
@mkdir profdir
@touch *.cpp *.h
$(MAKE) osx-icc64-profile-make
@echo ""
@echo "Running benchmark for pgo-build ..."
@$(PGOBENCH) > /dev/null
@echo "Benchmark finished. Build final executable now ..."
@echo ""
@touch *.cpp *.h
$(MAKE) osx-icc64-profile-use
@rm -rf profdir bench.txt
strip: strip:
strip $(EXE) strip $(EXE)
install:
-mkdir -p -m 755 $(BINDIR)
-cp $(EXE) $(BINDIR)
-strip $(BINDIR)/$(EXE)
clean:
$(RM) $(EXE) $(EXE).exe *.o .depend *~ core bench.txt *.gcda
testrun:
@$(PGOBENCH)
### ==========================================================================
### Section 5. Private targets
### ==========================================================================
all: $(EXE) .depend
config-sanity:
@echo ""
@echo "Config:"
@echo "debug: '$(debug)'"
@echo "optimize: '$(optimize)'"
@echo "arch: '$(arch)'"
@echo "os: '$(os)'"
@echo "bits: '$(bits)'"
@echo "bigendian: '$(bigendian)'"
@echo "prefetch: '$(prefetch)'"
@echo "bsfq: '$(bsfq)'"
@echo "popcnt: '$(popcnt)'"
@echo ""
@echo "Flags:"
@echo "CXX: $(CXX)"
@echo "CXXFLAGS: $(CXXFLAGS)"
@echo "LDFLAGS: $(LDFLAGS)"
@echo ""
@echo "Testing config sanity. If this fails, try 'make help' ..."
@echo ""
@test "$(debug)" = "yes" || test "$(debug)" = "no"
@test "$(optimize)" = "yes" || test "$(optimize)" = "no"
@test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
test "$(arch)" = "ppc64" || test "$(arch)" = "ppc"
@test "$(os)" = "any" || test "$(os)" = "osx"
@test "$(bits)" = "32" || test "$(bits)" = "64"
@test "$(bigendian)" = "yes" || test "$(bigendian)" = "no"
@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)" = "mingw"
### Compilation. Do not change
$(EXE): $(OBJS) $(EXE): $(OBJS)
$(CXX) -o $@ $(OBJS) $(LDFLAGS) $(CXX) $(LDFLAGS) -o $@ $(OBJS)
gcc-profile-prepare:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) gcc-profile-clean
gcc-profile-make:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-fprofile-generate' \
EXTRALDFLAGS='-lgcov' \
all
gcc-profile-use:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-fprofile-use' \
all
gcc-profile-clean:
@rm -rf *.gcda *.gcno bench.txt
icc-profile-prepare:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) icc-profile-clean
@mkdir profdir
icc-profile-make:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-prof-gen=srcpos -prof_dir ./profdir' \
all
icc-profile-use:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-prof_use -prof_dir ./profdir' \
all
icc-profile-clean:
@rm -rf profdir bench.txt
### Dependencies. Do not change
.depend: .depend:
-@$(CXX) $(DEPENDFLAGS) -MM $(OBJS:.o=.cpp) > $@ 2> /dev/null $(CXX) -msse -MM $(OBJS:.o=.cpp) > $@
-include .depend
### ==========================================================================
### Section 6. Non-standard targets
### ==========================================================================
hpux:
$(MAKE) \
CXX='/opt/aCC/bin/aCC -AA +hpxstd98 -DBIGENDIAN -mt +O3 -DNDEBUG -DNO_PREFETCH' \
CXXFLAGS="" \
LDFLAGS="" \
all
include .depend
+75
View File
@@ -0,0 +1,75 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2009 Marco Costalba
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_threads();
// Make random number generation less deterministic, for book moves
for (int i = abs(get_system_time() % 10000); i > 0; i--)
genrand_int32();
}
Application::~Application() {
stop_threads();
quit_eval();
}
void Application::initialize() {
// A static Application object is allocated
// once only when this function is called.
static Application singleton;
}
void Application::exit_with_failure() {
exit(EXIT_FAILURE); // d'tor will be called automatically
}
+39
View File
@@ -0,0 +1,39 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2009 Marco Costalba
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&);
~Application();
public:
static void initialize();
static void exit_with_failure();
};
#endif // !defined(APPLICATION_H_INCLUDED)
+73 -46
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -22,8 +22,10 @@
//// Includes //// Includes
//// ////
#include <fstream> #include <fstream>
#include <sstream>
#include <vector> #include <vector>
#include "benchmark.h"
#include "search.h" #include "search.h"
#include "thread.h" #include "thread.h"
#include "ucioption.h" #include "ucioption.h"
@@ -34,10 +36,10 @@ using namespace std;
//// Variables //// Variables
//// ////
static const string BenchmarkPositions[] = { const string BenchmarkPositions[] = {
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -", "r4rk1/1b2qppp/p1n1p3/1p6/1b1PN3/3BRN2/PP3PPP/R2Q2K1 b - - 7 16",
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -", "4r1k1/ppq3pp/3b4/2pP4/2Q1p3/4B1P1/PP5P/R5K1 b - - 0 20",
"4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19", "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
"rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14", "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14",
"r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14", "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14",
@@ -49,9 +51,8 @@ static const string BenchmarkPositions[] = {
"r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16", "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16",
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22", "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18", "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22", "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"
""
}; };
@@ -60,49 +61,63 @@ static const string BenchmarkPositions[] = {
//// ////
/// benchmark() runs a simple benchmark by letting Stockfish analyze a set /// benchmark() runs a simple benchmark by letting Stockfish analyze a set
/// of positions for a given limit each. There are five parameters; the /// of positions for a given time each. There are four parameters; the
/// transposition table size, the number of search threads that should /// transposition table size, the number of search threads that should
/// be used, the limit value spent for each position (optional, default /// be used, the time in seconds spent for each position (optional, default
/// is ply 12), an optional file name where to look for positions in fen /// is 60) and an optional file name where to look for positions in fen
/// format (default are the BenchmarkPositions defined above) and the type /// format (default are the BenchmarkPositions defined above).
/// of the limit value: depth (default), time in secs or number of nodes.
/// The analysis is written to a file named bench.txt. /// The analysis is written to a file named bench.txt.
void benchmark(int argc, char* argv[]) { void benchmark(const string& commandLine) {
vector<string> positions; istringstream csVal(commandLine);
string ttSize, threads, valStr, posFile, valType; istringstream csStr(commandLine);
string ttSize, threads, fileName, limitType, timFile;
int val, secsPerPos, maxDepth, maxNodes; int val, secsPerPos, maxDepth, maxNodes;
ttSize = argc > 2 ? argv[2] : "128"; csStr >> ttSize;
threads = argc > 3 ? argv[3] : "1"; csVal >> val;
valStr = argc > 4 ? argv[4] : "12"; if (val < 4 || val > 1024)
posFile = argc > 5 ? argv[5] : "default"; {
valType = argc > 6 ? argv[6] : "depth"; cerr << "The hash table size must be between 4 and 1024" << endl;
Application::exit_with_failure();
}
csStr >> threads;
csVal >> val;
if (val < 1 || val > THREAD_MAX)
{
cerr << "The number of threads must be between 1 and " << THREAD_MAX << 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");
Options["Hash"].set_value(ttSize); csVal >> val;
Options["Threads"].set_value(threads); csVal >> fileName;
Options["OwnBook"].set_value("false"); csVal >> limitType;
Options["Use Search Log"].set_value("true"); csVal >> timFile;
Options["Search Log Filename"].set_value("bench.txt");
secsPerPos = maxDepth = maxNodes = 0; secsPerPos = maxDepth = maxNodes = 0;
val = atoi(valStr.c_str());
if (valType == "depth" || valType == "perft") if (limitType == "time")
maxDepth = val;
else if (valType == "time")
secsPerPos = val * 1000; secsPerPos = val * 1000;
else if (limitType == "depth" || limitType == "perft")
maxDepth = val;
else else
maxNodes = val; maxNodes = val;
if (posFile != "default") vector<string> positions;
if (fileName != "default")
{ {
ifstream fenFile(posFile.c_str()); ifstream fenFile(fileName.c_str());
if (!fenFile.is_open()) if (!fenFile.is_open())
{ {
cerr << "Unable to open positions file " << posFile << endl; cerr << "Unable to open positions file " << fileName << endl;
exit(EXIT_FAILURE); Application::exit_with_failure();
} }
string pos; string pos;
while (fenFile.good()) while (fenFile.good())
@@ -113,8 +128,19 @@ void benchmark(int argc, char* argv[]) {
} }
fenFile.close(); fenFile.close();
} else } else
for (int i = 0; !BenchmarkPositions[i].empty(); i++) for (int i = 0; i < 16; i++)
positions.push_back(BenchmarkPositions[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();
}
}
vector<string>::iterator it; vector<string>::iterator it;
int cnt = 1; int cnt = 1;
@@ -125,18 +151,13 @@ void benchmark(int argc, char* argv[]) {
{ {
Move moves[1] = {MOVE_NONE}; Move moves[1] = {MOVE_NONE};
int dummy[2] = {0, 0}; int dummy[2] = {0, 0};
Position pos(*it, false, 0); Position pos(*it);
cerr << "\nBench position: " << cnt << '/' << positions.size() << endl << endl; cerr << "\nBench position: " << cnt << '/' << positions.size() << endl << endl;
if (valType == "perft") if (limitType == "perft")
{ totalNodes += perft(pos, maxDepth * OnePly);
int64_t perftCnt = perft(pos, maxDepth * ONE_PLY); else if (!think(pos, false, false, 0, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves))
cerr << "\nPerft " << maxDepth << " result (nodes searched): " << perftCnt << endl << endl;
totalNodes += perftCnt;
} else {
if (!think(pos, false, false, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves))
break; break;
totalNodes += pos.nodes_searched(); totalNodes += nodes_searched();
}
} }
cnt = get_system_time() - startTime; cnt = get_system_time() - startTime;
@@ -145,10 +166,16 @@ void benchmark(int argc, char* argv[]) {
<< "\nNodes searched : " << totalNodes << "\nNodes searched : " << totalNodes
<< "\nNodes/second : " << (int)(totalNodes/(cnt/1000.0)) << endl << endl; << "\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 // Under MS Visual C++ debug window always unconditionally closes
// when program exits, this is bad because we want to read results before. // when program exits, this is bad because we want to read results before.
#if (defined(WINDOWS) || defined(WIN32) || defined(WIN64)) #if (defined(WINDOWS) || defined(WIN32) || defined(WIN64))
cerr << "Press any key to exit" << endl; cerr << "Press any key to exit" << endl;
cin >> ttSize; cin >> fileName;
#endif #endif
} }
+12 -17
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,25 +18,20 @@
*/ */
#if !defined(TIMEMAN_H_INCLUDED) #if !defined(BENCHMARK_H_INCLUDED)
#define TIMEMAN_H_INCLUDED #define BENCHMARK_H_INCLUDED
////
//// Includes
////
#include <string>
//// ////
//// Prototypes //// Prototypes
//// ////
class TimeManager { extern void benchmark(const std::string& commandLine);
public:
void init(int myTime, int myInc, int movesToGo, int currentPly); #endif // !defined(BENCHMARK_H_INCLUDED)
void pv_instability(int curChanges, int prevChanges);
int available_time() const { return optimumSearchTime + unstablePVExtraTime; }
int maximum_time() const { return maximumSearchTime; }
private:
int optimumSearchTime;
int maximumSearchTime;
int unstablePVExtraTime;
};
#endif // !defined(TIMEMAN_H_INCLUDED)
+162 -131
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -24,7 +24,9 @@
#include <cassert> #include <cassert>
#include "bitbase.h"
#include "bitboard.h" #include "bitboard.h"
#include "move.h"
#include "square.h" #include "square.h"
@@ -44,22 +46,30 @@ namespace {
struct KPKPosition { struct KPKPosition {
void from_index(int index); void from_index(int index);
int to_index() const;
bool is_legal() const; bool is_legal() const;
bool is_immediate_draw() const; bool is_immediate_draw() const;
bool is_immediate_win() const; bool is_immediate_win() const;
Bitboard wk_attacks() const { return StepAttackBB[WK][whiteKingSquare]; } Bitboard wk_attacks() const;
Bitboard bk_attacks() const { return StepAttackBB[BK][blackKingSquare]; } Bitboard bk_attacks() const;
Bitboard pawn_attacks() const { return StepAttackBB[WP][pawnSquare]; } Bitboard pawn_attacks() const;
Square whiteKingSquare, blackKingSquare, pawnSquare; Square whiteKingSquare, blackKingSquare, pawnSquare;
Color sideToMove; Color sideToMove;
}; };
const int IndexMax = 2 * 24 * 64 * 64;
Result classify_wtm(const KPKPosition& pos, const Result bb[]); Result *Bitbase;
Result classify_btm(const KPKPosition& pos, const Result bb[]); const int IndexMax = 2*24*64*64;
int UnknownCount = 0;
void initialize();
bool next_iteration();
Result classify_wtm(const KPKPosition &p);
Result classify_btm(const KPKPosition &p);
int compute_index(Square wksq, Square bksq, Square psq, Color stm); int compute_index(Square wksq, Square bksq, Square psq, Color stm);
int compress_result(Result r);
} }
@@ -68,157 +78,175 @@ namespace {
//// ////
void generate_kpk_bitbase(uint8_t bitbase[]) { void generate_kpk_bitbase(uint8_t bitbase[]) {
// Allocate array and initialize:
Bitbase = new Result[IndexMax];
initialize();
bool repeat; // Iterate until all positions are classified:
while(next_iteration());
// Compress bitbase into the supplied parameter:
int i, j, b; int i, j, b;
KPKPosition pos; for(i = 0; i < 24576; i++) {
Result bb[IndexMax]; for(b = 0, j = 0; j < 8; b |= (compress_result(Bitbase[8*i+j]) << j), j++);
assert(b == int(uint8_t(b)));
// Initialize table
for (i = 0; i < IndexMax; i++)
{
pos.from_index(i);
bb[i] = !pos.is_legal() ? RESULT_INVALID
: pos.is_immediate_draw() ? RESULT_DRAW
: pos.is_immediate_win() ? RESULT_WIN : RESULT_UNKNOWN;
}
// Iterate until all positions are classified (30 cycles needed)
do {
repeat = false;
for (i = 0; i < IndexMax; i++)
if (bb[i] == RESULT_UNKNOWN)
{
pos.from_index(i);
bb[i] = (pos.sideToMove == WHITE) ? classify_wtm(pos, bb)
: classify_btm(pos, bb);
if (bb[i] != RESULT_UNKNOWN)
repeat = true;
}
} while (repeat);
// Compress result and map into supplied bitbase parameter
for (i = 0; i < 24576; i++)
{
b = 0;
for (j = 0; j < 8; j++)
if (bb[8*i+j] == RESULT_WIN || bb[8*i+j] == RESULT_LOSS)
b |= (1 << j);
bitbase[i] = (uint8_t)b; bitbase[i] = (uint8_t)b;
} }
// Release allocated memory:
delete [] Bitbase;
} }
namespace { namespace {
int compute_index(Square wksq, Square bksq, Square psq, Color stm) {
int p = int(square_file(psq)) + (int(square_rank(psq)) - 1) * 4;
int r = int(stm) + 2 * int(bksq) + 128 * int(wksq) + 8192 * p;
assert(r >= 0 && r < IndexMax);
return r;
}
void KPKPosition::from_index(int index) { void KPKPosition::from_index(int index) {
int s;
int s = (index / 8192) % 24;
sideToMove = Color(index % 2); sideToMove = Color(index % 2);
blackKingSquare = Square((index / 2) % 64); blackKingSquare = Square((index / 2) % 64);
whiteKingSquare = Square((index / 128) % 64); whiteKingSquare = Square((index / 128) % 64);
s = (index / 8192) % 24;
pawnSquare = make_square(File(s % 4), Rank(s / 4 + 1)); pawnSquare = make_square(File(s % 4), Rank(s / 4 + 1));
} }
int KPKPosition::to_index() const {
return compute_index(whiteKingSquare, blackKingSquare, pawnSquare,
sideToMove);
}
bool KPKPosition::is_legal() const { bool KPKPosition::is_legal() const {
if(whiteKingSquare == pawnSquare || whiteKingSquare == blackKingSquare ||
if ( whiteKingSquare == pawnSquare pawnSquare == blackKingSquare)
|| whiteKingSquare == blackKingSquare
|| pawnSquare == blackKingSquare)
return false; return false;
if(sideToMove == WHITE) {
if (sideToMove == WHITE) if(bit_is_set(this->wk_attacks(), blackKingSquare))
{ return false;
if ( bit_is_set(wk_attacks(), blackKingSquare) if(bit_is_set(this->pawn_attacks(), blackKingSquare))
|| bit_is_set(pawn_attacks(), blackKingSquare))
return false; return false;
} }
else if (bit_is_set(bk_attacks(), whiteKingSquare)) else {
if(bit_is_set(this->bk_attacks(), whiteKingSquare))
return false; return false;
}
return true; return true;
} }
bool KPKPosition::is_immediate_draw() const {
if (sideToMove == BLACK) bool KPKPosition::is_immediate_draw() const {
{ if(sideToMove == BLACK) {
Bitboard wka = wk_attacks(); Bitboard wka = this->wk_attacks();
Bitboard bka = bk_attacks(); Bitboard bka = this->bk_attacks();
// Case 1: Stalemate // Case 1: Stalemate
if ((bka & ~(wka | pawn_attacks())) == EmptyBoardBB) if((bka & ~(wka | this->pawn_attacks())) == EmptyBoardBB)
return true; return true;
// Case 2: King can capture pawn // Case 2: King can capture pawn
if(bit_is_set(bka, pawnSquare) && !bit_is_set(wka, pawnSquare)) if(bit_is_set(bka, pawnSquare) && !bit_is_set(wka, pawnSquare))
return true; return true;
} }
else else {
{
// Case 1: Stalemate // Case 1: Stalemate
if ( whiteKingSquare == SQ_A8 if(whiteKingSquare == SQ_A8 && pawnSquare == SQ_A7 &&
&& pawnSquare == SQ_A7 (blackKingSquare == SQ_C7 || blackKingSquare == SQ_C8))
&& (blackKingSquare == SQ_C7 || blackKingSquare == SQ_C8))
return true; return true;
} }
return false; return false;
} }
bool KPKPosition::is_immediate_win() const {
// The position is an immediate win if it is white to move and the bool KPKPosition::is_immediate_win() const {
// white pawn can be promoted without getting captured. // The position is an immediate win if it is white to move and the white
return sideToMove == WHITE // pawn can be promoted without getting captured:
&& square_rank(pawnSquare) == RANK_7 return
&& ( square_distance(blackKingSquare, pawnSquare + DELTA_N) > 1 sideToMove == WHITE &&
|| bit_is_set(wk_attacks(), pawnSquare + DELTA_N)); square_rank(pawnSquare) == RANK_7 &&
(square_distance(blackKingSquare, pawnSquare+DELTA_N) > 1 ||
bit_is_set(this->wk_attacks(), pawnSquare+DELTA_N));
} }
Result classify_wtm(const KPKPosition& pos, const Result bb[]) {
Bitboard KPKPosition::wk_attacks() const {
return StepAttackBB[WK][whiteKingSquare];
}
Bitboard KPKPosition::bk_attacks() const {
return StepAttackBB[BK][blackKingSquare];
}
Bitboard KPKPosition::pawn_attacks() const {
return StepAttackBB[WP][pawnSquare];
}
void initialize() {
KPKPosition p;
for(int i = 0; i < IndexMax; i++) {
p.from_index(i);
if(!p.is_legal())
Bitbase[i] = RESULT_INVALID;
else if(p.is_immediate_draw())
Bitbase[i] = RESULT_DRAW;
else if(p.is_immediate_win())
Bitbase[i] = RESULT_WIN;
else {
Bitbase[i] = RESULT_UNKNOWN;
UnknownCount++;
}
}
}
bool next_iteration() {
KPKPosition p;
int previousUnknownCount = UnknownCount;
for(int i = 0; i < IndexMax; i++)
if(Bitbase[i] == RESULT_UNKNOWN) {
p.from_index(i);
Bitbase[i] = (p.sideToMove == WHITE)? classify_wtm(p) : classify_btm(p);
if(Bitbase[i] == RESULT_WIN || Bitbase[i] == RESULT_LOSS ||
Bitbase[i] == RESULT_DRAW)
UnknownCount--;
}
return UnknownCount != previousUnknownCount;
}
Result classify_wtm(const KPKPosition &p) {
// If one move leads to a position classified as RESULT_LOSS, the result // If one move leads to a position classified as RESULT_LOSS, the result
// of the current position is RESULT_WIN. If all moves lead to positions // of the current position is RESULT_WIN. If all moves lead to positions
// classified as RESULT_DRAW, the current position is classified RESULT_DRAW // classified as RESULT_DRAW, the current position is classified as
// otherwise the current position is classified as RESULT_UNKNOWN. // RESULT_DRAW. Otherwise, the current position is classified as
// RESULT_UNKNOWN.
bool unknownFound = false; bool unknownFound = false;
Bitboard b; Bitboard b;
Square s; Square s;
int idx;
// King moves // King moves
b = pos.wk_attacks(); b = p.wk_attacks();
while (b) while(b) {
{
s = pop_1st_bit(&b); s = pop_1st_bit(&b);
idx = compute_index(s, pos.blackKingSquare, pos.pawnSquare, BLACK); switch(Bitbase[compute_index(s, p.blackKingSquare, p.pawnSquare,
BLACK)]) {
switch (bb[idx]) {
case RESULT_LOSS: case RESULT_LOSS:
return RESULT_WIN; return RESULT_WIN;
case RESULT_UNKNOWN: case RESULT_UNKNOWN:
unknownFound = true; unknownFound = true;
break;
case RESULT_DRAW: case RESULT_DRAW: case RESULT_INVALID:
case RESULT_INVALID:
break; break;
default: default:
@@ -227,45 +255,37 @@ namespace {
} }
// Pawn moves // Pawn moves
if (square_rank(pos.pawnSquare) < RANK_7) if(square_rank(p.pawnSquare) < RANK_7) {
{ s = p.pawnSquare + DELTA_N;
s = pos.pawnSquare + DELTA_N; switch(Bitbase[compute_index(p.whiteKingSquare, p.blackKingSquare, s,
idx = compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK); BLACK)]) {
switch (bb[idx]) {
case RESULT_LOSS: case RESULT_LOSS:
return RESULT_WIN; return RESULT_WIN;
case RESULT_UNKNOWN: case RESULT_UNKNOWN:
unknownFound = true; unknownFound = true;
break;
case RESULT_DRAW: case RESULT_DRAW: case RESULT_INVALID:
case RESULT_INVALID:
break; break;
default: default:
assert(false); assert(false);
} }
// Double pawn push if(square_rank(s) == RANK_3 &&
if ( square_rank(s) == RANK_3 s != p.whiteKingSquare && s != p.blackKingSquare) {
&& s != pos.whiteKingSquare
&& s != pos.blackKingSquare)
{
s += DELTA_N; s += DELTA_N;
idx = compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK); switch(Bitbase[compute_index(p.whiteKingSquare, p.blackKingSquare, s,
BLACK)]) {
switch (bb[idx]) {
case RESULT_LOSS: case RESULT_LOSS:
return RESULT_WIN; return RESULT_WIN;
case RESULT_UNKNOWN: case RESULT_UNKNOWN:
unknownFound = true; unknownFound = true;
break;
case RESULT_DRAW: case RESULT_DRAW: case RESULT_INVALID:
case RESULT_INVALID:
break; break;
default: default:
@@ -273,11 +293,12 @@ namespace {
} }
} }
} }
return unknownFound? RESULT_UNKNOWN : RESULT_DRAW; return unknownFound? RESULT_UNKNOWN : RESULT_DRAW;
} }
Result classify_btm(const KPKPosition& pos, const Result bb[]) { Result classify_btm(const KPKPosition &p) {
// If one move leads to a position classified as RESULT_DRAW, the result // If one move leads to a position classified as RESULT_DRAW, the result
// of the current position is RESULT_DRAW. If all moves lead to positions // of the current position is RESULT_DRAW. If all moves lead to positions
@@ -288,32 +309,42 @@ namespace {
bool unknownFound = false; bool unknownFound = false;
Bitboard b; Bitboard b;
Square s; Square s;
int idx;
// King moves // King moves
b = pos.bk_attacks(); b = p.bk_attacks();
while (b) while(b) {
{
s = pop_1st_bit(&b); s = pop_1st_bit(&b);
idx = compute_index(pos.whiteKingSquare, s, pos.pawnSquare, WHITE); switch(Bitbase[compute_index(p.whiteKingSquare, s, p.pawnSquare,
WHITE)]) {
switch (bb[idx]) {
case RESULT_DRAW: case RESULT_DRAW:
return RESULT_DRAW; return RESULT_DRAW;
case RESULT_UNKNOWN: case RESULT_UNKNOWN:
unknownFound = true; unknownFound = true;
break;
case RESULT_WIN: case RESULT_WIN: case RESULT_INVALID:
case RESULT_INVALID:
break; break;
default: default:
assert(false); assert(false);
} }
} }
return unknownFound? RESULT_UNKNOWN : RESULT_LOSS; return unknownFound? RESULT_UNKNOWN : RESULT_LOSS;
} }
int compute_index(Square wksq, Square bksq, Square psq, Color stm) {
int p = int(square_file(psq)) + (int(square_rank(psq)) - 1) * 4;
int result = int(stm) + 2*int(bksq) + 128*int(wksq) + 8192*p;
assert(result >= 0 && result < IndexMax);
return result;
}
int compress_result(Result r) {
return (r == RESULT_WIN || r == RESULT_LOSS)? 1 : 0;
}
} }
+38
View File
@@ -0,0 +1,38 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2009 Marco Costalba
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)
+113 -102
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@
#include "bitboard.h" #include "bitboard.h"
#include "bitcount.h" #include "bitcount.h"
#include "direction.h"
#if defined(IS_64BIT) #if defined(IS_64BIT)
@@ -162,10 +163,7 @@ const int RShift[64] = {
#endif // defined(IS_64BIT) #endif // defined(IS_64BIT)
const Bitboard LightSquaresBB = 0x55AA55AA55AA55AAULL; const Bitboard SquaresByColorBB[2] = { BlackSquaresBB, WhiteSquaresBB };
const Bitboard DarkSquaresBB = 0xAA55AA55AA55AA55ULL;
const Bitboard SquaresByColorBB[2] = { DarkSquaresBB, LightSquaresBB };
const Bitboard FileBB[8] = { const Bitboard FileBB[8] = {
FileABB, FileBBB, FileCBB, FileDBB, FileEBB, FileFBB, FileGBB, FileHBB FileABB, FileBBB, FileCBB, FileDBB, FileEBB, FileFBB, FileGBB, FileHBB
@@ -225,11 +223,11 @@ Bitboard SetMaskBB[65];
Bitboard ClearMaskBB[65]; Bitboard ClearMaskBB[65];
Bitboard StepAttackBB[16][64]; Bitboard StepAttackBB[16][64];
Bitboard RayBB[64][8];
Bitboard BetweenBB[64][64]; Bitboard BetweenBB[64][64];
Bitboard SquaresInFrontMask[2][64];
Bitboard PassedPawnMask[2][64]; Bitboard PassedPawnMask[2][64];
Bitboard AttackSpanMask[2][64]; Bitboard OutpostMask[2][64];
Bitboard BishopPseudoAttacks[64]; Bitboard BishopPseudoAttacks[64];
Bitboard RookPseudoAttacks[64]; Bitboard RookPseudoAttacks[64];
@@ -245,14 +243,17 @@ uint8_t BitCount8Bit[256];
namespace { namespace {
void init_masks(); void init_masks();
void init_ray_bitboards();
void init_attacks(); void init_attacks();
void init_between_bitboards(); void init_between_bitboards();
void init_pseudo_attacks();
Bitboard index_to_bitboard(int index, Bitboard mask);
Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2], Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
int fmin, int fmax, int rmin, int rmax); int fmin, int fmax, int rmin, int rmax);
void init_sliding_attacks(Bitboard attacks[], int attackIndex[], Bitboard mask[], Bitboard index_to_bitboard(int index, Bitboard mask);
const int shift[], const Bitboard mult[], int deltas[][2]); void init_sliding_attacks(Bitboard attacks[],
int attackIndex[], Bitboard mask[],
const int shift[2], const Bitboard mult[],
int deltas[][2]);
void init_pseudo_attacks();
} }
@@ -264,13 +265,10 @@ namespace {
/// standard output. This is sometimes useful for debugging. /// standard output. This is sometimes useful for debugging.
void print_bitboard(Bitboard b) { void print_bitboard(Bitboard b) {
for(Rank r = RANK_8; r >= RANK_1; r--) {
for (Rank r = RANK_8; r >= RANK_1; r--)
{
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl; std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
for(File f = FILE_A; f <= FILE_H; f++) for(File f = FILE_A; f <= FILE_H; f++)
std::cout << "| " << (bit_is_set(b, make_square(f, r))? 'X' : ' ') << ' '; std::cout << "| " << (bit_is_set(b, make_square(f, r))? 'X' : ' ') << ' ';
std::cout << "|" << std::endl; std::cout << "|" << std::endl;
} }
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl; std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
@@ -281,16 +279,15 @@ void print_bitboard(Bitboard b) {
/// program initialization. /// program initialization.
void init_bitboards() { void init_bitboards() {
int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}}; int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}}; int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
init_masks(); init_masks();
init_ray_bitboards();
init_attacks(); init_attacks();
init_between_bitboards();
init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift, RMult, rookDeltas); init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift, RMult, rookDeltas);
init_sliding_attacks(BAttacks, BAttackIndex, BMask, BShift, BMult, bishopDeltas); init_sliding_attacks(BAttacks, BAttackIndex, BMask, BShift, BMult, bishopDeltas);
init_pseudo_attacks(); init_pseudo_attacks();
init_between_bitboards();
} }
@@ -300,8 +297,8 @@ void init_bitboards() {
#if defined(IS_64BIT) && !defined(USE_BSFQ) #if defined(IS_64BIT) && !defined(USE_BSFQ)
static CACHE_LINE_ALIGNMENT CACHE_LINE_ALIGNMENT
const int BitTable[64] = { static const int BitTable[64] = {
0, 1, 2, 7, 3, 13, 8, 19, 4, 25, 14, 28, 9, 34, 20, 40, 5, 17, 26, 38, 15, 0, 1, 2, 7, 3, 13, 8, 19, 4, 25, 14, 28, 9, 34, 20, 40, 5, 17, 26, 38, 15,
46, 29, 48, 10, 31, 35, 54, 21, 50, 41, 57, 63, 6, 12, 18, 24, 27, 33, 39, 46, 29, 48, 10, 31, 35, 54, 21, 50, 41, 57, 63, 6, 12, 18, 24, 27, 33, 39,
16, 37, 45, 47, 30, 53, 49, 56, 62, 11, 23, 32, 36, 44, 52, 55, 61, 22, 43, 16, 37, 45, 47, 30, 53, 49, 56, 62, 11, 23, 32, 36, 44, 52, 55, 61, 22, 43,
@@ -329,7 +326,6 @@ const int BitTable[64] = {
}; };
Square first_1(Bitboard b) { Square first_1(Bitboard b) {
b ^= (b - 1); b ^= (b - 1);
uint32_t fold = int(b) ^ int(b >> 32); uint32_t fold = int(b) ^ int(b >> 32);
return Square(BitTable[(fold * 0x783a9b23) >> 26]); return Square(BitTable[(fold * 0x783a9b23) >> 26]);
@@ -372,6 +368,28 @@ Square pop_1st_bit(Bitboard* bb) {
#endif #endif
int bitScanReverse32(uint32_t b)
{
int result = 0;
if (b > 0xFFFF) {
b >>= 16;
result += 16;
}
if (b > 0xFF) {
b >>= 8;
result += 8;
}
if (b > 0xF) {
b >>= 4;
result += 4;
}
if (b > 0x3) {
b >>= 2;
result += 2;
}
return result + (b > 0) + (b > 1);
}
namespace { namespace {
@@ -381,31 +399,39 @@ namespace {
// be necessary to touch any of them. // be necessary to touch any of them.
void init_masks() { void init_masks() {
SetMaskBB[SQ_NONE] = 0ULL; SetMaskBB[SQ_NONE] = 0ULL;
ClearMaskBB[SQ_NONE] = ~SetMaskBB[SQ_NONE]; ClearMaskBB[SQ_NONE] = ~SetMaskBB[SQ_NONE];
for(Square s = SQ_A1; s <= SQ_H8; s++) {
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
SetMaskBB[s] = (1ULL << s); SetMaskBB[s] = (1ULL << s);
ClearMaskBB[s] = ~SetMaskBB[s]; ClearMaskBB[s] = ~SetMaskBB[s];
} }
for(Color c = WHITE; c <= BLACK; c++) for(Color c = WHITE; c <= BLACK; c++)
for (Square s = SQ_A1; s <= SQ_H8; s++) for(Square s = SQ_A1; s <= SQ_H8; s++) {
{ PassedPawnMask[c][s] =
SquaresInFrontMask[c][s] = in_front_bb(c, s) & file_bb(s); in_front_bb(c, s) & this_and_neighboring_files_bb(s);
PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_neighboring_files_bb(s); OutpostMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
} }
for (Bitboard b = 0; b < 256; b++) for (Bitboard b = 0ULL; b < 256ULL; b++)
BitCount8Bit[b] = (uint8_t)count_1s<CNT32>(b); BitCount8Bit[b] = (uint8_t)count_1s(b);
} }
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[(i&7)|((i>>4)<<3)][j] = EmptyBoardBB;
for(int k = i + d[j]; (k & 0x88) == 0; k += d[j])
set_bit(&(RayBB[(i&7)|((i>>4)<<3)][j]), Square((k&7)|((k>>4)<<3)));
}
}
}
void init_attacks() { void init_attacks() {
int i, j, k, l;
const int step[16][8] = { int step[16][8] = {
{0}, {0},
{7,9,0}, {17,15,10,6,-6,-10,-15,-17}, {9,7,-7,-9,0}, {8,1,-1,-8,0}, {7,9,0}, {17,15,10,6,-6,-10,-15,-17}, {9,7,-7,-9,0}, {8,1,-1,-8,0},
{9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8}, {0}, {0}, {9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8}, {0}, {0},
@@ -413,117 +439,102 @@ namespace {
{9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8} {9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8}
}; };
for (int i = 0; i < 64; i++) for(i = 0; i < 64; i++) {
for (int j = 0; j <= int(BK); j++) for(j = 0; j <= int(BK); j++) {
{
StepAttackBB[j][i] = EmptyBoardBB; StepAttackBB[j][i] = EmptyBoardBB;
for (int k = 0; k < 8 && step[j][k] != 0; k++) for(k = 0; k < 8 && step[j][k] != 0; k++) {
{ l = i + step[j][k];
int l = i + step[j][k];
if(l >= 0 && l < 64 && abs((i&7) - (l&7)) < 3) if(l >= 0 && l < 64 && abs((i&7) - (l&7)) < 3)
StepAttackBB[j][i] |= (1ULL << l); StepAttackBB[j][i] |= (1ULL << l);
} }
} }
} }
}
Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2], Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
int fmin=0, int fmax=7, int rmin=0, int rmax=7) { int fmin=0, int fmax=7, int rmin=0, int rmax=7) {
Bitboard result = 0ULL; Bitboard result = 0ULL;
int rk = sq / 8; int rk = sq / 8, fl = sq % 8, r, f, i;
int fl = sq % 8; for(i = 0; i < dirs; i++) {
int dx = deltas[i][0], dy = deltas[i][1];
for (int i = 0; i < dirs; i++) for(f = fl+dx, r = rk+dy;
{ (dx==0 || (f>=fmin && f<=fmax)) && (dy==0 || (r>=rmin && r<=rmax));
int dx = deltas[i][0]; f += dx, r += dy) {
int dy = deltas[i][1];
int f = fl + dx;
int r = rk + dy;
while ( (dx == 0 || (f >= fmin && f <= fmax))
&& (dy == 0 || (r >= rmin && r <= rmax)))
{
result |= (1ULL << (f + r*8)); result |= (1ULL << (f + r*8));
if (block & (1ULL << (f + r*8))) if(block & (1ULL << (f + r*8))) break;
break;
f += dx;
r += dy;
} }
} }
return result; return result;
} }
void init_between_bitboards() { void init_between_bitboards() {
SquareDelta step[8] = {
Square s1, s2, s3; DELTA_E, DELTA_W, DELTA_N, DELTA_S, DELTA_NE, DELTA_SW, DELTA_NW, DELTA_SE
SquareDelta d; };
int f, r; SignedDirection d;
for(Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (s1 = SQ_A1; s1 <= SQ_H8; s1++) for(Square s2 = SQ_A1; s2 <= SQ_H8; s2++) {
for (s2 = SQ_A1; s2 <= SQ_H8; s2++)
{
BetweenBB[s1][s2] = EmptyBoardBB; BetweenBB[s1][s2] = EmptyBoardBB;
d = signed_direction_between_squares(s1, s2);
if (bit_is_set(QueenPseudoAttacks[s1], s2)) if(d != SIGNED_DIR_NONE)
{ for(Square s3 = s1 + step[d]; s3 != s2; s3 += step[d])
f = file_distance(s1, s2);
r = rank_distance(s1, s2);
d = SquareDelta(s2 - s1) / Max(f, r);
for (s3 = s1 + d; s3 != s2; s3 += d)
set_bit(&(BetweenBB[s1][s2]), s3); set_bit(&(BetweenBB[s1][s2]), s3);
} }
} }
}
Bitboard index_to_bitboard(int index, Bitboard mask) { Bitboard index_to_bitboard(int index, Bitboard mask) {
int i, j, bits = count_1s(mask);
Bitboard result = 0ULL; Bitboard result = 0ULL;
int bits = count_1s<CNT32>(mask); for(i = 0; i < bits; i++) {
j = pop_1st_bit(&mask);
for (int i = 0; i < bits; i++) if(index & (1 << i)) result |= (1ULL << j);
{
int j = pop_1st_bit(&mask);
if (index & (1 << i))
result |= (1ULL << j);
} }
return result; return result;
} }
void init_sliding_attacks(Bitboard attacks[], int attackIndex[], Bitboard mask[],
const int shift[], const Bitboard mult[], int deltas[][2]) {
for (int i = 0, index = 0; i < 64; i++) void init_sliding_attacks(Bitboard attacks[],
{ int attackIndex[], Bitboard mask[],
const int shift[2], const Bitboard mult[],
int deltas[][2]) {
int i, j, k, index = 0;
Bitboard b;
for(i = 0; i < 64; i++) {
attackIndex[i] = index; attackIndex[i] = index;
mask[i] = sliding_attacks(i, 0, 4, deltas, 1, 6, 1, 6); mask[i] = sliding_attacks(i, 0ULL, 4, deltas, 1, 6, 1, 6);
#if defined(IS_64BIT) #if defined(IS_64BIT)
int j = (1 << (64 - shift[i])); j = (1 << (64 - shift[i]));
#else #else
int j = (1 << (32 - shift[i])); j = (1 << (32 - shift[i]));
#endif #endif
for (int k = 0; k < j; k++) for(k = 0; k < j; k++) {
{
#if defined(IS_64BIT) #if defined(IS_64BIT)
Bitboard b = index_to_bitboard(k, mask[i]); b = index_to_bitboard(k, mask[i]);
attacks[index + ((b * mult[i]) >> shift[i])] = sliding_attacks(i, b, 4, deltas); attacks[index + ((b * mult[i]) >> shift[i])] =
sliding_attacks(i, b, 4, deltas);
#else #else
Bitboard b = index_to_bitboard(k, mask[i]); b = index_to_bitboard(k, mask[i]);
unsigned v = int(b) * int(mult[i]) ^ int(b >> 32) * int(mult[i] >> 32); attacks[index +
attacks[index + (v >> shift[i])] = sliding_attacks(i, b, 4, deltas); (unsigned(int(b) * int(mult[i]) ^
int(b >> 32) * int(mult[i] >> 32))
>> shift[i])] =
sliding_attacks(i, b, 4, deltas);
#endif #endif
} }
index += j; index += j;
} }
} }
void init_pseudo_attacks() {
for (Square s = SQ_A1; s <= SQ_H8; s++) void init_pseudo_attacks() {
{ Square s;
for(s = SQ_A1; s <= SQ_H8; s++) {
BishopPseudoAttacks[s] = bishop_attacks_bb(s, EmptyBoardBB); BishopPseudoAttacks[s] = bishop_attacks_bb(s, EmptyBoardBB);
RookPseudoAttacks[s] = rook_attacks_bb(s, EmptyBoardBB); RookPseudoAttacks[s] = rook_attacks_bb(s, EmptyBoardBB);
QueenPseudoAttacks[s] = queen_attacks_bb(s, EmptyBoardBB); QueenPseudoAttacks[s] = queen_attacks_bb(s, EmptyBoardBB);
+54 -42
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@
//// Includes //// Includes
//// ////
#include "direction.h"
#include "piece.h" #include "piece.h"
#include "square.h" #include "square.h"
#include "types.h" #include "types.h"
@@ -35,25 +36,28 @@
//// Constants and variables //// Constants and variables
//// ////
const Bitboard EmptyBoardBB = 0; const Bitboard EmptyBoardBB = 0ULL;
const Bitboard WhiteSquaresBB = 0x55AA55AA55AA55AAULL;
const Bitboard BlackSquaresBB = 0xAA55AA55AA55AA55ULL;
const Bitboard FileABB = 0x0101010101010101ULL; const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = FileABB << 1; const Bitboard FileBBB = 0x0202020202020202ULL;
const Bitboard FileCBB = FileABB << 2; const Bitboard FileCBB = 0x0404040404040404ULL;
const Bitboard FileDBB = FileABB << 3; const Bitboard FileDBB = 0x0808080808080808ULL;
const Bitboard FileEBB = FileABB << 4; const Bitboard FileEBB = 0x1010101010101010ULL;
const Bitboard FileFBB = FileABB << 5; const Bitboard FileFBB = 0x2020202020202020ULL;
const Bitboard FileGBB = FileABB << 6; const Bitboard FileGBB = 0x4040404040404040ULL;
const Bitboard FileHBB = FileABB << 7; const Bitboard FileHBB = 0x8080808080808080ULL;
const Bitboard Rank1BB = 0xFF; const Bitboard Rank1BB = 0xFFULL;
const Bitboard Rank2BB = Rank1BB << (8 * 1); const Bitboard Rank2BB = 0xFF00ULL;
const Bitboard Rank3BB = Rank1BB << (8 * 2); const Bitboard Rank3BB = 0xFF0000ULL;
const Bitboard Rank4BB = Rank1BB << (8 * 3); const Bitboard Rank4BB = 0xFF000000ULL;
const Bitboard Rank5BB = Rank1BB << (8 * 4); const Bitboard Rank5BB = 0xFF00000000ULL;
const Bitboard Rank6BB = Rank1BB << (8 * 5); const Bitboard Rank6BB = 0xFF0000000000ULL;
const Bitboard Rank7BB = Rank1BB << (8 * 6); const Bitboard Rank7BB = 0xFF000000000000ULL;
const Bitboard Rank8BB = Rank1BB << (8 * 7); const Bitboard Rank8BB = 0xFF00000000000000ULL;
extern const Bitboard SquaresByColorBB[2]; extern const Bitboard SquaresByColorBB[2];
extern const Bitboard FileBB[8]; extern const Bitboard FileBB[8];
@@ -67,11 +71,11 @@ extern Bitboard SetMaskBB[65];
extern Bitboard ClearMaskBB[65]; extern Bitboard ClearMaskBB[65];
extern Bitboard StepAttackBB[16][64]; extern Bitboard StepAttackBB[16][64];
extern Bitboard RayBB[64][8];
extern Bitboard BetweenBB[64][64]; extern Bitboard BetweenBB[64][64];
extern Bitboard SquaresInFrontMask[2][64];
extern Bitboard PassedPawnMask[2][64]; extern Bitboard PassedPawnMask[2][64];
extern Bitboard AttackSpanMask[2][64]; extern Bitboard OutpostMask[2][64];
extern const uint64_t RMult[64]; extern const uint64_t RMult[64];
extern const int RShift[64]; extern const int RShift[64];
@@ -123,8 +127,9 @@ inline void do_move_bb(Bitboard *b, Bitboard move_bb) {
*b ^= move_bb; *b ^= move_bb;
} }
/// rank_bb() and file_bb() take a file or a square as input, and return /// rank_bb() and file_bb() gives a bitboard containing all squares on a given
/// a bitboard representing all squares on the given file or rank. /// file or rank. It is also possible to pass a square as input to these
/// functions.
inline Bitboard rank_bb(Rank r) { inline Bitboard rank_bb(Rank r) {
return RankBB[r]; return RankBB[r];
@@ -151,7 +156,7 @@ inline Bitboard neighboring_files_bb(File f) {
} }
inline Bitboard neighboring_files_bb(Square s) { inline Bitboard neighboring_files_bb(Square s) {
return NeighboringFilesBB[square_file(s)]; return neighboring_files_bb(square_file(s));
} }
@@ -164,7 +169,7 @@ inline Bitboard this_and_neighboring_files_bb(File f) {
} }
inline Bitboard this_and_neighboring_files_bb(Square s) { inline Bitboard this_and_neighboring_files_bb(Square s) {
return ThisAndNeighboringFilesBB[square_file(s)]; return this_and_neighboring_files_bb(square_file(s));
} }
@@ -190,7 +195,7 @@ inline Bitboard in_front_bb(Color c, Rank r) {
} }
inline Bitboard in_front_bb(Color c, Square s) { inline Bitboard in_front_bb(Color c, Square s) {
return InFrontBB[c][square_rank(s)]; return in_front_bb(c, square_rank(s));
} }
@@ -203,7 +208,15 @@ inline Bitboard behind_bb(Color c, Rank r) {
} }
inline Bitboard behind_bb(Color c, Square s) { inline Bitboard behind_bb(Color c, Square s) {
return InFrontBB[opposite_color(c)][square_rank(s)]; return in_front_bb(opposite_color(c), square_rank(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];
} }
@@ -261,11 +274,12 @@ inline Bitboard squares_between(Square s1, Square s2) {
/// squares_in_front_of takes a color and a square as input, and returns a /// squares_in_front_of takes a color and a square as input, and returns a
/// bitboard representing all squares along the line in front of the square, /// bitboard representing all squares along the line in front of the square,
/// from the point of view of the given color. Definition of the table is: /// from the point of view of the given color. For instance,
/// SquaresInFrontOf[c][s] = in_front_bb(c, s) & file_bb(s) /// squares_in_front_of(BLACK, SQ_E4) returns a bitboard with the squares
/// e3, e2 and e1 set.
inline Bitboard squares_in_front_of(Color c, Square s) { inline Bitboard squares_in_front_of(Color c, Square s) {
return SquaresInFrontMask[c][s]; return in_front_bb(c, s) & file_bb(s);
} }
@@ -273,36 +287,33 @@ inline Bitboard squares_in_front_of(Color c, Square s) {
/// behind the square instead of in front of the square. /// behind the square instead of in front of the square.
inline Bitboard squares_behind(Color c, Square s) { inline Bitboard squares_behind(Color c, Square s) {
return SquaresInFrontMask[opposite_color(c)][s]; return in_front_bb(opposite_color(c), s) & file_bb(s);
} }
/// passed_pawn_mask takes a color and a square as input, and returns a /// passed_pawn_mask takes a color and a square as input, and returns a
/// bitboard mask which can be used to test if a pawn of the given color on /// bitboard mask which can be used to test if a pawn of the given color on
/// the given square is a passed pawn. Definition of the table is: /// the given square is a passed pawn.
/// PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_neighboring_files_bb(s)
inline Bitboard passed_pawn_mask(Color c, Square s) { inline Bitboard passed_pawn_mask(Color c, Square s) {
return PassedPawnMask[c][s]; return PassedPawnMask[c][s];
} }
/// attack_span_mask takes a color and a square as input, and returns a bitboard /// outpost_mask takes a color and a square as input, and returns a bitboard
/// representing all squares that can be attacked by a pawn of the given color /// mask which can be used to test whether a piece on the square can possibly
/// when it moves along its file starting from the given square. Definition is: /// be driven away by an enemy pawn.
/// AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
inline Bitboard attack_span_mask(Color c, Square s) { inline Bitboard outpost_mask(Color c, Square s) {
return AttackSpanMask[c][s]; return OutpostMask[c][s];
} }
/// squares_aligned returns true if the squares s1, s2 and s3 are aligned /// isolated_pawn_mask takes a square as input, and returns a bitboard mask
/// either on a straight or on a diagonal line. /// which can be used to test whether a pawn on the given square is isolated.
inline bool squares_aligned(Square s1, Square s2, Square s3) { inline Bitboard isolated_pawn_mask(Square s) {
return (BetweenBB[s1][s2] | BetweenBB[s1][s3] | BetweenBB[s2][s3]) return neighboring_files_bb(s);
& ((1ULL << s1) | (1ULL << s2) | (1ULL << s3));
} }
@@ -338,6 +349,7 @@ extern Square pop_1st_bit(Bitboard* b);
extern void print_bitboard(Bitboard b); extern void print_bitboard(Bitboard b);
extern void init_bitboards(); extern void init_bitboards();
extern int bitScanReverse32(uint32_t b);
#endif // !defined(BITBOARD_H_INCLUDED) #endif // !defined(BITBOARD_H_INCLUDED)
+95 -53
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -24,23 +24,73 @@
#include "types.h" #include "types.h"
enum BitCountType { // Select type of intrinsic bit count instruction to use, see
CNT64, // README.txt on how to pgo compile with POPCNT support.
CNT64_MAX15,
CNT32,
CNT32_MAX15,
CNT_POPCNT
};
/// count_1s() counts the number of nonzero bits in a bitboard. #if defined(__INTEL_COMPILER) && defined(USE_POPCNT) // Intel compiler
/// We have different optimized versions according if platform
/// is 32 or 64 bits, and to the maximum number of nonzero bits.
/// We also support hardware popcnt instruction. See Readme.txt
/// on how to pgo compile with popcnt support.
template<BitCountType> inline int count_1s(Bitboard);
template<> #include <nmmintrin.h>
inline int count_1s<CNT64>(Bitboard b) {
inline bool cpu_has_popcnt() {
int CPUInfo[4] = {-1};
__cpuid(CPUInfo, 0x00000001);
return (CPUInfo[2] >> 23) & 1;
}
#define POPCNT_INTRINSIC(x) _mm_popcnt_u64(x)
#elif defined(_MSC_VER) && defined(USE_POPCNT) // Microsoft compiler
#include <intrin.h>
inline bool cpu_has_popcnt() {
int CPUInfo[4] = {-1};
__cpuid(CPUInfo, 0x00000001);
return (CPUInfo[2] >> 23) & 1;
}
#define POPCNT_INTRINSIC(x) __popcnt64(x)
#elif defined(__GNUC__) && defined(USE_POPCNT) // Gcc compiler
inline void __cpuid(unsigned int op,
unsigned int *eax, unsigned int *ebx,
unsigned int *ecx, unsigned int *edx)
{
*eax = op;
*ecx = 0;
__asm__("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
: "0" (*eax), "2" (*ecx));
}
inline bool cpu_has_popcnt() {
unsigned int eax, ebx, ecx, edx;
__cpuid(1, &eax, &ebx, &ecx, &edx);
return (ecx >> 23) & 1;
}
#define POPCNT_INTRINSIC(x) ({ \
unsigned long __ret; \
__asm__("popcnt %1, %0" : "=r" (__ret) : "r" (x)); \
__ret; })
#else // Safe fallback for unsupported compilers or when USE_POPCNT is disabled
inline bool cpu_has_popcnt() { return false; }
#define POPCNT_INTRINSIC(x) 0
#endif // cpu_has_popcnt() and POPCNT_INTRINSIC() definitions
/// Software implementation of bit count functions
#if defined(IS_64BIT)
inline int count_1s(Bitboard b) {
b -= ((b>>1) & 0x5555555555555555ULL); b -= ((b>>1) & 0x5555555555555555ULL);
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL; b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL;
@@ -48,16 +98,16 @@ inline int count_1s<CNT64>(Bitboard b) {
return int(b >> 56); return int(b >> 56);
} }
template<> inline int count_1s_max_15(Bitboard b) {
inline int count_1s<CNT64_MAX15>(Bitboard b) {
b -= (b>>1) & 0x5555555555555555ULL; b -= (b>>1) & 0x5555555555555555ULL;
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b *= 0x1111111111111111ULL; b *= 0x1111111111111111ULL;
return int(b >> 60); return int(b >> 60);
} }
template<> #else // if !defined(IS_64BIT)
inline int count_1s<CNT32>(Bitboard b) {
inline int count_1s(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b); unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555; w -= (w >> 1) & 0x55555555;
@@ -69,8 +119,7 @@ inline int count_1s<CNT32>(Bitboard b) {
return int(v >> 24); return int(v >> 24);
} }
template<> inline int count_1s_max_15(Bitboard b) {
inline int count_1s<CNT32_MAX15>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b); unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555; w -= (w >> 1) & 0x55555555;
@@ -81,45 +130,38 @@ inline int count_1s<CNT32_MAX15>(Bitboard b) {
return int(v >> 28); return int(v >> 28);
} }
template<> #endif // BITCOUNT
inline int count_1s<CNT_POPCNT>(Bitboard b) {
#if !defined(USE_POPCNT)
return int(b != 0); // Avoid 'b not used' warning /// count_1s() counts the number of nonzero bits in a bitboard.
#elif defined(_MSC_VER) /// If template parameter is true an intrinsic is called, otherwise
return __popcnt64(b); /// we fallback on a software implementation.
#elif defined(__GNUC__)
unsigned long ret; template<bool UseIntrinsic>
__asm__("popcnt %1, %0" : "=r" (ret) : "r" (b)); inline int count_1s(Bitboard b) {
return ret;
#endif return UseIntrinsic ? POPCNT_INTRINSIC(b) : count_1s(b);
}
template<bool UseIntrinsic>
inline int count_1s_max_15(Bitboard b) {
return UseIntrinsic ? POPCNT_INTRINSIC(b) : count_1s_max_15(b);
} }
/// cpu_has_popcnt() detects support for popcnt instruction at runtime // Global constant initialized at startup that is set to true if
inline bool cpu_has_popcnt() { // CPU on which application runs supports POPCNT intrinsic. Unless
// USE_POPCNT is not defined.
int CPUInfo[4] = {-1};
__cpuid(CPUInfo, 0x00000001);
return (CPUInfo[2] >> 23) & 1;
}
/// CpuHasPOPCNT is a global constant initialized at startup that
/// is set to true if CPU on which application runs supports popcnt
/// hardware instruction. Unless USE_POPCNT is not defined.
#if defined(USE_POPCNT)
const bool CpuHasPOPCNT = cpu_has_popcnt(); const bool CpuHasPOPCNT = cpu_has_popcnt();
#else
const bool CpuHasPOPCNT = false;
#endif
/// CpuIs64Bit is a global constant initialized at compile time that // Global constant used to print info about the use of 64 optimized
/// is set to true if CPU on which application runs is a 64 bits. // functions to verify that a 64 bit compile has been correctly built.
#if defined(IS_64BIT) #if defined(IS_64BIT)
const bool CpuIs64Bit = true; const bool CpuHas64BitPath = true;
#else #else
const bool CpuIs64Bit = false; const bool CpuHas64BitPath = false;
#endif #endif
#endif // !defined(BITCOUNT_H_INCLUDED) #endif // !defined(BITCOUNT_H_INCLUDED)
+80 -84
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -32,20 +32,30 @@
#include <cassert> #include <cassert>
#include "book.h" #include "book.h"
#include "mersenne.h"
#include "movegen.h" #include "movegen.h"
using namespace std; using namespace std;
////
//// Global variables
////
Book OpeningBook;
//// ////
//// Local definitions //// Local definitions
//// ////
namespace { namespace {
// Book entry size in bytes /// Book entry size in bytes
const int EntrySize = 16; 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] = { const uint64_t Random64[781] = {
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL, 0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL, 0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
@@ -310,13 +320,17 @@ namespace {
0xF8D626AAAF278509ULL 0xF8D626AAAF278509ULL
}; };
// Indices to the Random64[] array
const int PieceIdx = 0;
const int CastleIdx = 768;
const int EnPassantIdx = 772;
const int TurnIdx = 780;
// Local functions /// Indices to the Random64[] array
const int RandomPiece = 0;
const int RandomCastle = 768;
const int RandomEnPassant = 772;
const int RandomTurn = 780;
/// Prototypes
uint64_t book_key(const Position& pos); uint64_t book_key(const Position& pos);
uint64_t book_piece_key(Piece p, Square s); uint64_t book_piece_key(Piece p, Square s);
uint64_t book_castle_key(const Position& pos); uint64_t book_castle_key(const Position& pos);
@@ -329,13 +343,6 @@ namespace {
//// Functions //// 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. /// Destructor. Be sure file is closed before we leave.
@@ -345,6 +352,31 @@ Book::~Book() {
} }
/// Book::open() opens a book file with a given file name
void Book::open(const string& fName) {
// Close old file before opening the new
close();
fileName = fName;
ifstream::open(fileName.c_str(), ifstream::in | ifstream::binary);
if (!is_open())
return;
// Get the book size in number of entries
seekg(0, ios::end);
bookSize = tellg() / EntrySize;
seekg(0, ios::beg);
if (!good())
{
cerr << "Failed to open book file " << fileName << endl;
Application::exit_with_failure();
}
}
/// Book::close() closes the file only if it is open, otherwise /// 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. /// we can end up in a little mess due to how std::ifstream works.
@@ -355,37 +387,10 @@ void Book::close() {
} }
/// Book::open() opens a book file with a given file name
void Book::open(const string& fName) {
// Close old file before opening the new
close();
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;
// Get the book size in number of entries
seekg(0, ios::end);
bookSize = long(tellg()) / EntrySize;
seekg(0, ios::beg);
if (!good())
{
cerr << "Failed to open book file " << fileName << endl;
exit(EXIT_FAILURE);
}
}
/// Book::file_name() returns the file name of the currently active book, /// Book::file_name() returns the file name of the currently active book,
/// or the empty string if no book is open. /// or the empty string if no book is open.
const string Book::file_name() { // Not const to compile on HP-UX 11.X const string Book::file_name() const {
return is_open() ? fileName : ""; return is_open() ? fileName : "";
} }
@@ -394,15 +399,14 @@ const string Book::file_name() { // Not const to compile on HP-UX 11.X
/// Book::get_move() gets a book move for a given position. Returns /// Book::get_move() gets a book move for a given position. Returns
/// MOVE_NONE if no book move is found. /// MOVE_NONE if no book move is found.
Move Book::get_move(const Position& pos, bool findBestMove) { Move Book::get_move(const Position& pos) {
if (!is_open() || bookSize == 0) if (!is_open() || bookSize == 0)
return MOVE_NONE; return MOVE_NONE;
BookEntry entry; int bookMove = 0, scoresSum = 0;
int bookMove = MOVE_NONE;
unsigned scoresSum = 0, bestScore = 0;
uint64_t key = book_key(pos); uint64_t key = book_key(pos);
BookEntry entry;
// Choose a book move among the possible moves for the given position // Choose a book move among the possible moves for the given position
for (int idx = find_key(key); idx < bookSize; idx++) for (int idx = find_key(key); idx < bookSize; idx++)
@@ -411,31 +415,21 @@ Move Book::get_move(const Position& pos, bool findBestMove) {
if (entry.key != key) if (entry.key != key)
break; break;
unsigned score = entry.count; int score = entry.count;
// If findBestMove is true choose highest rated book move assert(score > 0);
if (findBestMove)
{
if (score > bestScore)
{
bestScore = score;
bookMove = entry.move;
}
continue;
}
// Choose book move according to its score. If a move has a very // Choose book move according to its score. If a move has a very
// high score it has more probability to be choosen then a one with // high score it has more probability to be choosen then a one with
// lower score. Note that first entry is always chosen. // lower score. Note that first entry is always chosen.
scoresSum += score; scoresSum += score;
if (RKiss.rand<unsigned>() % scoresSum < score) if (int(genrand_int32() % scoresSum) < score)
bookMove = entry.move; bookMove = entry.move;
} }
if (!bookMove) if (!bookMove)
return MOVE_NONE; return MOVE_NONE;
// Verify the book move is legal MoveStack mlist[256];
MoveStack mlist[MOVES_MAX];
MoveStack* last = generate_moves(pos, mlist); MoveStack* last = generate_moves(pos, mlist);
for (MoveStack* cur = mlist; cur != last; cur++) for (MoveStack* cur = mlist; cur != last; cur++)
if ((int(cur->move) & 07777) == bookMove) if ((int(cur->move) & 07777) == bookMove)
@@ -468,7 +462,6 @@ int Book::find_key(uint64_t key) {
assert(mid >= left && mid < right); assert(mid >= left && mid < right);
read_entry(entry, mid); read_entry(entry, mid);
if (key <= entry.key) if (key <= entry.key)
right = mid; right = mid;
else else
@@ -478,7 +471,7 @@ int Book::find_key(uint64_t key) {
assert(left == right); assert(left == right);
read_entry(entry, left); read_entry(entry, left);
return entry.key == key ? left : bookSize; return (entry.key == key)? left : bookSize;
} }
@@ -492,13 +485,11 @@ void Book::read_entry(BookEntry& entry, int idx) {
assert(is_open()); assert(is_open());
seekg(idx * EntrySize, ios_base::beg); seekg(idx * EntrySize, ios_base::beg);
*this >> entry; *this >> entry;
if (!good()) if (!good())
{ {
cerr << "Failed to read book entry at index " << idx << endl; cerr << "Failed to read book entry at index " << idx << endl;
exit(EXIT_FAILURE); Application::exit_with_failure();
} }
} }
@@ -509,11 +500,10 @@ void Book::read_entry(BookEntry& entry, int idx) {
uint64_t Book::read_integer(int size) { uint64_t Book::read_integer(int size) {
char buf[8]; char buf[8];
uint64_t n = 0;
read(buf, size); read(buf, size);
// Numbers are stored on disk as a binary byte stream // Numbers are stored on disk as a binary byte stream
uint64_t n = 0ULL;
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
n = (n << 8) + (unsigned char)buf[i]; n = (n << 8) + (unsigned char)buf[i];
@@ -529,15 +519,23 @@ namespace {
uint64_t book_key(const Position& pos) { uint64_t book_key(const Position& pos) {
uint64_t result = 0; uint64_t result = 0ULL;
Bitboard b = pos.occupied_squares();
for (Color c = WHITE; c <= BLACK; c++)
{
Bitboard b = pos.pieces_of_color(c);
while (b) while (b)
{ {
Square s = pop_1st_bit(&b); Square s = pop_1st_bit(&b);
result ^= book_piece_key(pos.piece_on(s), s); 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_castle_key(pos); result ^= book_castle_key(pos);
result ^= book_ep_key(pos); result ^= book_ep_key(pos);
result ^= book_color_key(pos); result ^= book_color_key(pos);
@@ -547,41 +545,39 @@ namespace {
uint64_t book_piece_key(Piece p, Square s) { 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 }; static const int PieceTo12[] = { 0, 0, 2, 4, 6, 8, 10, 0, 0, 1, 3, 5, 7, 9, 11 };
return Random64[PieceIdx + (PieceTo12[int(p)]^1) * 64 + int(s)]; return Random64[RandomPiece + (PieceTo12[int(p)]^1) * 64 + int(s)];
} }
uint64_t book_castle_key(const Position& pos) { uint64_t book_castle_key(const Position& pos) {
uint64_t result = 0; uint64_t result = 0ULL;
if (pos.can_castle_kingside(WHITE)) if (pos.can_castle_kingside(WHITE))
result ^= Random64[CastleIdx + 0]; result ^= Random64[RandomCastle+0];
if (pos.can_castle_queenside(WHITE)) if (pos.can_castle_queenside(WHITE))
result ^= Random64[CastleIdx + 1]; result ^= Random64[RandomCastle+1];
if (pos.can_castle_kingside(BLACK)) if (pos.can_castle_kingside(BLACK))
result ^= Random64[CastleIdx + 2]; result ^= Random64[RandomCastle+2];
if (pos.can_castle_queenside(BLACK)) if (pos.can_castle_queenside(BLACK))
result ^= Random64[CastleIdx + 3]; result ^= Random64[RandomCastle+3];
return result; return result;
} }
uint64_t book_ep_key(const Position& pos) { 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) { 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;
} }
} }
+11 -8
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -38,7 +38,6 @@
#include "move.h" #include "move.h"
#include "position.h" #include "position.h"
#include "rkiss.h"
//// ////
@@ -54,15 +53,12 @@ struct BookEntry {
}; };
class Book : private std::ifstream { class Book : private std::ifstream {
Book(const Book&); // just decleared..
Book& operator=(const Book&); // ..to avoid a warning
public: public:
Book();
~Book(); ~Book();
void open(const std::string& fName); void open(const std::string& fName);
void close(); void close();
const std::string file_name(); const std::string file_name() const;
Move get_move(const Position& pos, bool findBestMove); Move get_move(const Position& pos);
private: private:
Book& operator>>(uint64_t& n) { n = read_integer(8); return *this; } Book& operator>>(uint64_t& n) { n = read_integer(8); return *this; }
@@ -75,7 +71,14 @@ private:
std::string fileName; std::string fileName;
int bookSize; int bookSize;
RKISS RKiss;
}; };
////
//// Global variables
////
extern Book OpeningBook;
#endif // !defined(BOOK_H_INCLUDED) #endif // !defined(BOOK_H_INCLUDED)
+3 -9
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,7 +21,6 @@
#if !defined(COLOR_H_INCLUDED) #if !defined(COLOR_H_INCLUDED)
#define COLOR_H_INCLUDED #define COLOR_H_INCLUDED
#include "types.h"
//// ////
//// Types //// Types
@@ -33,18 +32,13 @@ enum Color {
COLOR_NONE COLOR_NONE
}; };
enum SquareColor {
DARK,
LIGHT
};
ENABLE_OPERATORS_ON(Color)
//// ////
//// Inline functions //// Inline functions
//// ////
inline void operator++ (Color &c, int) { c = Color(int(c) + 1); }
inline Color opposite_color(Color c) { inline Color opposite_color(Color c) {
return Color(int(c) ^ 1); return Color(int(c) ^ 1);
} }
+28 -12
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,23 +21,39 @@
#if !defined(DEPTH_H_INCLUDED) #if !defined(DEPTH_H_INCLUDED)
#define DEPTH_H_INCLUDED #define DEPTH_H_INCLUDED
#include "types.h"
//// ////
//// Types //// Types
//// ////
enum Depth { enum Depth {
DEPTH_ZERO = 0,
ONE_PLY = 2, DEPTH_MAX = 200 // 100 * OnePly;
DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_NONE = -127 * ONE_PLY
}; };
ENABLE_OPERATORS_ON(Depth)
////
//// Constants
////
const Depth OnePly = Depth(2);
////
//// Inline functions
////
inline Depth operator+ (Depth d, int i) { return Depth(int(d) + i); }
inline Depth operator+ (Depth d1, Depth d2) { return Depth(int(d1) + int(d2)); }
inline void operator+= (Depth &d, int i) { d = Depth(int(d) + i); }
inline void operator+= (Depth &d1, Depth d2) { d1 += int(d2); }
inline Depth operator- (Depth d, int i) { return Depth(int(d) - i); }
inline Depth operator- (Depth d1, Depth d2) { return Depth(int(d1) - int(d2)); }
inline void operator-= (Depth & d, int i) { d = Depth(int(d) - i); }
inline Depth operator* (Depth d, int i) { return Depth(int(d) * i); }
inline Depth operator* (int i, Depth d) { return Depth(int(d) * i); }
inline void operator*= (Depth &d, int i) { d = Depth(int(d) * i); }
inline Depth operator/ (Depth d, int i) { return Depth(int(d) / i); }
inline void operator/= (Depth &d, int i) { d = Depth(int(d) / i); }
#endif // !defined(DEPTH_H_INCLUDED) #endif // !defined(DEPTH_H_INCLUDED)
+87
View File
@@ -0,0 +1,87 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2009 Marco Costalba
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;
}
}
}
}
+92
View File
@@ -0,0 +1,92 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2009 Marco Costalba
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
};
////
//// Variables
////
extern uint8_t DirectionTable[64][64];
extern uint8_t SignedDirectionTable[64][64];
////
//// Inline functions
////
inline void operator++ (Direction& d, int) {
d = Direction(int(d) + 1);
}
inline void operator++ (SignedDirection& d, int) {
d = SignedDirection(int(d) + 1);
}
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)
+116 -114
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -24,6 +24,7 @@
#include <cassert> #include <cassert>
#include "bitbase.h"
#include "bitcount.h" #include "bitcount.h"
#include "endgame.h" #include "endgame.h"
@@ -64,13 +65,13 @@ namespace {
// the two kings in basic endgames. // the two kings in basic endgames.
const int DistanceBonus[8] = { 0, 0, 100, 80, 60, 40, 20, 10 }; const int DistanceBonus[8] = { 0, 0, 100, 80, 60, 40, 20, 10 };
// Bitbase for KP vs K
uint8_t KPKBitbase[24576];
// Penalty for big distance between king and knight for the defending king // Penalty for big distance between king and knight for the defending king
// and knight in KR vs KN endgames. // and knight in KR vs KN endgames.
const int KRKNKingKnightDistancePenalty[8] = { 0, 0, 4, 10, 20, 32, 48, 70 }; const int KRKNKingKnightDistancePenalty[8] = { 0, 0, 4, 10, 20, 32, 48, 70 };
// Bitbase for KP vs K
uint8_t KPKBitbase[24576];
// Various inline functions for accessing the above arrays // Various inline functions for accessing the above arrays
inline Value mate_table(Square s) { inline Value mate_table(Square s) {
return Value(MateTable[s]); return Value(MateTable[s]);
@@ -98,25 +99,15 @@ namespace {
//// Functions //// Functions
//// ////
/// 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);
}
/// Mate with KX vs K. This function is used to evaluate positions with /// Mate with KX vs K. This function is used to evaluate positions with
/// King and plenty of material vs a lone king. It simply gives the /// King and plenty of material vs a lone king. It simply gives the
/// attacking side a bonus for driving the defending king towards the edge /// attacking side a bonus for driving the defending king towards the edge
/// of the board, and for keeping the distance between the two kings small. /// of the board, and for keeping the distance between the two kings small.
template<> template<>
Value EvaluationFunction<KXK>::apply(const Position& pos) const { Value EvaluationFunction<KXK>::apply(const Position& pos) {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide) == Value(0));
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO); assert(pos.piece_count(weakerSide, PAWN) == Value(0));
Square winnerKSq = pos.king_square(strongerSide); Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide); Square loserKSq = pos.king_square(weakerSide);
@@ -126,23 +117,23 @@ Value EvaluationFunction<KXK>::apply(const Position& pos) const {
+ mate_table(loserKSq) + mate_table(loserKSq)
+ distance_bonus(square_distance(winnerKSq, loserKSq)); + distance_bonus(square_distance(winnerKSq, loserKSq));
if ( pos.piece_count(strongerSide, QUEEN) if ( pos.piece_count(strongerSide, QUEEN) > 0
|| pos.piece_count(strongerSide, ROOK) || pos.piece_count(strongerSide, ROOK) > 0
|| pos.piece_count(strongerSide, BISHOP) > 1) || pos.piece_count(strongerSide, BISHOP) > 1)
// TODO: check for two equal-colored bishops! // TODO: check for two equal-colored bishops!
result += VALUE_KNOWN_WIN; result += VALUE_KNOWN_WIN;
return strongerSide == pos.side_to_move() ? result : -result; return (strongerSide == pos.side_to_move() ? result : -result);
} }
/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the /// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the
/// defending king towards a corner square of the right color. /// defending king towards a corner square of the right color.
template<> template<>
Value EvaluationFunction<KBNK>::apply(const Position& pos) const { Value EvaluationFunction<KBNK>::apply(const Position& pos) {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide) == Value(0));
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO); assert(pos.piece_count(weakerSide, PAWN) == Value(0));
assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame + BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame + BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, KNIGHT) == 1); assert(pos.piece_count(strongerSide, KNIGHT) == 1);
@@ -152,10 +143,7 @@ Value EvaluationFunction<KBNK>::apply(const Position& pos) const {
Square loserKSq = pos.king_square(weakerSide); Square loserKSq = pos.king_square(weakerSide);
Square bishopSquare = pos.piece_list(strongerSide, BISHOP, 0); Square bishopSquare = pos.piece_list(strongerSide, BISHOP, 0);
// kbnk_mate_table() tries to drive toward corners A1 or H8, if (square_color(bishopSquare) == BLACK)
// if we have a bishop that cannot reach the above squares we
// mirror the kings so to drive enemy toward corners A8 or H1.
if (!same_color_squares(bishopSquare, SQ_A1))
{ {
winnerKSq = flop_square(winnerKSq); winnerKSq = flop_square(winnerKSq);
loserKSq = flop_square(loserKSq); loserKSq = flop_square(loserKSq);
@@ -165,16 +153,16 @@ Value EvaluationFunction<KBNK>::apply(const Position& pos) const {
+ distance_bonus(square_distance(winnerKSq, loserKSq)) + distance_bonus(square_distance(winnerKSq, loserKSq))
+ kbnk_mate_table(loserKSq); + kbnk_mate_table(loserKSq);
return strongerSide == pos.side_to_move() ? result : -result; return (strongerSide == pos.side_to_move() ? result : -result);
} }
/// KP vs K. This endgame is evaluated with the help of a bitbase. /// KP vs K. This endgame is evaluated with the help of a bitbase.
template<> template<>
Value EvaluationFunction<KPK>::apply(const Position& pos) const { Value EvaluationFunction<KPK>::apply(const Position& pos) {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); assert(pos.non_pawn_material(strongerSide) == Value(0));
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide) == Value(0));
assert(pos.piece_count(strongerSide, PAWN) == 1); assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, PAWN) == 0);
@@ -210,7 +198,7 @@ Value EvaluationFunction<KPK>::apply(const Position& pos) const {
+ PawnValueEndgame + PawnValueEndgame
+ Value(square_rank(wpsq)); + Value(square_rank(wpsq));
return strongerSide == pos.side_to_move() ? result : -result; return (strongerSide == pos.side_to_move() ? result : -result);
} }
@@ -219,7 +207,7 @@ Value EvaluationFunction<KPK>::apply(const Position& pos) const {
/// far advanced with support of the king, while the attacking king is far /// far advanced with support of the king, while the attacking king is far
/// away. /// away.
template<> template<>
Value EvaluationFunction<KRKP>::apply(const Position& pos) const { Value EvaluationFunction<KRKP>::apply(const Position& pos) {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -269,14 +257,14 @@ Value EvaluationFunction<KRKP>::apply(const Position& pos) const {
+ Value(square_distance(bksq, bpsq + DELTA_S) * 8) + Value(square_distance(bksq, bpsq + DELTA_S) * 8)
+ Value(square_distance(bpsq, queeningSq) * 8); + Value(square_distance(bpsq, queeningSq) * 8);
return strongerSide == pos.side_to_move() ? result : -result; return (strongerSide == pos.side_to_move() ? result : -result);
} }
/// KR vs KB. This is very simple, and always returns drawish scores. The /// KR vs KB. This is very simple, and always returns drawish scores. The
/// score is slightly bigger when the defending king is close to the edge. /// score is slightly bigger when the defending king is close to the edge.
template<> template<>
Value EvaluationFunction<KRKB>::apply(const Position& pos) const { Value EvaluationFunction<KRKB>::apply(const Position& pos) {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -285,14 +273,14 @@ Value EvaluationFunction<KRKB>::apply(const Position& pos) const {
assert(pos.piece_count(weakerSide, BISHOP) == 1); assert(pos.piece_count(weakerSide, BISHOP) == 1);
Value result = mate_table(pos.king_square(weakerSide)); Value result = mate_table(pos.king_square(weakerSide));
return strongerSide == pos.side_to_move() ? result : -result; return (pos.side_to_move() == strongerSide ? result : -result);
} }
/// KR vs KN. The attacking side has slightly better winning chances than /// KR vs KN. The attacking side has slightly better winning chances than
/// in KR vs KB, particularly if the king and the knight are far apart. /// in KR vs KB, particularly if the king and the knight are far apart.
template<> template<>
Value EvaluationFunction<KRKN>::apply(const Position& pos) const { Value EvaluationFunction<KRKN>::apply(const Position& pos) {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -303,12 +291,10 @@ Value EvaluationFunction<KRKN>::apply(const Position& pos) const {
Square defendingKSq = pos.king_square(weakerSide); Square defendingKSq = pos.king_square(weakerSide);
Square nSq = pos.piece_list(weakerSide, KNIGHT, 0); Square nSq = pos.piece_list(weakerSide, KNIGHT, 0);
int d = square_distance(defendingKSq, nSq); Value result = Value(10) + mate_table(defendingKSq) +
Value result = Value(10) krkn_king_knight_distance_penalty(square_distance(defendingKSq, nSq));
+ mate_table(defendingKSq)
+ krkn_king_knight_distance_penalty(d);
return strongerSide == pos.side_to_move() ? result : -result; return (strongerSide == pos.side_to_move())? result : -result;
} }
@@ -318,7 +304,7 @@ Value EvaluationFunction<KRKN>::apply(const Position& pos) const {
/// for the defending side in the search, this is usually sufficient to be /// for the defending side in the search, this is usually sufficient to be
/// able to win KQ vs KR. /// able to win KQ vs KR.
template<> template<>
Value EvaluationFunction<KQKR>::apply(const Position& pos) const { Value EvaluationFunction<KQKR>::apply(const Position& pos) {
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame); assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -333,11 +319,11 @@ Value EvaluationFunction<KQKR>::apply(const Position& pos) const {
+ mate_table(loserKSq) + mate_table(loserKSq)
+ distance_bonus(square_distance(winnerKSq, loserKSq)); + distance_bonus(square_distance(winnerKSq, loserKSq));
return strongerSide == pos.side_to_move() ? result : -result; return (strongerSide == pos.side_to_move())? result : -result;
} }
template<> template<>
Value EvaluationFunction<KBBKN>::apply(const Position& pos) const { Value EvaluationFunction<KBBKN>::apply(const Position& pos) {
assert(pos.piece_count(strongerSide, BISHOP) == 2); assert(pos.piece_count(strongerSide, BISHOP) == 2);
assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMidgame);
@@ -357,31 +343,31 @@ Value EvaluationFunction<KBBKN>::apply(const Position& pos) const {
result += Value(square_distance(bksq, nsq) * 32); result += Value(square_distance(bksq, nsq) * 32);
// Bonus for restricting the knight's mobility // Bonus for restricting the knight's mobility
result += Value((8 - count_1s<CNT32_MAX15>(pos.attacks_from<KNIGHT>(nsq))) * 8); result += Value((8 - count_1s_max_15(pos.attacks_from<KNIGHT>(nsq))) * 8);
return strongerSide == pos.side_to_move() ? result : -result; return (strongerSide == pos.side_to_move() ? result : -result);
} }
/// K and two minors vs K and one or two minors or K and two knights against /// K and two minors vs K and one or two minors or K and two knights against
/// king alone are always draw. /// king alone are always draw.
template<> template<>
Value EvaluationFunction<KmmKm>::apply(const Position&) const { Value EvaluationFunction<KmmKm>::apply(const Position&) {
return VALUE_DRAW; return Value(0);
} }
template<> template<>
Value EvaluationFunction<KNNK>::apply(const Position&) const { Value EvaluationFunction<KNNK>::apply(const Position&) {
return VALUE_DRAW; return Value(0);
} }
/// KBPKScalingFunction scales endgames where the stronger side has king, /// KBPKScalingFunction scales endgames where the stronger side has king,
/// bishop and one or more pawns. It checks for draws with rook pawns and a /// bishop and one or more pawns. It checks for draws with rook pawns and a
/// bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_ZERO is /// bishop of the wrong color. If such a draw is detected, ScaleFactor(0) is
/// returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling /// returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
/// will be used. /// will be used.
template<> template<>
ScaleFactor ScalingFunction<KBPsK>::apply(const Position& pos) const { ScaleFactor ScalingFunction<KBPsK>::apply(const Position& pos) {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -401,7 +387,7 @@ ScaleFactor ScalingFunction<KBPsK>::apply(const Position& pos) const {
Square queeningSq = relative_square(strongerSide, make_square(pawnFile, RANK_8)); Square queeningSq = relative_square(strongerSide, make_square(pawnFile, RANK_8));
Square kingSq = pos.king_square(weakerSide); Square kingSq = pos.king_square(weakerSide);
if ( !same_color_squares(queeningSq, bishopSq) if ( square_color(queeningSq) != square_color(bishopSq)
&& file_distance(square_file(kingSq), pawnFile) <= 1) && file_distance(square_file(kingSq), pawnFile) <= 1)
{ {
// The bishop has the wrong color, and the defending king is on the // The bishop has the wrong color, and the defending king is on the
@@ -423,7 +409,7 @@ ScaleFactor ScalingFunction<KBPsK>::apply(const Position& pos) const {
// is placed somewhere in front of the pawn, it's a draw. // is placed somewhere in front of the pawn, it's a draw.
if ( square_distance(kingSq, queeningSq) <= 1 if ( square_distance(kingSq, queeningSq) <= 1
|| relative_rank(strongerSide, kingSq) >= rank) || relative_rank(strongerSide, kingSq) >= rank)
return SCALE_FACTOR_ZERO; return ScaleFactor(0);
} }
} }
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@@ -435,7 +421,7 @@ ScaleFactor ScalingFunction<KBPsK>::apply(const Position& pos) const {
/// It tests for fortress draws with a rook on the third rank defended by /// It tests for fortress draws with a rook on the third rank defended by
/// a pawn. /// a pawn.
template<> template<>
ScaleFactor ScalingFunction<KQKRPs>::apply(const Position& pos) const { ScaleFactor ScalingFunction<KQKRPs>::apply(const Position& pos) {
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame); assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
assert(pos.piece_count(strongerSide, QUEEN) == 1); assert(pos.piece_count(strongerSide, QUEEN) == 1);
@@ -452,7 +438,7 @@ ScaleFactor ScalingFunction<KQKRPs>::apply(const Position& pos) const {
{ {
Square rsq = pos.piece_list(weakerSide, ROOK, 0); Square rsq = pos.piece_list(weakerSide, ROOK, 0);
if (pos.attacks_from<PAWN>(rsq, strongerSide) & pos.pieces(PAWN, weakerSide)) if (pos.attacks_from<PAWN>(rsq, strongerSide) & pos.pieces(PAWN, weakerSide))
return SCALE_FACTOR_ZERO; return ScaleFactor(0);
} }
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
@@ -466,7 +452,7 @@ ScaleFactor ScalingFunction<KQKRPs>::apply(const Position& pos) const {
/// It would also be nice to rewrite the actual code for this function, /// It would also be nice to rewrite the actual code for this function,
/// which is mostly copied from Glaurung 1.x, and not very pretty. /// which is mostly copied from Glaurung 1.x, and not very pretty.
template<> template<>
ScaleFactor ScalingFunction<KRPKR>::apply(const Position& pos) const { ScaleFactor ScalingFunction<KRPKR>::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 1); assert(pos.piece_count(strongerSide, PAWN) == 1);
@@ -509,7 +495,7 @@ ScaleFactor ScalingFunction<KRPKR>::apply(const Position& pos) const {
&& square_distance(bksq, queeningSq) <= 1 && square_distance(bksq, queeningSq) <= 1
&& wksq <= SQ_H5 && wksq <= SQ_H5
&& (square_rank(brsq) == RANK_6 || (r <= RANK_3 && square_rank(wrsq) != RANK_6))) && (square_rank(brsq) == RANK_6 || (r <= RANK_3 && square_rank(wrsq) != RANK_6)))
return SCALE_FACTOR_ZERO; return ScaleFactor(0);
// The defending side saves a draw by checking from behind in case the pawn // The defending side saves a draw by checking from behind in case the pawn
// has advanced to the 6th rank with the king behind. // has advanced to the 6th rank with the king behind.
@@ -517,13 +503,13 @@ ScaleFactor ScalingFunction<KRPKR>::apply(const Position& pos) const {
&& square_distance(bksq, queeningSq) <= 1 && square_distance(bksq, queeningSq) <= 1
&& square_rank(wksq) + tempo <= RANK_6 && square_rank(wksq) + tempo <= RANK_6
&& (square_rank(brsq) == RANK_1 || (!tempo && abs(square_file(brsq) - f) >= 3))) && (square_rank(brsq) == RANK_1 || (!tempo && abs(square_file(brsq) - f) >= 3)))
return SCALE_FACTOR_ZERO; return ScaleFactor(0);
if ( r >= RANK_6 if ( r >= RANK_6
&& bksq == queeningSq && bksq == queeningSq
&& square_rank(brsq) == RANK_1 && square_rank(brsq) == RANK_1
&& (!tempo || square_distance(wksq, wpsq) >= 2)) && (!tempo || square_distance(wksq, wpsq) >= 2))
return SCALE_FACTOR_ZERO; return ScaleFactor(0);
// White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7 // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7
// and the black rook is behind the pawn. // and the black rook is behind the pawn.
@@ -532,7 +518,7 @@ ScaleFactor ScalingFunction<KRPKR>::apply(const Position& pos) const {
&& (bksq == SQ_H7 || bksq == SQ_G7) && (bksq == SQ_H7 || bksq == SQ_G7)
&& square_file(brsq) == FILE_A && square_file(brsq) == FILE_A
&& (square_rank(brsq) <= RANK_3 || square_file(wksq) >= FILE_D || square_rank(wksq) <= RANK_5)) && (square_rank(brsq) <= RANK_3 || square_file(wksq) >= FILE_D || square_rank(wksq) <= RANK_5))
return SCALE_FACTOR_ZERO; return ScaleFactor(0);
// If the defending king blocks the pawn and the attacking king is too far // If the defending king blocks the pawn and the attacking king is too far
// away, it's a draw. // away, it's a draw.
@@ -540,7 +526,7 @@ ScaleFactor ScalingFunction<KRPKR>::apply(const Position& pos) const {
&& bksq == wpsq + DELTA_N && bksq == wpsq + DELTA_N
&& square_distance(wksq, wpsq) - tempo >= 2 && square_distance(wksq, wpsq) - tempo >= 2
&& square_distance(wksq, brsq) - tempo >= 2) && square_distance(wksq, brsq) - tempo >= 2)
return SCALE_FACTOR_ZERO; return ScaleFactor(0);
// Pawn on the 7th rank supported by the rook from behind usually wins if the // Pawn on the 7th rank supported by the rook from behind usually wins if the
// attacking king is closer to the queening square than the defending king, // attacking king is closer to the queening square than the defending king,
@@ -563,8 +549,8 @@ ScaleFactor ScalingFunction<KRPKR>::apply(const Position& pos) const {
|| ( square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo || ( square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo
&& (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wrsq) + tempo)))) && (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wrsq) + tempo))))
return ScaleFactor( SCALE_FACTOR_MAX return ScaleFactor( SCALE_FACTOR_MAX
- 8 * square_distance(wpsq, queeningSq) - (8 * square_distance(wpsq, queeningSq)
- 2 * square_distance(wksq, queeningSq)); + 2 * square_distance(wksq, queeningSq)));
// If the pawn is not far advanced, and the defending king is somewhere in // If the pawn is not far advanced, and the defending king is somewhere in
// the pawn's path, it's probably a draw. // the pawn's path, it's probably a draw.
@@ -584,7 +570,7 @@ ScaleFactor ScalingFunction<KRPKR>::apply(const Position& pos) const {
/// single pattern: If the stronger side has no pawns and the defending king /// single pattern: If the stronger side has no pawns and the defending king
/// is actively placed, the position is drawish. /// is actively placed, the position is drawish.
template<> template<>
ScaleFactor ScalingFunction<KRPPKRP>::apply(const Position& pos) const { ScaleFactor ScalingFunction<KRPPKRP>::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 2); assert(pos.piece_count(strongerSide, PAWN) == 2);
@@ -623,34 +609,42 @@ ScaleFactor ScalingFunction<KRPPKRP>::apply(const Position& pos) const {
/// against king. There is just a single rule here: If all pawns are on /// against king. There is just a single rule here: If all pawns are on
/// the same rook file and are blocked by the defending king, it's a draw. /// the same rook file and are blocked by the defending king, it's a draw.
template<> template<>
ScaleFactor ScalingFunction<KPsK>::apply(const Position& pos) const { ScaleFactor ScalingFunction<KPsK>::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); assert(pos.non_pawn_material(strongerSide) == Value(0));
assert(pos.piece_count(strongerSide, PAWN) >= 2); assert(pos.piece_count(strongerSide, PAWN) >= 2);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide) == Value(0));
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, PAWN) == 0);
Square ksq = pos.king_square(weakerSide);
Bitboard pawns = pos.pieces(PAWN, strongerSide); Bitboard pawns = pos.pieces(PAWN, strongerSide);
// Are all pawns on the 'a' file? // Are all pawns on the 'a' file?
if ((pawns & ~FileABB) == EmptyBoardBB) if ((pawns & ~FileABB) == EmptyBoardBB)
{ {
// Does the defending king block the pawns? // Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1 Square ksq = pos.king_square(weakerSide);
|| ( square_file(ksq) == FILE_A if (square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1)
&& (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB)) return ScaleFactor(0);
return SCALE_FACTOR_ZERO; else if( square_file(ksq) == FILE_A
&& (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB)
return ScaleFactor(0);
else
return SCALE_FACTOR_NONE;
} }
// Are all pawns on the 'h' file? // Are all pawns on the 'h' file?
else if ((pawns & ~FileHBB) == EmptyBoardBB) else if ((pawns & ~FileHBB) == EmptyBoardBB)
{ {
// Does the defending king block the pawns? // Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1 Square ksq = pos.king_square(weakerSide);
|| ( square_file(ksq) == FILE_H if (square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1)
&& (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB)) return ScaleFactor(0);
return SCALE_FACTOR_ZERO; else if ( square_file(ksq) == FILE_H
&& (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB)
return ScaleFactor(0);
else
return SCALE_FACTOR_NONE;
} }
else
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
@@ -661,7 +655,7 @@ ScaleFactor ScalingFunction<KPsK>::apply(const Position& pos) const {
/// it's a draw. If the two bishops have opposite color, it's almost always /// it's a draw. If the two bishops have opposite color, it's almost always
/// a draw. /// a draw.
template<> template<>
ScaleFactor ScalingFunction<KBPKB>::apply(const Position& pos) const { ScaleFactor ScalingFunction<KBPKB>::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -678,12 +672,12 @@ ScaleFactor ScalingFunction<KBPKB>::apply(const Position& pos) const {
// Case 1: Defending king blocks the pawn, and cannot be driven away // Case 1: Defending king blocks the pawn, and cannot be driven away
if ( square_file(weakerKingSq) == square_file(pawnSq) if ( square_file(weakerKingSq) == square_file(pawnSq)
&& relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq) && relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq)
&& ( !same_color_squares(weakerKingSq, strongerBishopSq) && ( square_color(weakerKingSq) != square_color(strongerBishopSq)
|| relative_rank(strongerSide, weakerKingSq) <= RANK_6)) || relative_rank(strongerSide, weakerKingSq) <= RANK_6))
return SCALE_FACTOR_ZERO; return ScaleFactor(0);
// Case 2: Opposite colored bishops // Case 2: Opposite colored bishops
if (!same_color_squares(strongerBishopSq, weakerBishopSq)) if (square_color(strongerBishopSq) != square_color(weakerBishopSq))
{ {
// We assume that the position is drawn in the following three situations: // We assume that the position is drawn in the following three situations:
// //
@@ -696,17 +690,15 @@ ScaleFactor ScalingFunction<KBPKB>::apply(const Position& pos) const {
// reasonably well. // reasonably well.
if (relative_rank(strongerSide, pawnSq) <= RANK_5) if (relative_rank(strongerSide, pawnSq) <= RANK_5)
return SCALE_FACTOR_ZERO; return ScaleFactor(0);
else else
{ {
Bitboard path = squares_in_front_of(strongerSide, pawnSq); Bitboard ray = ray_bb(pawnSq, (strongerSide == WHITE)? SIGNED_DIR_N : SIGNED_DIR_S);
if (ray & pos.pieces(KING, weakerSide))
if (path & pos.pieces(KING, weakerSide)) return ScaleFactor(0);
return SCALE_FACTOR_ZERO; if( (pos.attacks_from<BISHOP>(weakerBishopSq) & ray)
if ( (pos.attacks_from<BISHOP>(weakerBishopSq) & path)
&& square_distance(weakerBishopSq, pawnSq) >= 3) && square_distance(weakerBishopSq, pawnSq) >= 3)
return SCALE_FACTOR_ZERO; return ScaleFactor(0);
} }
} }
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@@ -716,7 +708,7 @@ ScaleFactor ScalingFunction<KBPKB>::apply(const Position& pos) const {
/// KBPPKBScalingFunction scales KBPP vs KB endgames. It detects a few basic /// KBPPKBScalingFunction scales KBPP vs KB endgames. It detects a few basic
/// draws with opposite-colored bishops. /// draws with opposite-colored bishops.
template<> template<>
ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) const { ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -728,7 +720,7 @@ ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) const {
Square wbsq = pos.piece_list(strongerSide, BISHOP, 0); Square wbsq = pos.piece_list(strongerSide, BISHOP, 0);
Square bbsq = pos.piece_list(weakerSide, BISHOP, 0); Square bbsq = pos.piece_list(weakerSide, BISHOP, 0);
if (same_color_squares(wbsq, bbsq)) if (square_color(wbsq) == square_color(bbsq))
// Not opposite-colored bishops, no scaling // Not opposite-colored bishops, no scaling
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@@ -757,8 +749,8 @@ ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) const {
// some square in the frontmost pawn's path. // some square in the frontmost pawn's path.
if ( square_file(ksq) == square_file(blockSq1) if ( square_file(ksq) == square_file(blockSq1)
&& relative_rank(strongerSide, ksq) >= relative_rank(strongerSide, blockSq1) && relative_rank(strongerSide, ksq) >= relative_rank(strongerSide, blockSq1)
&& !same_color_squares(ksq, wbsq)) && square_color(ksq) != square_color(wbsq))
return SCALE_FACTOR_ZERO; return ScaleFactor(0);
else else
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@@ -767,17 +759,16 @@ ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) const {
// in front of the frontmost pawn's path, and the square diagonally behind // in front of the frontmost pawn's path, and the square diagonally behind
// this square on the file of the other pawn. // this square on the file of the other pawn.
if ( ksq == blockSq1 if ( ksq == blockSq1
&& !same_color_squares(ksq, wbsq) && square_color(ksq) != square_color(wbsq)
&& ( bbsq == blockSq2 && ( bbsq == blockSq2
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(BISHOP, weakerSide)) || (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(BISHOP, weakerSide))
|| rank_distance(r1, r2) >= 2)) || rank_distance(r1, r2) >= 2))
return SCALE_FACTOR_ZERO; return ScaleFactor(0);
else if ( ksq == blockSq2 else if ( ksq == blockSq2
&& !same_color_squares(ksq, wbsq) && square_color(ksq) != square_color(wbsq)
&& ( bbsq == blockSq1 && ( bbsq == blockSq1
|| (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(BISHOP, weakerSide)))) || (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(BISHOP, weakerSide))))
return SCALE_FACTOR_ZERO; return ScaleFactor(0);
else else
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@@ -793,7 +784,7 @@ ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) const {
/// square of the king is not of the same color as the stronger side's bishop, /// square of the king is not of the same color as the stronger side's bishop,
/// it's a draw. /// it's a draw.
template<> template<>
ScaleFactor ScalingFunction<KBPKN>::apply(const Position& pos) const { ScaleFactor ScalingFunction<KBPKN>::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -808,9 +799,9 @@ ScaleFactor ScalingFunction<KBPKN>::apply(const Position& pos) const {
if ( square_file(weakerKingSq) == square_file(pawnSq) if ( square_file(weakerKingSq) == square_file(pawnSq)
&& relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq) && relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq)
&& ( !same_color_squares(weakerKingSq, strongerBishopSq) && ( square_color(weakerKingSq) != square_color(strongerBishopSq)
|| relative_rank(strongerSide, weakerKingSq) <= RANK_6)) || relative_rank(strongerSide, weakerKingSq) <= RANK_6))
return SCALE_FACTOR_ZERO; return ScaleFactor(0);
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
@@ -820,12 +811,12 @@ ScaleFactor ScalingFunction<KBPKN>::apply(const Position& pos) const {
/// If the pawn is a rook pawn on the 7th rank and the defending king prevents /// If the pawn is a rook pawn on the 7th rank and the defending king prevents
/// the pawn from advancing, the position is drawn. /// the pawn from advancing, the position is drawn.
template<> template<>
ScaleFactor ScalingFunction<KNPK>::apply(const Position& pos) const { ScaleFactor ScalingFunction<KNPK>::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame); assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame);
assert(pos.piece_count(strongerSide, KNIGHT) == 1); assert(pos.piece_count(strongerSide, KNIGHT) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 1); assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide) == Value(0));
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, PAWN) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN, 0); Square pawnSq = pos.piece_list(strongerSide, PAWN, 0);
@@ -833,11 +824,11 @@ ScaleFactor ScalingFunction<KNPK>::apply(const Position& pos) const {
if ( pawnSq == relative_square(strongerSide, SQ_A7) if ( pawnSq == relative_square(strongerSide, SQ_A7)
&& square_distance(weakerKingSq, relative_square(strongerSide, SQ_A8)) <= 1) && square_distance(weakerKingSq, relative_square(strongerSide, SQ_A8)) <= 1)
return SCALE_FACTOR_ZERO; return ScaleFactor(0);
if ( pawnSq == relative_square(strongerSide, SQ_H7) if ( pawnSq == relative_square(strongerSide, SQ_H7)
&& square_distance(weakerKingSq, relative_square(strongerSide, SQ_H8)) <= 1) && square_distance(weakerKingSq, relative_square(strongerSide, SQ_H8)) <= 1)
return SCALE_FACTOR_ZERO; return ScaleFactor(0);
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
@@ -850,10 +841,10 @@ ScaleFactor ScalingFunction<KNPK>::apply(const Position& pos) const {
/// advanced and not on a rook file; in this case it is often possible to win /// advanced and not on a rook file; in this case it is often possible to win
/// (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1). /// (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1).
template<> template<>
ScaleFactor ScalingFunction<KPKP>::apply(const Position& pos) const { ScaleFactor ScalingFunction<KPKP>::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); assert(pos.non_pawn_material(strongerSide) == Value(0));
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide) == Value(0));
assert(pos.piece_count(WHITE, PAWN) == 1); assert(pos.piece_count(WHITE, PAWN) == 1);
assert(pos.piece_count(BLACK, PAWN) == 1); assert(pos.piece_count(BLACK, PAWN) == 1);
@@ -890,21 +881,32 @@ ScaleFactor ScalingFunction<KPKP>::apply(const Position& pos) const {
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a // Probe the KPK bitbase with the weakest side's pawn removed. If it's a
// draw, it's probably at least a draw even with the pawn. // draw, it's probably at least a draw even with the pawn.
return probe_kpk(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_ZERO; if (probe_kpk(wksq, wpsq, bksq, stm))
return SCALE_FACTOR_NONE;
else
return ScaleFactor(0);
}
/// 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.
void init_bitbases() {
generate_kpk_bitbase(KPKBitbase);
} }
namespace { namespace {
// Probe the KP vs K bitbase // Probe the KP vs K bitbase:
int probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm) { int probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm) {
int wp = square_file(wpsq) + 4 * (square_rank(wpsq) - 1); int wp = int(square_file(wpsq)) + (int(square_rank(wpsq)) - 1) * 4;
int index = int(stm) + 2 * bksq + 128 * wksq + 8192 * wp; int index = int(stm) + 2*int(bksq) + 128*int(wksq) + 8192*wp;
assert(index >= 0 && index < 24576*8); assert(index >= 0 && index < 24576*8);
return KPKBitbase[index/8] & (1 << (index&7)); return KPKBitbase[index/8] & (1 << (index&7));
} }
} }
+6 -4
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@
//// ////
#include "position.h" #include "position.h"
#include "scale.h"
#include "value.h" #include "value.h"
@@ -67,7 +68,7 @@ class EndgameFunctionBase {
public: public:
EndgameFunctionBase(Color c) : strongerSide(c), weakerSide(opposite_color(c)) {} EndgameFunctionBase(Color c) : strongerSide(c), weakerSide(opposite_color(c)) {}
virtual ~EndgameFunctionBase() {} virtual ~EndgameFunctionBase() {}
virtual T apply(const Position&) const = 0; virtual T apply(const Position&) = 0;
Color color() const { return strongerSide; } Color color() const { return strongerSide; }
protected: protected:
@@ -84,14 +85,14 @@ template<EndgameType>
struct EvaluationFunction : public EndgameEvaluationFunctionBase { struct EvaluationFunction : public EndgameEvaluationFunctionBase {
typedef EndgameEvaluationFunctionBase Base; typedef EndgameEvaluationFunctionBase Base;
explicit EvaluationFunction(Color c): EndgameEvaluationFunctionBase(c) {} explicit EvaluationFunction(Color c): EndgameEvaluationFunctionBase(c) {}
Value apply(const Position&) const; Value apply(const Position&);
}; };
template<EndgameType> template<EndgameType>
struct ScalingFunction : public EndgameScalingFunctionBase { struct ScalingFunction : public EndgameScalingFunctionBase {
typedef EndgameScalingFunctionBase Base; typedef EndgameScalingFunctionBase Base;
explicit ScalingFunction(Color c) : EndgameScalingFunctionBase(c) {} explicit ScalingFunction(Color c) : EndgameScalingFunctionBase(c) {}
ScaleFactor apply(const Position&) const; ScaleFactor apply(const Position&);
}; };
@@ -101,4 +102,5 @@ struct ScalingFunction : public EndgameScalingFunctionBase {
extern void init_bitbases(); extern void init_bitbases();
#endif // !defined(ENDGAME_H_INCLUDED) #endif // !defined(ENDGAME_H_INCLUDED)
+695 -590
View File
File diff suppressed because it is too large Load Diff
+84 -5
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,14 +21,93 @@
#if !defined(EVALUATE_H_INCLUDED) #if !defined(EVALUATE_H_INCLUDED)
#define EVALUATE_H_INCLUDED #define EVALUATE_H_INCLUDED
#include "color.h" ////
#include "value.h" //// Includes
////
#include <iostream>
#include "material.h"
#include "pawns.h"
////
//// Types
////
/// The EvalInfo struct contains various information computed and collected
/// by the evaluation function. An EvalInfo object is passed as one of the
/// arguments to the evaluation function, and the search can make use of its
/// contents to make intelligent search decisions.
///
/// At the moment, this is not utilized very much: The only part of the
/// EvalInfo object which is used by the search is futilityMargin.
class Position; class Position;
extern Value evaluate(const Position& pos, Value& margin); struct EvalInfo {
// Middle game and endgame evaluations
Score value;
// Pointers to material and pawn hash table entries
MaterialInfo* mi;
PawnInfo* pi;
// 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.
Bitboard attackedBy[2][8];
Bitboard attacked_by(Color c) const { return attackedBy[c][0]; }
Bitboard attacked_by(Color c, PieceType pt) const { return attackedBy[c][pt]; }
// kingZone[color] is the zone around the enemy king which is considered
// by the king safety evaluation. This consists of the squares directly
// adjacent to the king, and the three (or two, for a king on an edge file)
// squares two ranks in front of the king. For instance, if black's king
// is on g8, kingZone[WHITE] is a bitboard containing the squares f8, h8,
// f7, g7, h7, f6, g6 and h6.
Bitboard kingZone[2];
// kingAttackersCount[color] is the number of pieces of the given color
// which attack a square in the kingZone of the enemy king.
int kingAttackersCount[2];
// kingAttackersWeight[color] is the sum of the "weight" of the pieces of the
// given color which attack a square in the kingZone of the enemy king. The
// weights of the individual piece types are given by the variables
// QueenAttackWeight, RookAttackWeight, BishopAttackWeight and
// KnightAttackWeight in evaluate.cpp
int kingAttackersWeight[2];
// kingAdjacentZoneAttacksCount[color] is the number of attacks to squares
// directly adjacent to the king of the given color. Pieces which attack
// more than one square are counted multiple times. For instance, if black's
// king is on g8 and there's a white knight on g5, this knight adds
// 2 to kingAdjacentZoneAttacksCount[BLACK].
int kingAdjacentZoneAttacksCount[2];
// mateThreat[color] is a move for the given side which gives a direct mate.
Move mateThreat[2];
// Middle game and endgame mobility scores.
Score mobility;
// Extra futility margin. This is added to the standard futility margin
// in the quiescence search.
Value futilityMargin;
};
////
//// Prototypes
////
extern Value evaluate(const Position& pos, EvalInfo& ei, int threadID);
extern Value quick_evaluate(const Position& pos);
extern void init_eval(int threads); extern void init_eval(int threads);
extern void quit_eval(); extern void quit_eval();
extern void read_evaluation_uci_options(Color sideToMove); extern void read_weights(Color sideToMove);
#endif // !defined(EVALUATE_H_INCLUDED) #endif // !defined(EVALUATE_H_INCLUDED)
+12 -18
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -41,8 +41,7 @@ History::History() { clear(); }
/// History::clear() clears the history tables /// History::clear() clears the history tables
void History::clear() { void History::clear() {
memset(history, 0, 16 * 64 * sizeof(int)); memset(history, 0, 2 * 8 * 64 * sizeof(int));
memset(maxStaticValueDelta, 0, 16 * 64 * sizeof(int));
} }
@@ -62,7 +61,7 @@ void History::success(Piece p, Square to, Depth d) {
if (history[p][to] >= HistoryMax) if (history[p][to] >= HistoryMax)
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
for (int j = 0; j < 64; j++) for (int j = 0; j < 64; j++)
history[i][j] /= 2; history[i][j] /= 4;
} }
@@ -76,23 +75,18 @@ void History::failure(Piece p, Square to, Depth d) {
assert(square_is_ok(to)); assert(square_is_ok(to));
history[p][to] -= int(d) * int(d); history[p][to] -= int(d) * int(d);
if (history[p][to] < 0)
// Prevent history underflow history[p][to] = 0;
if (history[p][to] <= -HistoryMax)
for (int i = 0; i < 16; i++)
for (int j = 0; j < 64; j++)
history[i][j] /= 2;
} }
/// History::set_gain() and History::gain() store and retrieve the /// History::move_ordering_score() returns an integer value used to order the
/// gain of a move given the delta of the static position evaluations /// non-capturing moves in the MovePicker class.
/// before and after the move.
void History::set_gain(Piece p, Square to, Value delta) { int History::move_ordering_score(Piece p, Square to) const {
if (delta >= maxStaticValueDelta[p][to]) assert(piece_is_ok(p));
maxStaticValueDelta[p][to] = delta; assert(square_is_ok(to));
else
maxStaticValueDelta[p][to]--; return history[p][to];
} }
+4 -20
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -28,7 +28,6 @@
#include "depth.h" #include "depth.h"
#include "move.h" #include "move.h"
#include "piece.h" #include "piece.h"
#include "value.h"
//// ////
@@ -49,13 +48,10 @@ public:
void clear(); void clear();
void success(Piece p, Square to, Depth d); void success(Piece p, Square to, Depth d);
void failure(Piece p, Square to, Depth d); void failure(Piece p, Square to, Depth d);
int value(Piece p, Square to) const; int move_ordering_score(Piece p, Square to) const;
void set_gain(Piece p, Square to, Value delta);
Value gain(Piece p, Square to) const;
private: private:
int history[16][64]; // [piece][square] int history[16][64]; // [piece][square]
int maxStaticValueDelta[16][64]; // [piece][from_square][to_square]
}; };
@@ -65,25 +61,13 @@ private:
/// HistoryMax controls how often the history counters will be scaled down: /// HistoryMax controls how often the history counters will be scaled down:
/// When the history score for a move gets bigger than HistoryMax, all /// When the history score for a move gets bigger than HistoryMax, all
/// entries in the table are divided by 2. It is difficult to guess what /// entries in the table are divided by 4. It is difficult to guess what
/// the ideal value of this constant is. Scaling down the scores often has /// the ideal value of this constant is. Scaling down the scores often has
/// the effect that parts of the search tree which have been searched /// the effect that parts of the search tree which have been searched
/// recently have a bigger importance for move ordering than the moves which /// recently have a bigger importance for move ordering than the moves which
/// have been searched a long time ago. /// have been searched a long time ago.
const int HistoryMax = 50000 * ONE_PLY; const int HistoryMax = 25000 * OnePly;
////
//// Inline functions
////
inline int History::value(Piece p, Square to) const {
return history[p][to];
}
inline Value History::gain(Piece p, Square to) const {
return Value(maxStaticValueDelta[p][to]);
}
#endif // !defined(HISTORY_H_INCLUDED) #endif // !defined(HISTORY_H_INCLUDED)
+57 -35
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -22,21 +22,67 @@
#define LOCK_H_INCLUDED #define LOCK_H_INCLUDED
#if !defined(_MSC_VER) // x86 assembly language locks or OS spin locks may perform faster than
// mutex locks on some platforms. On my machine, mutexes seem to be the
// best.
//#define ASM_LOCK
//#define OS_SPIN_LOCK
#if defined(ASM_LOCK)
typedef volatile int Lock;
static inline void LockX86(Lock *lock) {
int dummy;
asm __volatile__("1: movl $1, %0" "\n\t"
" xchgl (%1), %0" "\n\t" " testl %0, %0" "\n\t"
" jz 3f" "\n\t" "2: pause" "\n\t"
" movl (%1), %0" "\n\t" " testl %0, %0" "\n\t"
" jnz 2b" "\n\t" " jmp 1b" "\n\t" "3:"
"\n\t":"=&q"(dummy)
:"q"(lock)
:"cc");
}
static inline void UnlockX86(Lock *lock) {
int dummy;
asm __volatile__("movl $0, (%1)":"=&q"(dummy)
:"q"(lock));
}
# define lock_init(x, y) (*(x) = 0)
# define lock_grab(x) LockX86(x)
# define lock_release(x) UnlockX86(x)
# define lock_destroy(x)
#elif defined(OS_SPIN_LOCK)
# include <libkern/OSAtomic.h>
typedef OSSpinLock Lock;
# define lock_init(x, y) (*(x) = 0)
# define lock_grab(x) OSSpinLockLock(x)
# define lock_release(x) OSSpinLockUnlock(x)
# define lock_destroy(x)
#elif !defined(_MSC_VER)
# include <pthread.h> # include <pthread.h>
typedef pthread_mutex_t Lock; typedef pthread_mutex_t Lock;
typedef pthread_cond_t WaitCondition;
# define lock_init(x) pthread_mutex_init(x, NULL) # define lock_init(x, y) pthread_mutex_init(x, y)
# define lock_grab(x) pthread_mutex_lock(x) # define lock_grab(x) pthread_mutex_lock(x)
# define lock_release(x) pthread_mutex_unlock(x) # define lock_release(x) pthread_mutex_unlock(x)
# define lock_destroy(x) pthread_mutex_destroy(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 #else
@@ -44,38 +90,14 @@ typedef pthread_cond_t WaitCondition;
#include <windows.h> #include <windows.h>
#undef WIN32_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN
// Default fast and race free locks and condition variables
#if !defined(OLD_LOCKS)
typedef SRWLOCK Lock;
typedef CONDITION_VARIABLE WaitCondition;
# define lock_init(x) InitializeSRWLock(x)
# define lock_grab(x) AcquireSRWLockExclusive(x)
# define lock_release(x) ReleaseSRWLockExclusive(x)
# define lock_destroy(x) (x)
# define cond_destroy(x) (x)
# define cond_init(x) InitializeConditionVariable(x)
# define cond_signal(x) WakeConditionVariable(x)
# define cond_wait(x,y) SleepConditionVariableSRW(x, y, INFINITE,0)
// Fallback solution to build for Windows XP and older versions, note that
// cond_wait() is racy between lock_release() and WaitForSingleObject().
#else
typedef CRITICAL_SECTION Lock; typedef CRITICAL_SECTION Lock;
typedef HANDLE WaitCondition; # define lock_init(x, y) InitializeCriticalSection(x)
# define lock_init(x) InitializeCriticalSection(x)
# define lock_grab(x) EnterCriticalSection(x) # define lock_grab(x) EnterCriticalSection(x)
# define lock_release(x) LeaveCriticalSection(x) # define lock_release(x) LeaveCriticalSection(x)
# define lock_destroy(x) DeleteCriticalSection(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 #endif
#endif // !defined(LOCK_H_INCLUDED) #endif // !defined(LOCK_H_INCLUDED)
+30 -51
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -28,16 +28,10 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include "bitboard.h" #include "benchmark.h"
#include "bitcount.h" #include "bitcount.h"
#include "endgame.h"
#include "evaluate.h"
#include "material.h"
#include "misc.h" #include "misc.h"
#include "position.h" #include "uci.h"
#include "search.h"
#include "thread.h"
#include "ucioption.h"
#ifdef USE_CALLGRIND #ifdef USE_CALLGRIND
#include <valgrind/callgrind.h> #include <valgrind/callgrind.h>
@@ -45,8 +39,6 @@
using namespace std; using namespace std;
extern bool execute_uci_command(const string& cmd);
extern void benchmark(int argc, char* argv[]);
//// ////
//// Functions //// Functions
@@ -58,53 +50,40 @@ int main(int argc, char* argv[]) {
cout.rdbuf()->pubsetbuf(NULL, 0); cout.rdbuf()->pubsetbuf(NULL, 0);
cin.rdbuf()->pubsetbuf(NULL, 0); cin.rdbuf()->pubsetbuf(NULL, 0);
// Startup initializations // Initialization through global resources manager
init_bitboards(); Application::initialize();
init_uci_options();
Position::init_zobrist();
Position::init_piece_square_tables();
init_eval(1);
init_bitbases();
init_search();
init_threads();
#ifdef USE_CALLGRIND #ifdef USE_CALLGRIND
CALLGRIND_START_INSTRUMENTATION; CALLGRIND_START_INSTRUMENTATION;
#endif #endif
if (argc <= 1) // Process command line arguments if any
if (argc > 1)
{ {
// Print copyright notice if (string(argv[1]) != "bench" || argc < 4 || argc > 8)
cout << engine_name() cout << "Usage: stockfish bench <hash size> <threads> "
<< " by Tord Romstad, Marco Costalba, Joona Kiiski" << endl; << "[time = 60s] [fen positions file = default] "
<< "[time, depth, perft or node limited = time] "
if (CpuHasPOPCNT) << "[timing file name = none]" << endl;
cout << "Good! CPU has hardware POPCNT." << endl;
// 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 > 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 else
benchmark(argc, argv); {
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);
} }
return 0;
exit_threads(); }
quit_eval();
// Print copyright notice
cout << engine_name()
<< ". By Tord Romstad, Marco Costalba, Joona Kiiski." << endl;
if (CpuHasPOPCNT)
cout << "Good! CPU has hardware POPCNT. We will use it." << endl;
// Enter UCI mode
uci_main_loop();
return 0; return 0;
} }
+94 -99
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@
//// ////
#include <cassert> #include <cassert>
#include <cstring> #include <sstream>
#include <map> #include <map>
#include "material.h" #include "material.h"
@@ -47,50 +47,25 @@ namespace {
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 }; const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
const int QuadraticCoefficientsSameColor[][8] = { const int QuadraticCoefficientsSameColor[][6] = {
{ 7, 7, 7, 7, 7, 7 }, { 39, 2, 7, 7, 7, 7 }, { 35, 271, -4, 7, 7, 7 }, { 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 } }; { 7, 25, 4, 7, 7, 7 }, { -27, -2, 46, 100, 56, 7 }, { 58, 29, 83, 148, -3, -25 } };
const int QuadraticCoefficientsOppositeColor[][8] = { const int QuadraticCoefficientsOppositeColor[][6] = {
{ 41, 41, 41, 41, 41, 41 }, { 37, 41, 41, 41, 41, 41 }, { 10, 62, 41, 41, 41, 41 }, { 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 } }; { 57, 64, 39, 41, 41, 41 }, { 50, 40, 23, -22, 41, 41 }, { 106, 101, 3, 151, 171, 41 } };
// Named endgame evaluation and scaling functions, these
// are accessed direcly and not through the function maps.
EvaluationFunction<KmmKm> EvaluateKmmKm(WHITE);
EvaluationFunction<KXK> EvaluateKXK(WHITE), EvaluateKKX(BLACK);
ScalingFunction<KBPsK> ScaleKBPsK(WHITE), ScaleKKBPs(BLACK);
ScalingFunction<KQKRPs> ScaleKQKRPs(WHITE), ScaleKRPsKQ(BLACK);
ScalingFunction<KPsK> ScaleKPsK(WHITE), ScaleKKPs(BLACK);
ScalingFunction<KPKP> ScaleKPKPw(WHITE), ScaleKPKPb(BLACK);
typedef EndgameEvaluationFunctionBase EF; typedef EndgameEvaluationFunctionBase EF;
typedef EndgameScalingFunctionBase SF; typedef EndgameScalingFunctionBase SF;
typedef map<Key, EF*> EFMap;
typedef map<Key, SF*> SFMap;
// Endgame evaluation and scaling functions accessed direcly and not through
// the function maps because correspond to more then one material hash key.
EvaluationFunction<KmmKm> EvaluateKmmKm[] = { EvaluationFunction<KmmKm>(WHITE), EvaluationFunction<KmmKm>(BLACK) };
EvaluationFunction<KXK> EvaluateKXK[] = { EvaluationFunction<KXK>(WHITE), EvaluationFunction<KXK>(BLACK) };
ScalingFunction<KBPsK> ScaleKBPsK[] = { ScalingFunction<KBPsK>(WHITE), ScalingFunction<KBPsK>(BLACK) };
ScalingFunction<KQKRPs> ScaleKQKRPs[] = { ScalingFunction<KQKRPs>(WHITE), ScalingFunction<KQKRPs>(BLACK) };
ScalingFunction<KPsK> ScaleKPsK[] = { ScalingFunction<KPsK>(WHITE), ScalingFunction<KPsK>(BLACK) };
ScalingFunction<KPKP> ScaleKPKP[] = { ScalingFunction<KPKP>(WHITE), ScalingFunction<KPKP>(BLACK) };
// Helper templates used to detect a given material distribution
template<Color Us> bool is_KXK(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
return pos.non_pawn_material(Them) == VALUE_ZERO
&& pos.piece_count(Them, PAWN) == 0
&& pos.non_pawn_material(Us) >= RookValueMidgame;
}
template<Color Us> bool is_KBPsK(const Position& pos) {
return pos.non_pawn_material(Us) == BishopValueMidgame
&& pos.piece_count(Us, BISHOP) == 1
&& pos.piece_count(Us, PAWN) >= 1;
}
template<Color Us> bool is_KQKRPs(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
return pos.piece_count(Us, PAWN) == 0
&& pos.non_pawn_material(Us) == QueenValueMidgame
&& pos.piece_count(Us, QUEEN) == 1
&& pos.piece_count(Them, ROOK) == 1
&& pos.piece_count(Them, PAWN) >= 1;
}
} }
@@ -115,17 +90,21 @@ private:
static Key buildKey(const string& keyCode); static Key buildKey(const string& keyCode);
static const string swapColors(const string& keyCode); static const string swapColors(const string& keyCode);
// Here we store two maps, for evaluate and scaling functions... // Here we store two maps, for evaluate and scaling functions
pair<EFMap, SFMap> maps; pair<map<Key, EF*>, map<Key, SF*> > maps;
// ...and here is the accessing template function // Maps accessing functions returning const and non-const references
template<typename T> const map<Key, T*>& get() const; template<typename T> const map<Key, T*>& get() const { return maps.first; }
template<typename T> map<Key, T*>& get() { return maps.first; }
}; };
// Explicit specializations of a member function shall be declared in // Explicit specializations of a member function shall be declared in
// the namespace of which the class template is a member. // the namespace of which the class template is a member.
template<> const EFMap& EndgameFunctions::get<EF>() const { return maps.first; } template<> const map<Key, SF*>&
template<> const SFMap& EndgameFunctions::get<SF>() const { return maps.second; } EndgameFunctions::get<SF>() const { return maps.second; }
template<> map<Key, SF*>&
EndgameFunctions::get<SF>() { return maps.second; }
//// ////
@@ -134,18 +113,18 @@ template<> const SFMap& EndgameFunctions::get<SF>() const { return maps.second;
/// MaterialInfoTable c'tor and d'tor, called once by each thread /// MaterialInfoTable c'tor and d'tor, called once by each thread
MaterialInfoTable::MaterialInfoTable() { MaterialInfoTable::MaterialInfoTable(unsigned int numOfEntries) {
entries = new MaterialInfo[MaterialTableSize]; size = numOfEntries;
entries = new MaterialInfo[size];
funcs = new EndgameFunctions(); funcs = new EndgameFunctions();
if (!entries || !funcs) if (!entries || !funcs)
{ {
cerr << "Failed to allocate " << MaterialTableSize * sizeof(MaterialInfo) cerr << "Failed to allocate " << numOfEntries * sizeof(MaterialInfo)
<< " bytes for material hash table." << endl; << " bytes for material hash table." << endl;
exit(EXIT_FAILURE); Application::exit_with_failure();
} }
memset(entries, 0, MaterialTableSize * sizeof(MaterialInfo));
} }
MaterialInfoTable::~MaterialInfoTable() { MaterialInfoTable::~MaterialInfoTable() {
@@ -165,8 +144,7 @@ Phase MaterialInfoTable::game_phase(const Position& pos) {
if (npm >= MidgameLimit) if (npm >= MidgameLimit)
return PHASE_MIDGAME; return PHASE_MIDGAME;
else if (npm <= EndgameLimit)
if (npm <= EndgameLimit)
return PHASE_ENDGAME; return PHASE_ENDGAME;
return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit)); return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
@@ -181,7 +159,7 @@ Phase MaterialInfoTable::game_phase(const Position& pos) {
MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) { MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
Key key = pos.get_material_key(); Key key = pos.get_material_key();
unsigned index = unsigned(key & (MaterialTableSize - 1)); int index = key & (size - 1);
MaterialInfo* mi = entries + index; MaterialInfo* mi = entries + index;
// If mi->key matches the position's material hash key, it means that we // If mi->key matches the position's material hash key, it means that we
@@ -191,8 +169,7 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
return mi; return mi;
// Clear the MaterialInfo object, and set its key // Clear the MaterialInfo object, and set its key
memset(mi, 0, sizeof(MaterialInfo)); mi->clear();
mi->factor[WHITE] = mi->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
mi->key = key; mi->key = key;
// Store game phase // Store game phase
@@ -204,13 +181,21 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
if ((mi->evaluationFunction = funcs->get<EF>(key)) != NULL) if ((mi->evaluationFunction = funcs->get<EF>(key)) != NULL)
return mi; return mi;
if (is_KXK<WHITE>(pos) || is_KXK<BLACK>(pos)) else if ( pos.non_pawn_material(BLACK) == Value(0)
&& pos.piece_count(BLACK, PAWN) == 0
&& pos.non_pawn_material(WHITE) >= RookValueMidgame)
{ {
mi->evaluationFunction = is_KXK<WHITE>(pos) ? &EvaluateKXK[WHITE] : &EvaluateKXK[BLACK]; mi->evaluationFunction = &EvaluateKXK;
return mi; return mi;
} }
else if ( pos.non_pawn_material(WHITE) == Value(0)
if ( pos.pieces(PAWN) == EmptyBoardBB && pos.piece_count(WHITE, PAWN) == 0
&& pos.non_pawn_material(BLACK) >= RookValueMidgame)
{
mi->evaluationFunction = &EvaluateKKX;
return mi;
}
else if ( pos.pieces(PAWN) == EmptyBoardBB
&& pos.pieces(ROOK) == EmptyBoardBB && pos.pieces(ROOK) == EmptyBoardBB
&& pos.pieces(QUEEN) == EmptyBoardBB) && pos.pieces(QUEEN) == EmptyBoardBB)
{ {
@@ -222,7 +207,7 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2 if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2
&& pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2) && pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2)
{ {
mi->evaluationFunction = &EvaluateKmmKm[WHITE]; mi->evaluationFunction = &EvaluateKmmKm;
return mi; return mi;
} }
} }
@@ -230,8 +215,10 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
// OK, we didn't find any special evaluation function for the current // OK, we didn't find any special evaluation function for the current
// material configuration. Is there a suitable scaling function? // material configuration. Is there a suitable scaling function?
// //
// We face problems when there are several conflicting applicable // The code below is rather messy, and it could easily get worse later,
// scaling functions and we need to decide which one to use. // if we decide to add more special cases. We face problems when there
// are several conflicting applicable scaling functions and we need to
// decide which one to use.
SF* sf; SF* sf;
if ((sf = funcs->get<SF>(key)) != NULL) if ((sf = funcs->get<SF>(key)) != NULL)
@@ -243,36 +230,48 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
// Generic scaling functions that refer to more then one material // Generic scaling functions that refer to more then one material
// distribution. Should be probed after the specialized ones. // distribution. Should be probed after the specialized ones.
// Note that these ones don't return after setting the function. // Note that these ones don't return after setting the function.
if (is_KBPsK<WHITE>(pos)) if ( pos.non_pawn_material(WHITE) == BishopValueMidgame
mi->scalingFunction[WHITE] = &ScaleKBPsK[WHITE]; && pos.piece_count(WHITE, BISHOP) == 1
&& pos.piece_count(WHITE, PAWN) >= 1)
mi->scalingFunction[WHITE] = &ScaleKBPsK;
if (is_KBPsK<BLACK>(pos)) if ( pos.non_pawn_material(BLACK) == BishopValueMidgame
mi->scalingFunction[BLACK] = &ScaleKBPsK[BLACK]; && pos.piece_count(BLACK, BISHOP) == 1
&& pos.piece_count(BLACK, PAWN) >= 1)
mi->scalingFunction[BLACK] = &ScaleKKBPs;
if (is_KQKRPs<WHITE>(pos)) if ( pos.piece_count(WHITE, PAWN) == 0
mi->scalingFunction[WHITE] = &ScaleKQKRPs[WHITE]; && pos.non_pawn_material(WHITE) == QueenValueMidgame
&& pos.piece_count(WHITE, QUEEN) == 1
&& pos.piece_count(BLACK, ROOK) == 1
&& pos.piece_count(BLACK, PAWN) >= 1)
mi->scalingFunction[WHITE] = &ScaleKQKRPs;
else if (is_KQKRPs<BLACK>(pos)) else if ( pos.piece_count(BLACK, PAWN) == 0
mi->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK]; && pos.non_pawn_material(BLACK) == QueenValueMidgame
&& pos.piece_count(BLACK, QUEEN) == 1
&& pos.piece_count(WHITE, ROOK) == 1
&& pos.piece_count(WHITE, PAWN) >= 1)
mi->scalingFunction[BLACK] = &ScaleKRPsKQ;
if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) == VALUE_ZERO) if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) == Value(0))
{ {
if (pos.piece_count(BLACK, PAWN) == 0) if (pos.piece_count(BLACK, PAWN) == 0)
{ {
assert(pos.piece_count(WHITE, PAWN) >= 2); assert(pos.piece_count(WHITE, PAWN) >= 2);
mi->scalingFunction[WHITE] = &ScaleKPsK[WHITE]; mi->scalingFunction[WHITE] = &ScaleKPsK;
} }
else if (pos.piece_count(WHITE, PAWN) == 0) else if (pos.piece_count(WHITE, PAWN) == 0)
{ {
assert(pos.piece_count(BLACK, PAWN) >= 2); assert(pos.piece_count(BLACK, PAWN) >= 2);
mi->scalingFunction[BLACK] = &ScaleKPsK[BLACK]; mi->scalingFunction[BLACK] = &ScaleKKPs;
} }
else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1) else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1)
{ {
// This is a special case because we set scaling functions // This is a special case because we set scaling functions
// for both colors instead of only one. // for both colors instead of only one.
mi->scalingFunction[WHITE] = &ScaleKPKP[WHITE]; mi->scalingFunction[WHITE] = &ScaleKPKPw;
mi->scalingFunction[BLACK] = &ScaleKPKP[BLACK]; mi->scalingFunction[BLACK] = &ScaleKPKPb;
} }
} }
@@ -289,12 +288,10 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
} }
// Evaluate the material balance // Evaluate the material balance
const int pieceCount[2][8] = { const int pieceCount[2][6] = { { pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT),
{ 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(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) > 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) } }; pos.piece_count(BLACK, BISHOP), pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } };
Color c, them; Color c, them;
int sign, pt1, pt2, pc; int sign, pt1, pt2, pc;
int v, vv, matValue = 0; int v, vv, matValue = 0;
@@ -335,9 +332,9 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
// Second-degree polynomial material imbalance by Tord Romstad // Second-degree polynomial material imbalance by Tord Romstad
// //
// We use PIECE_TYPE_NONE as a place holder for the bishop pair "extended piece", // We use NO_PIECE_TYPE as a place holder for the bishop pair "extended piece",
// this allow us to be more flexible in defining bishop pair bonuses. // this allow us to be more flexible in defining bishop pair bonuses.
for (pt1 = PIECE_TYPE_NONE; pt1 <= QUEEN; pt1++) for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
{ {
pc = pieceCount[c][pt1]; pc = pieceCount[c][pt1];
if (!pc) if (!pc)
@@ -345,7 +342,7 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
vv = LinearCoefficients[pt1]; vv = LinearCoefficients[pt1];
for (pt2 = PIECE_TYPE_NONE; pt2 <= pt1; pt2++) for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
vv += pieceCount[c][pt2] * QuadraticCoefficientsSameColor[pt1][pt2] vv += pieceCount[c][pt2] * QuadraticCoefficientsSameColor[pt1][pt2]
+ pieceCount[them][pt2] * QuadraticCoefficientsOppositeColor[pt1][pt2]; + pieceCount[them][pt2] * QuadraticCoefficientsOppositeColor[pt1][pt2];
@@ -353,12 +350,12 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
} }
matValue += sign * v; matValue += sign * v;
} }
mi->value = (int16_t)(matValue / 16); mi->value = int16_t(matValue / 16);
return mi; return mi;
} }
/// EndgameFunctions member definitions /// EndgameFunctions member definitions.
EndgameFunctions::EndgameFunctions() { EndgameFunctions::EndgameFunctions() {
@@ -381,19 +378,19 @@ EndgameFunctions::EndgameFunctions() {
EndgameFunctions::~EndgameFunctions() { EndgameFunctions::~EndgameFunctions() {
for (EFMap::const_iterator it = maps.first.begin(); it != maps.first.end(); ++it) for (map<Key, EF*>::iterator it = maps.first.begin(); it != maps.first.end(); ++it)
delete it->second; delete (*it).second;
for (SFMap::const_iterator it = maps.second.begin(); it != maps.second.end(); ++it) for (map<Key, SF*>::iterator it = maps.second.begin(); it != maps.second.end(); ++it)
delete it->second; delete (*it).second;
} }
Key EndgameFunctions::buildKey(const string& keyCode) { Key EndgameFunctions::buildKey(const string& keyCode) {
assert(keyCode.length() > 0 && keyCode.length() < 8); assert(keyCode.length() > 0 && keyCode[0] == 'K');
assert(keyCode[0] == 'K'); assert(keyCode.length() < 8);
string fen; stringstream s;
bool upcase = false; bool upcase = false;
// Build up a fen string with the given pieces, note that // Build up a fen string with the given pieces, note that
@@ -403,17 +400,16 @@ Key EndgameFunctions::buildKey(const string& keyCode) {
if (keyCode[i] == 'K') if (keyCode[i] == 'K')
upcase = !upcase; upcase = !upcase;
fen += char(upcase ? toupper(keyCode[i]) : tolower(keyCode[i])); s << char(upcase? toupper(keyCode[i]) : tolower(keyCode[i]));
} }
fen += char(8 - keyCode.length() + '0'); s << 8 - keyCode.length() << "/8/8/8/8/8/8/8 w -";
fen += "/8/8/8/8/8/8/8 w - -"; return Position(s.str()).get_material_key();
return Position(fen, false, 0).get_material_key();
} }
const string EndgameFunctions::swapColors(const string& keyCode) { const string EndgameFunctions::swapColors(const string& keyCode) {
// Build corresponding key for the opposite color: "KBPKN" -> "KNKBP" // 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); return keyCode.substr(idx) + keyCode.substr(0, idx);
} }
@@ -421,15 +417,14 @@ template<class T>
void EndgameFunctions::add(const string& keyCode) { void EndgameFunctions::add(const string& keyCode) {
typedef typename T::Base F; typedef typename T::Base F;
typedef map<Key, F*> M;
const_cast<M&>(get<F>()).insert(pair<Key, F*>(buildKey(keyCode), new T(WHITE))); get<F>().insert(pair<Key, F*>(buildKey(keyCode), new T(WHITE)));
const_cast<M&>(get<F>()).insert(pair<Key, F*>(buildKey(swapColors(keyCode)), new T(BLACK))); get<F>().insert(pair<Key, F*>(buildKey(swapColors(keyCode)), new T(BLACK)));
} }
template<class T> template<class T>
T* EndgameFunctions::get(Key key) const { T* EndgameFunctions::get(Key key) const {
typename map<Key, T*>::const_iterator it = get<T>().find(key); typename map<Key, T*>::const_iterator it(get<T>().find(key));
return it != get<T>().end() ? it->second : NULL; return (it != get<T>().end() ? it->second : NULL);
} }
+22 -7
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -27,14 +27,13 @@
#include "endgame.h" #include "endgame.h"
#include "position.h" #include "position.h"
#include "scale.h"
//// ////
//// Types //// Types
//// ////
const int MaterialTableSize = 1024;
/// MaterialInfo is a class which contains various information about a /// MaterialInfo is a class which contains various information about a
/// material configuration. It contains a material balance evaluation, /// material configuration. It contains a material balance evaluation,
/// a function pointer to a special endgame evaluation function (which in /// a function pointer to a special endgame evaluation function (which in
@@ -50,6 +49,8 @@ class MaterialInfo {
friend class MaterialInfoTable; friend class MaterialInfoTable;
public: public:
MaterialInfo() : key(0) { clear(); }
Score material_value() const; Score material_value() const;
ScaleFactor scale_factor(const Position& pos, Color c) const; ScaleFactor scale_factor(const Position& pos, Color c) const;
int space_weight() const; int space_weight() const;
@@ -58,6 +59,8 @@ public:
Value evaluate(const Position& pos) const; Value evaluate(const Position& pos) const;
private: private:
inline void clear();
Key key; Key key;
int16_t value; int16_t value;
uint8_t factor[2]; uint8_t factor[2];
@@ -75,17 +78,15 @@ class EndgameFunctions;
class MaterialInfoTable { class MaterialInfoTable {
MaterialInfoTable(const MaterialInfoTable&);
MaterialInfoTable& operator=(const MaterialInfoTable&);
public: public:
MaterialInfoTable(); MaterialInfoTable(unsigned numOfEntries);
~MaterialInfoTable(); ~MaterialInfoTable();
MaterialInfo* get_material_info(const Position& pos); MaterialInfo* get_material_info(const Position& pos);
static Phase game_phase(const Position& pos); static Phase game_phase(const Position& pos);
private: private:
unsigned size;
MaterialInfo* entries; MaterialInfo* entries;
EndgameFunctions* funcs; EndgameFunctions* funcs;
}; };
@@ -104,6 +105,20 @@ inline Score MaterialInfo::material_value() const {
return make_score(value, value); return make_score(value, value);
} }
/// MaterialInfo::clear() resets a MaterialInfo object to an empty state,
/// with all slots at their default values but the key.
inline void MaterialInfo::clear() {
value = 0;
factor[WHITE] = factor[BLACK] = uint8_t(SCALE_FACTOR_NORMAL);
evaluationFunction = NULL;
scalingFunction[WHITE] = scalingFunction[BLACK] = NULL;
spaceWeight = 0;
}
/// MaterialInfo::scale_factor takes a position and a color as input, and /// MaterialInfo::scale_factor takes a position and a color as input, and
/// returns a scale factor for the given color. We have to provide the /// returns a scale factor for the given color. We have to provide the
/// position in addition to the color, because the scale factor need not /// position in addition to the color, because the scale factor need not
+149
View File
@@ -0,0 +1,149 @@
/*
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 "types.h"
#include "mersenne.h"
/* Period parameters */
#define N 624
#define M 397
#define MATRIX_A 0x9908b0dfUL /* constant vector a */
#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
static unsigned long mt[N]; /* the array for the state vector */
static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
/* initializes mt[N] with a seed */
static void init_genrand(unsigned long s)
{
mt[0]= s & 0xffffffffUL;
for (mti=1; mti<N; mti++) {
mt[mti] =
(1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + 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] &= 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 */
static void init_by_array(unsigned long init_key[], int key_length)
{
int i, j, k;
init_genrand(19650218UL);
i=1; j=0;
k = (N>key_length ? N : key_length);
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 */
}
/* generates a random number on [0,0xffffffff]-interval */
uint32_t genrand_int32(void) {
unsigned long y;
static unsigned long mag01[2]={0x0UL, MATRIX_A};
/* mag01[x] = x * MATRIX_A for x=0,1 */
if (mti >= N) { /* generate N words at one time */
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(void) {
uint64_t x, y;
x = genrand_int32(); y = genrand_int32();
return (x<<32)|y;
}
void init_mersenne(void) {
unsigned long init[4]={0x123, 0x234, 0x345, 0x456}, length=4;
init_by_array(init, length);
}
+40
View File
@@ -0,0 +1,40 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2009 Marco Costalba
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(void);
extern uint64_t genrand_int64(void);
extern void init_mersenne(void);
#endif // !defined(MERSENNE_H_INCLUDED)
+68 -80
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -27,9 +27,6 @@
# include <sys/time.h> # include <sys/time.h>
# include <sys/types.h> # include <sys/types.h>
# include <unistd.h> # include <unistd.h>
# if defined(__hpux)
# include <sys/pstat.h>
# endif
#else #else
@@ -39,10 +36,6 @@
#endif #endif
#if !defined(NO_PREFETCH)
# include <xmmintrin.h>
#endif
#include <cassert> #include <cassert>
#include <cstdio> #include <cstdio>
#include <iomanip> #include <iomanip>
@@ -51,14 +44,13 @@
#include "bitcount.h" #include "bitcount.h"
#include "misc.h" #include "misc.h"
#include "thread.h"
using namespace std; using namespace std;
/// Version number. If this is left empty, the current date (in the format /// Version number. If this is left empty, the current date (in the format
/// YYMMDD) is used as a version number. /// YYMMDD) is used as a version number.
static const string EngineVersion = "2.0.1"; static const string EngineVersion = "1.6.3";
static const string AppName = "Stockfish"; static const string AppName = "Stockfish";
static const string AppTag = ""; static const string AppTag = "";
@@ -67,8 +59,10 @@ static const string AppTag = "";
//// Variables //// Variables
//// ////
static uint64_t dbg_cnt0 = 0; bool Chess960;
static uint64_t dbg_cnt1 = 0;
uint64_t dbg_cnt0 = 0;
uint64_t dbg_cnt1 = 0;
bool dbg_show_mean = false; bool dbg_show_mean = false;
bool dbg_show_hit_rate = false; bool dbg_show_hit_rate = false;
@@ -127,29 +121,42 @@ void dbg_print_mean() {
<< (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << endl; << (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. /// engine_name() returns the full name of the current Stockfish version.
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when /// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when the
/// the program was compiled) or "Stockfish <version number>", depending /// program was compiled) or "Stockfish <version number>", depending on whether
/// on whether the constant EngineVersion (defined in misc.h) is empty. /// the constant EngineVersion (defined in misc.h) is empty.
const string engine_name() { const string engine_name() {
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); const string cpu64(CpuHas64BitPath ? " 64bit" : "");
const string cpu64(CpuIs64Bit ? " 64bit" : "");
if (!EngineVersion.empty()) if (!EngineVersion.empty())
return AppName+ " " + EngineVersion + cpu64; return AppName+ " " + EngineVersion + cpu64;
stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008" string date(__DATE__); // From compiler, format is "Sep 21 2008"
string month, day, year; string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
date >> month >> day >> year; size_t mon = 1 + months.find(date.substr(0, 3)) / 4;
s << setfill('0') << AppName + " " + AppTag + " " stringstream s;
<< year.substr(2, 2) << setw(2) string day = (date[4] == ' ' ? date.substr(5, 1) : date.substr(4, 2));
<< (1 + months.find(month) / 4) << setw(2)
<< day << cpu64; string name = AppName + " " + AppTag + " ";
s << name << date.substr(date.length() - 2) << setfill('0')
<< setw(2) << mon << setw(2) << day << cpu64;
return s.str(); return s.str();
} }
@@ -178,15 +185,7 @@ int get_system_time() {
# if defined(_SC_NPROCESSORS_ONLN) # if defined(_SC_NPROCESSORS_ONLN)
int cpu_count() { int cpu_count() {
return Min(sysconf(_SC_NPROCESSORS_ONLN), MAX_THREADS); return Min(sysconf(_SC_NPROCESSORS_ONLN), 8);
}
# elif defined(__hpux)
int cpu_count() {
struct pst_dynamic psd;
if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) == -1)
return 1;
return Min(psd.psd_proc_cnt, MAX_THREADS);
} }
# else # else
int cpu_count() { int cpu_count() {
@@ -199,24 +198,26 @@ int cpu_count() {
int cpu_count() { int cpu_count() {
SYSTEM_INFO s; SYSTEM_INFO s;
GetSystemInfo(&s); GetSystemInfo(&s);
return Min(s.dwNumberOfProcessors, MAX_THREADS); return Min(s.dwNumberOfProcessors, 8);
} }
#endif #endif
/// Check for console input. Original code from Beowulf and Olithink /*
From Beowulf, from Olithink
*/
#ifndef _WIN32 #ifndef _WIN32
/* Non-windows version */
int data_available() int Bioskey()
{ {
fd_set readfds; fd_set readfds;
struct timeval timeout; struct timeval timeout;
FD_ZERO(&readfds); FD_ZERO(&readfds);
FD_SET(fileno(stdin), &readfds); FD_SET(fileno(stdin), &readfds);
timeout.tv_sec = 0; // Set to timeout immediately /* Set to timeout immediately */
timeout.tv_sec = 0;
timeout.tv_usec = 0; timeout.tv_usec = 0;
select(16, &readfds, 0, 0, &timeout); select(16, &readfds, 0, 0, &timeout);
@@ -224,36 +225,46 @@ int data_available()
} }
#else #else
/* Windows-version */
int data_available() #include <windows.h>
#include <conio.h>
int Bioskey()
{ {
static HANDLE inh = NULL; static int init = 0,
static bool usePipe; pipe;
INPUT_RECORD rec[256]; static HANDLE inh;
DWORD dw, recCnt; 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 (!inh) #if defined(FILE_CNT)
{ if (stdin->_cnt > 0)
return stdin->_cnt;
#endif
if (!init) {
init = 1;
inh = GetStdHandle(STD_INPUT_HANDLE); inh = GetStdHandle(STD_INPUT_HANDLE);
usePipe = !GetConsoleMode(inh, &dw); pipe = !GetConsoleMode(inh, &dw);
if (!usePipe) if (!pipe) {
{
SetConsoleMode(inh, dw & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT)); SetConsoleMode(inh, dw & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
FlushConsoleInputBuffer(inh); FlushConsoleInputBuffer(inh);
} }
} }
if (pipe) {
// If we're running under XBoard then we can't use PeekConsoleInput() as if (!PeekNamedPipe(inh, NULL, 0, NULL, &dw, NULL))
// the input commands are sent to us directly over the internal pipe. return 1;
if (usePipe) return dw;
return PeekNamedPipe(inh, NULL, 0, NULL, &dw, NULL) ? dw : 1; } else {
// Count the number of unread input records, including keyboard, // Count the number of unread input records, including keyboard,
// mouse, and window-resizing input records. // mouse, and window-resizing input records.
GetNumberOfConsoleInputEvents(inh, &dw); GetNumberOfConsoleInputEvents(inh, &dw);
if (dw <= 0)
return 0;
// Read data from console without removing it from the buffer // Read data from console without removing it from the buffer
if (dw <= 0 || !PeekConsoleInput(inh, rec, Min(dw, 256), &recCnt)) INPUT_RECORD rec[256];
DWORD recCnt;
if (!PeekConsoleInput(inh, rec, Min(dw, 256), &recCnt))
return 0; return 0;
// Search for at least one keyboard event // Search for at least one keyboard event
@@ -263,28 +274,5 @@ int data_available()
return 0; 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.
#if defined(NO_PREFETCH)
void prefetch(char*) {}
#else
void prefetch(char* addr) {
#if defined(__INTEL_COMPILER) || defined(__ICL)
// This hack prevents prefetches to be optimized away by
// Intel compiler. Both MSVC and gcc seems not affected.
__asm__ ("");
#endif
_mm_prefetch(addr, _MM_HINT_T2);
_mm_prefetch(addr+64, _MM_HINT_T2); // 64 bytes ahead
} }
#endif #endif
+15 -4
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -29,6 +29,7 @@
#include <fstream> #include <fstream>
#include <string> #include <string>
#include "application.h"
#include "types.h" #include "types.h"
//// ////
@@ -39,6 +40,13 @@
#define Max(x, y) (((x) < (y))? (y) : (x)) #define Max(x, y) (((x) < (y))? (y) : (x))
////
//// Variables
////
extern bool Chess960;
//// ////
//// Prototypes //// Prototypes
//// ////
@@ -46,9 +54,7 @@
extern const std::string engine_name(); extern const std::string engine_name();
extern int get_system_time(); extern int get_system_time();
extern int cpu_count(); extern int cpu_count();
extern int data_available(); extern int Bioskey();
extern void prefetch(char* addr);
extern void prefetchPawn(Key, int);
//// ////
@@ -58,6 +64,9 @@ extern void prefetchPawn(Key, int);
extern bool dbg_show_mean; extern bool dbg_show_mean;
extern bool dbg_show_hit_rate; 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(bool b);
extern void dbg_hit_on_c(bool c, bool b); extern void dbg_hit_on_c(bool c, bool b);
extern void dbg_before(); extern void dbg_before();
@@ -65,5 +74,7 @@ extern void dbg_after();
extern void dbg_mean_of(int v); extern void dbg_mean_of(int v);
extern void dbg_print_hit_rate(); extern void dbg_print_hit_rate();
extern void dbg_print_mean(); 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) #endif // !defined(MISC_H_INCLUDED)
+32 -32
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -23,7 +23,6 @@
//// ////
#include <cassert> #include <cassert>
#include <cctype>
#include "move.h" #include "move.h"
#include "piece.h" #include "piece.h"
@@ -34,13 +33,13 @@
//// Functions //// Functions
//// ////
/// move_from_uci() takes a position and a string as input, and attempts to /// move_from_string() takes a position and a string as input, and attempts to
/// convert the string to a move, using simple coordinate notation (g1f3, /// convert the string to a move, using simple coordinate notation (g1f3,
/// a7a8q, etc.). In order to correctly parse en passant captures and castling /// 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 /// moves, we need the position. This function is not robust, and expects that
/// the input move is legal and correctly formatted. /// the input move is legal and correctly formatted.
Move move_from_uci(const Position& pos, const std::string& str) { Move move_from_string(const Position& pos, const std::string& str) {
Square from, to; Square from, to;
Piece piece; Piece piece;
@@ -50,15 +49,15 @@ Move move_from_uci(const Position& pos, const std::string& str) {
return MOVE_NONE; return MOVE_NONE;
// Read the from and to squares // Read the from and to squares
from = make_square(file_from_char(str[0]), rank_from_char(str[1])); from = square_from_string(str.substr(0, 2));
to = make_square(file_from_char(str[2]), rank_from_char(str[3])); to = square_from_string(str.substr(2, 4));
// Find the moving piece // Find the moving piece
piece = pos.piece_on(from); piece = pos.piece_on(from);
// If the string has more than 4 characters, try to interpret the 5th // If the string has more than 4 characters, try to interpret the 5th
// character as a promotion. // character as a promotion
if (str.length() > 4 && piece == piece_of_color_and_type(us, PAWN)) if (type_of_piece(piece) == PAWN && str.length() > 4)
{ {
switch (tolower(str[4])) { switch (tolower(str[4])) {
case 'n': case 'n':
@@ -72,44 +71,44 @@ Move move_from_uci(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)) 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)) if (pos.piece_on(to) == piece_of_color_and_type(us, ROOK))
return make_castle_move(from, to); return make_castle_move(from, to);
if (square_distance(from, to) > 1) else if (square_distance(from, to) > 1)
{ {
// This is a castling move, but we have to translate it to the // This is a castling move, but we have to translate it to the
// internal "king captures rook" representation. // internal "king captures rook" representation.
SquareDelta delta = (to > from ? DELTA_E : DELTA_W); SquareDelta delta = (to > from ? DELTA_E : DELTA_W);
Square s = from; Square s = from + delta;
while (relative_rank(us, s) == RANK_1 && pos.piece_on(s) != piece_of_color_and_type(us, ROOK))
s += delta;
do s += delta; return (relative_rank(us, s) == RANK_1 ? make_castle_move(from, s) : MOVE_NONE);
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); return make_move(from, to);
} }
/// move_to_uci() converts a move to a string in coordinate notation /// move_to_string() converts a move to a string in coordinate notation
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we /// (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 /// print in the e1g1 notation in normal chess mode, and in e1h1 notation in
/// Chess960 mode. /// Chess960 mode.
const std::string move_to_uci(Move move, bool chess960) { const std::string move_to_string(Move move) {
std::string str; std::string str;
Square from = move_from(move); Square from = move_from(move);
@@ -121,15 +120,17 @@ const std::string move_to_uci(Move move, bool chess960) {
str = "0000"; str = "0000";
else else
{ {
if (move_is_short_castle(move) && !chess960) if (!Chess960)
{
if (move_is_short_castle(move))
return (from == SQ_E1 ? "e1g1" : "e8g8"); return (from == SQ_E1 ? "e1g1" : "e8g8");
if (move_is_long_castle(move) && !chess960) if (move_is_long_castle(move))
return (from == SQ_E1 ? "e1c1" : "e8c8"); return (from == SQ_E1 ? "e1c1" : "e8c8");
}
str = square_to_string(from) + square_to_string(to); str = square_to_string(from) + square_to_string(to);
if (move_is_promotion(move)) if (move_is_promotion(move))
str += char(tolower(piece_type_to_char(move_promotion_piece(move)))); str += piece_type_to_char(move_promotion_piece(move), false);
} }
return str; return str;
} }
@@ -139,8 +140,7 @@ const std::string move_to_uci(Move move, bool chess960) {
std::ostream &operator << (std::ostream& os, Move m) { std::ostream &operator << (std::ostream& os, Move m) {
bool chess960 = (os.iword(0) != 0); // See set960() return os << move_to_string(m);
return os << move_to_uci(m, chess960);
} }
+40 -21
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -31,8 +31,6 @@
#include "piece.h" #include "piece.h"
#include "square.h" #include "square.h"
// Maximum number of allowed moves per position
const int MOVES_MAX = 256;
//// ////
//// Types //// Types
@@ -64,33 +62,34 @@ struct MoveStack {
int score; int score;
}; };
inline bool operator<(const MoveStack& f, const MoveStack& s) { return f.score < s.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; }
// An helper insertion sort implementation, works with pointers and iterators // An helper insertion sort implementation
template<typename T, typename K> template<typename T>
inline void insertion_sort(K firstMove, K lastMove) inline void insertion_sort(T* firstMove, T* lastMove)
{ {
T value; T value;
K cur, p, d; T *cur, *p, *d;
if (firstMove != lastMove) if (firstMove != lastMove)
for (cur = firstMove + 1; cur != lastMove; cur++) for (cur = firstMove + 1; cur != lastMove; cur++)
{ {
p = d = cur; p = d = cur;
value = *p--; value = *p--;
if (*p < value) if (value < *p)
{ {
do *d = *p; do *d = *p;
while (--d != firstMove && *--p < value); while (--d != firstMove && value < *--p);
*d = value; *d = value;
} }
} }
} }
// Our dedicated sort in range [firstMove, lastMove), first splits // Our dedicated sort in range [firstMove, lastMove), it is well
// positive scores from ramining then order seaprately the two sets. // tuned for non-captures where we have a lot of zero scored moves.
template<typename T> template<typename T>
inline void sort_moves(T* firstMove, T* lastMove, T** lastPositive) inline void sort_moves(T* firstMove, T* lastMove)
{ {
T tmp; T tmp;
T *p, *d; T *p, *d;
@@ -102,11 +101,11 @@ inline void sort_moves(T* firstMove, T* lastMove, T** lastPositive)
// Split positives vs non-positives // Split positives vs non-positives
do { do {
while ((++p)->score > 0) {} while ((++p)->score > 0);
if (p != d) if (p != d)
{ {
while (--d != p && d->score <= 0) {} while (--d != p && d->score <= 0);
tmp = *p; tmp = *p;
*p = *d; *p = *d;
@@ -115,9 +114,29 @@ inline void sort_moves(T* firstMove, T* lastMove, T** lastPositive)
} while (p != d); } while (p != d);
// Sort just positive scored moves, remaining only when we get there // Sort positives
insertion_sort<T, T*>(firstMove, p); insertion_sort<T>(firstMove, p);
*lastPositive = p;
d = lastMove;
p--;
// Split zero vs negatives
do {
while ((++p)->score == 0);
if (p != d)
{
while (--d != p && d->score < 0);
tmp = *p;
*p = *d;
*d = tmp;
}
} while (p != d);
// Sort negatives
insertion_sort<T>(p, lastMove);
} }
// Picks up the best move in range [curMove, lastMove), one per cycle. // Picks up the best move in range [curMove, lastMove), one per cycle.
@@ -131,7 +150,7 @@ inline T pick_best(T* curMove, T* lastMove)
bestMove = *curMove; bestMove = *curMove;
while (++curMove != lastMove) while (++curMove != lastMove)
{ {
if (bestMove < *curMove) if (*curMove < bestMove)
{ {
tmp = *curMove; tmp = *curMove;
*curMove = bestMove; *curMove = bestMove;
@@ -203,8 +222,8 @@ inline Move make_ep_move(Square from, Square to) {
//// ////
extern std::ostream& operator<<(std::ostream &os, Move m); extern std::ostream& operator<<(std::ostream &os, Move m);
extern Move move_from_uci(const Position& pos, const std::string &str); extern Move move_from_string(const Position &pos, const std::string &str);
extern const std::string move_to_uci(Move m, bool chess960); extern const std::string move_to_string(Move m);
extern bool move_is_ok(Move m); extern bool move_is_ok(Move m);
+119 -125
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -140,32 +140,6 @@ MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist) {
} }
/// generate_non_evasions() generates all pseudo-legal captures and
/// non-captures. Returns a pointer to the end of the move list.
MoveStack* generate_non_evasions(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.is_check());
Color us = pos.side_to_move();
Bitboard target = pos.pieces_of_color(opposite_color(us));
mlist = generate_piece_moves<PAWN, CAPTURE>(pos, mlist, us, target);
mlist = generate_piece_moves<PAWN, NON_CAPTURE>(pos, mlist, us, pos.empty_squares());
target |= pos.empty_squares();
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target);
mlist = generate_piece_moves<KING>(pos, mlist, us, target);
mlist = generate_castle_moves<KING_SIDE>(pos, mlist);
return generate_castle_moves<QUEEN_SIDE>(pos, mlist);
}
/// generate_non_capture_checks() generates all pseudo-legal non-captures and knight /// generate_non_capture_checks() generates all pseudo-legal non-captures and knight
/// underpromotions that give check. Returns a pointer to the end of the move list. /// underpromotions that give check. Returns a pointer to the end of the move list.
@@ -244,7 +218,7 @@ MoveStack* generate_evasions(const Position& pos, MoveStack* mlist) {
case QUEEN: case QUEEN:
// In case of a queen remove also squares attacked in the other direction to // 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. // avoid possible illegal moves when queen and king are on adjacent squares.
if (RookPseudoAttacks[checksq] & (1ULL << ksq)) if (direction_is_straight(checksq, ksq))
sliderAttacks |= RookPseudoAttacks[checksq] | pos.attacks_from<BISHOP>(checksq); sliderAttacks |= RookPseudoAttacks[checksq] | pos.attacks_from<BISHOP>(checksq);
else else
sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq); sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq);
@@ -286,8 +260,11 @@ MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLega
Bitboard pinned = pos.pinned_pieces(pos.side_to_move()); Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
// Generate pseudo-legal moves // Generate pseudo-legal moves
last = pos.is_check() ? generate_evasions(pos, mlist) if (pos.is_check())
: generate_non_evasions(pos, mlist); last = generate_evasions(pos, mlist);
else
last = generate_noncaptures(pos, generate_captures(pos, mlist));
if (pseudoLegal) if (pseudoLegal)
return last; return last;
@@ -308,7 +285,7 @@ MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLega
bool move_is_legal(const Position& pos, const Move m) { bool move_is_legal(const Position& pos, const Move m) {
MoveStack mlist[MOVES_MAX]; MoveStack mlist[256];
MoveStack *cur, *last = generate_moves(pos, mlist, true); MoveStack *cur, *last = generate_moves(pos, mlist, true);
for (cur = mlist; cur != last; cur++) for (cur = mlist; cur != last; cur++)
@@ -335,7 +312,7 @@ bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) {
Piece pc = pos.piece_on(from); Piece pc = pos.piece_on(from);
// Use a slower but simpler function for uncommon cases // Use a slower but simpler function for uncommon cases
if (move_is_special(m)) if (move_is_ep(m) || move_is_castle(m))
return move_is_legal(pos, m); return move_is_legal(pos, m);
// If the from square is not occupied by a piece belonging to the side to // If the from square is not occupied by a piece belonging to the side to
@@ -355,9 +332,10 @@ bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) {
if ((us == WHITE) != (direction > 0)) if ((us == WHITE) != (direction > 0))
return false; return false;
// We have already handled promotion moves, so destination // A pawn move is a promotion iff the destination square is
// cannot be on the 8/1th rank. // on the 8/1th rank.
if (square_rank(to) == RANK_8 || square_rank(to) == RANK_1) if (( (square_rank(to) == RANK_8 && us == WHITE)
||(square_rank(to) == RANK_1 && us != WHITE)) != bool(move_is_promotion(m)))
return false; return false;
// Proceed according to the square delta between the origin and // Proceed according to the square delta between the origin and
@@ -404,14 +382,16 @@ bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) {
default: default:
return false; return false;
} }
}
else if (!bit_is_set(pos.attacks_from(pc, from), to))
return false;
// The move is pseudo-legal, check if it is also legal // The move is pseudo-legal, check if it is also legal
return pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned); return pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned);
} }
// Luckly we can handle all the other pieces in one go
return bit_is_set(pos.attacks_from(pc, from), to)
&& (pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned))
&& !move_is_promotion(m);
}
namespace { namespace {
@@ -422,13 +402,10 @@ namespace {
Square from; Square from;
const Square* ptr = pos.piece_list_begin(us, Piece); const Square* ptr = pos.piece_list_begin(us, Piece);
if (*ptr != SQ_NONE) while ((from = *ptr++) != SQ_NONE)
{ {
do {
from = *ptr;
b = pos.attacks_from<Piece>(from) & target; b = pos.attacks_from<Piece>(from) & target;
SERIALIZE_MOVES(b); SERIALIZE_MOVES(b);
} while (*++ptr != SQ_NONE);
} }
return mlist; return mlist;
} }
@@ -444,135 +421,152 @@ namespace {
return mlist; return mlist;
} }
template<SquareDelta Delta> template<Color Us, SquareDelta Direction>
inline Bitboard move_pawns(Bitboard p) { inline Bitboard move_pawns(Bitboard p) {
return Delta == DELTA_N ? p << 8 : Delta == DELTA_S ? p >> 8 : if (Direction == DELTA_N)
Delta == DELTA_NE ? p << 9 : Delta == DELTA_SE ? p >> 7 : return Us == WHITE ? p << 8 : p >> 8;
Delta == DELTA_NW ? p << 7 : Delta == DELTA_SW ? p >> 9 : p; 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;
} }
template<MoveType Type, SquareDelta Delta> template<Color Us, MoveType Type, SquareDelta Diagonal>
inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard target) { inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard enemyPieces) {
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB); // 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);
Bitboard b; Bitboard b1, b2;
Square to; Square to;
// Captures in the a1-h8 (a8-h1 for black) diagonal or in the h1-a8 (h8-a1 for black) // Captures in the a1-h8 (a8-h1 for black) diagonal or in the h1-a8 (h8-a1 for black)
b = move_pawns<Delta>(pawns) & target & ~TFileABB; b1 = move_pawns<Us, Diagonal>(pawns) & ~TFileABB & enemyPieces;
SERIALIZE_MOVES_D(b, -Delta);
return mlist;
}
template<Color Us, MoveType Type, SquareDelta Delta> // Capturing promotions and under-promotions
inline MoveStack* generate_promotions(const Position& pos, MoveStack* mlist, Bitboard pawnsOn7, Bitboard target) { if (b1 & TRank8BB)
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)
{ {
to = pop_1st_bit(&b); b2 = b1 & TRank8BB;
b1 &= ~TRank8BB;
while (b2)
{
to = pop_1st_bit(&b2);
if (Type == CAPTURE || Type == EVASION) if (Type == CAPTURE || Type == EVASION)
(*mlist++).move = make_promotion_move(to - Delta, to, QUEEN); (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, QUEEN);
if (Type == NON_CAPTURE || Type == EVASION) if (Type == NON_CAPTURE || Type == EVASION)
{ {
(*mlist++).move = make_promotion_move(to - Delta, to, ROOK); (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, ROOK);
(*mlist++).move = make_promotion_move(to - Delta, to, BISHOP); (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, BISHOP);
(*mlist++).move = make_promotion_move(to - Delta, to, KNIGHT); (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT);
} }
// This is the only possible under promotion that can give a check // This is the only possible under promotion that can give a check
// not already included in the queen-promotion. // not already included in the queen-promotion. It is not sure that
if ( Type == CHECK // the promoted knight will give check, but it doesn't worth to verify.
&& bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(opposite_color(Us)))) if (Type == CHECK)
(*mlist++).move = make_promotion_move(to - Delta, to, KNIGHT); (*mlist++).move = make_promotion_move(to - TTDELTA_NE, 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; return mlist;
} }
template<Color Us, MoveType Type> template<Color Us, MoveType Type>
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) { MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) {
// Calculate our parametrized parameters at compile time, named // Calculate our parametrized parameters at compile time
// according to the point of view of white side.
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const SquareDelta TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S); 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; Square to;
Bitboard b1, b2, dc1, dc2, pawnPushes, emptySquares; Bitboard b1, b2, enemyPieces, emptySquares;
Bitboard pawns = pos.pieces(PAWN, Us); Bitboard pawns = pos.pieces(PAWN, Us);
Bitboard pawnsOn7 = pawns & TRank7BB;
Bitboard enemyPieces = (Type == CAPTURE ? target : pos.pieces_of_color(Them));
// Pre-calculate pawn pushes before changing emptySquares definition // 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
if (Type != CAPTURE) if (Type != CAPTURE)
{ {
emptySquares = (Type == NON_CAPTURE ? target : pos.empty_squares()); emptySquares = (Type == NON_CAPTURE ? target : pos.empty_squares());
pawnPushes = move_pawns<TDELTA_N>(pawns & ~TRank7BB) & emptySquares;
}
if (Type == EVASION)
{
emptySquares &= target; // Only blocking squares
enemyPieces &= target; // Capture only the checker piece
}
// Promotions and underpromotions
if (pawnsOn7)
{
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 // Single and double pawn pushes
if (Type != CAPTURE) b1 = move_pawns<Us, DELTA_N>(pawns) & emptySquares & ~TRank8BB;
{ b2 = move_pawns<Us, DELTA_N>(b1 & TRank3BB) & emptySquares;
b1 = pawnPushes & emptySquares;
b2 = move_pawns<TDELTA_N>(pawnPushes & TRank3BB) & emptySquares;
if (Type == CHECK) // Filter out unwanted pushes according to the move type
if (Type == EVASION)
{ {
// Consider only pawn moves which give direct checks b1 &= target;
b2 &= target;
}
else if (Type == CHECK)
{
// Pawn moves which give direct cheks
b1 &= pos.attacks_from<PAWN>(ksq, Them); b1 &= pos.attacks_from<PAWN>(ksq, Them);
b2 &= pos.attacks_from<PAWN>(ksq, Them); b2 &= pos.attacks_from<PAWN>(ksq, Them);
// Add pawn moves which gives discovered check. This is possible only // Pawn moves which gives discovered check. This is possible only if
// if the pawn is not on the same file as the enemy king, because we // the pawn is not on the same file as the enemy king, because we
// don't generate captures. // don't generate captures.
if (pawns & target) // For CHECK type target is dc bitboard if (pawns & target) // For CHECK type target is dc bitboard
{ {
dc1 = move_pawns<TDELTA_N>(pawns & target & ~file_bb(ksq)) & emptySquares; Bitboard dc1 = move_pawns<Us, DELTA_N>(pawns & target & ~file_bb(ksq)) & emptySquares & ~TRank8BB;
dc2 = move_pawns<TDELTA_N>(dc1 & TRank3BB) & emptySquares; Bitboard dc2 = move_pawns<Us, DELTA_N>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1; b1 |= dc1;
b2 |= dc2; b2 |= dc2;
+1 -2
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -36,7 +36,6 @@ extern MoveStack* generate_captures(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist); extern MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist); extern MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_evasions(const Position& pos, MoveStack* mlist); extern MoveStack* generate_evasions(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_non_evasions(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLegal = false); extern MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLegal = false);
extern bool move_is_legal(const Position& pos, const Move m, Bitboard pinned); extern bool move_is_legal(const Position& pos, const Move m, Bitboard pinned);
extern bool move_is_legal(const Position& pos, const Move m); extern bool move_is_legal(const Position& pos, const Move m);
+50 -47
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -70,12 +70,12 @@ namespace {
/// search captures, promotions and some checks) and about how important good /// search captures, promotions and some checks) and about how important good
/// move ordering is at the current node. /// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
SearchStack* ss, Value beta) : pos(p), H(h) { const History& h, SearchStack* ss) : pos(p), H(h) {
int searchTT = ttm; int searchTT = ttm;
ttMoves[0].move = ttm; ttMoves[0].move = ttm;
badCaptureThreshold = 0; finished = false;
badCaptures = moves + MOVES_MAX; lastBadCapture = badCaptures;
pinned = p.pinned_pieces(pos.side_to_move()); pinned = p.pinned_pieces(pos.side_to_move());
@@ -90,29 +90,14 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
if (p.is_check()) if (p.is_check())
phasePtr = EvasionsPhaseTable; phasePtr = EvasionsPhaseTable;
else if (d > DEPTH_ZERO) else if (d > Depth(0))
{
// Consider sligtly negative captures as good if at low
// depth and far from beta.
if (ss && ss->eval < beta - PawnValueMidgame && d < 3 * ONE_PLY)
badCaptureThreshold = -PawnValueMidgame;
phasePtr = MainSearchPhaseTable; phasePtr = MainSearchPhaseTable;
} else if (d == Depth(0))
else if (d >= DEPTH_QS_CHECKS)
phasePtr = QsearchWithChecksPhaseTable; phasePtr = QsearchWithChecksPhaseTable;
else else
{
phasePtr = QsearchWithoutChecksPhaseTable; phasePtr = QsearchWithoutChecksPhaseTable;
// Skip TT move if is not a capture or a promotion, this avoids phasePtr += !searchTT - 1;
// qsearch tree explosion due to a possible perpetual check or
// similar rare cases when TT table is full.
if (ttm != MOVE_NONE && !pos.move_is_capture_or_promotion(ttm))
searchTT = ttMoves[0].move = MOVE_NONE;
}
phasePtr += int(!searchTT) - 1;
go_next_phase(); go_next_phase();
} }
@@ -144,14 +129,14 @@ void MovePicker::go_next_phase() {
case PH_NONCAPTURES: case PH_NONCAPTURES:
lastMove = generate_noncaptures(pos, moves); lastMove = generate_noncaptures(pos, moves);
score_noncaptures(); score_noncaptures();
sort_moves(moves, lastMove, &lastGoodNonCapture); sort_moves(moves, lastMove);
return; return;
case PH_BAD_CAPTURES: case PH_BAD_CAPTURES:
// Bad captures SEE value is already calculated so just pick // Bad captures SEE value is already calculated so just sort them
// them in order to get SEE move ordering. // to get SEE move ordering.
curMove = badCaptures; curMove = badCaptures;
lastMove = moves + MOVES_MAX; lastMove = lastBadCapture;
return; return;
case PH_EVASIONS: case PH_EVASIONS:
@@ -166,11 +151,12 @@ void MovePicker::go_next_phase() {
return; return;
case PH_QCHECKS: case PH_QCHECKS:
// Perhaps we should order moves move here? FIXME
lastMove = generate_non_capture_checks(pos, moves); lastMove = generate_non_capture_checks(pos, moves);
return; return;
case PH_STOP: case PH_STOP:
lastMove = curMove + 1; // Avoids another go_next_phase() call lastMove = curMove + 1; // hack to be friendly for get_next_move()
return; return;
default: default:
@@ -220,6 +206,7 @@ void MovePicker::score_noncaptures() {
Move m; Move m;
Piece piece; Piece piece;
Square from, to; Square from, to;
int hs;
for (MoveStack* cur = moves; cur != lastMove; cur++) for (MoveStack* cur = moves; cur != lastMove; cur++)
{ {
@@ -227,7 +214,14 @@ void MovePicker::score_noncaptures() {
from = move_from(m); from = move_from(m);
to = move_to(m); to = move_to(m);
piece = pos.piece_on(from); piece = pos.piece_on(from);
cur->score = H.value(piece, to) + H.gain(piece, to); hs = H.move_ordering_score(piece, to);
// Ensure history is always preferred to pst
if (hs > 0)
hs += 1000;
// pst based scoring
cur->score = hs + mg_value(pos.pst_delta(piece, from, to));
} }
} }
@@ -239,20 +233,16 @@ void MovePicker::score_evasions() {
Move m; Move m;
int seeScore; int seeScore;
// Skip if we don't have at least two moves to order
if (lastMove < moves + 2)
return;
for (MoveStack* cur = moves; cur != lastMove; cur++) for (MoveStack* cur = moves; cur != lastMove; cur++)
{ {
m = cur->move; m = cur->move;
if ((seeScore = pos.see_sign(m)) < 0) if ((seeScore = pos.see_sign(m)) < 0)
cur->score = seeScore - HistoryMax; // Be sure are at the bottom cur->score = seeScore;
else if (pos.move_is_capture(m)) else if (pos.move_is_capture(m))
cur->score = pos.midgame_value_of_piece_on(move_to(m)) cur->score = pos.midgame_value_of_piece_on(move_to(m))
- pos.type_of_piece_on(move_from(m)) + HistoryMax; - pos.type_of_piece_on(move_from(m)) + HistoryMax;
else else
cur->score = H.value(pos.piece_on(move_from(m)), move_to(m)); cur->score = H.move_ordering_score(pos.piece_on(move_from(m)), move_to(m));
} }
} }
@@ -261,8 +251,6 @@ void MovePicker::score_evasions() {
/// are no more moves left. /// are no more moves left.
/// It picks the move with the biggest score from a list of generated moves taking /// It picks the move with the biggest score from a list of generated moves taking
/// care not to return the tt move if has already been searched previously. /// care not to return the tt move if has already been searched previously.
/// Note that this function is not thread safe so should be lock protected by
/// caller when accessed through a shared MovePicker object.
Move MovePicker::get_next_move() { Move MovePicker::get_next_move() {
@@ -289,32 +277,29 @@ Move MovePicker::get_next_move() {
{ {
// Check for a non negative SEE now // Check for a non negative SEE now
int seeValue = pos.see_sign(move); int seeValue = pos.see_sign(move);
if (seeValue >= badCaptureThreshold) if (seeValue >= 0)
return move; return move;
// Losing capture, move it to the tail of the array, note // Losing capture, move it to the badCaptures[] array, note
// that move has now been already checked for legality. // that move has now been already checked for legality.
(--badCaptures)->move = move; assert(int(lastBadCapture - badCaptures) < 63);
badCaptures->score = seeValue; lastBadCapture->move = move;
lastBadCapture->score = seeValue;
lastBadCapture++;
} }
break; break;
case PH_KILLERS: case PH_KILLERS:
move = (curMove++)->move; move = (curMove++)->move;
if ( move != MOVE_NONE if ( move != MOVE_NONE
&& move_is_legal(pos, move, pinned)
&& move != ttMoves[0].move && move != ttMoves[0].move
&& move != ttMoves[1].move && move != ttMoves[1].move
&& move_is_legal(pos, move, pinned)
&& !pos.move_is_capture(move)) && !pos.move_is_capture(move))
return move; return move;
break; break;
case PH_NONCAPTURES: case PH_NONCAPTURES:
// Sort negative scored moves only when we get there
if (curMove == lastGoodNonCapture)
insertion_sort<MoveStack>(lastGoodNonCapture, lastMove);
move = (curMove++)->move; move = (curMove++)->move;
if ( move != ttMoves[0].move if ( move != ttMoves[0].move
&& move != ttMoves[1].move && move != ttMoves[1].move
@@ -355,3 +340,21 @@ Move MovePicker::get_next_move() {
} }
} }
/// A variant of get_next_move() which takes a lock as a parameter, used to
/// prevent multiple threads from picking the same move at a split point.
Move MovePicker::get_next_move(Lock &lock) {
lock_grab(&lock);
if (finished)
{
lock_release(&lock);
return MOVE_NONE;
}
Move m = get_next_move();
if (m == MOVE_NONE)
finished = true;
lock_release(&lock);
return m;
}
+9 -6
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
#include "depth.h" #include "depth.h"
#include "history.h" #include "history.h"
#include "lock.h"
#include "position.h" #include "position.h"
@@ -49,8 +50,9 @@ class MovePicker {
MovePicker& operator=(const MovePicker&); // silence a warning under MSVC MovePicker& operator=(const MovePicker&); // silence a warning under MSVC
public: public:
MovePicker(const Position& p, Move ttm, Depth d, const History& h, SearchStack* ss = NULL, Value beta = -VALUE_INFINITE); MovePicker(const Position& p, Move ttm, Depth d, const History& h, SearchStack* ss = NULL);
Move get_next_move(); Move get_next_move();
Move get_next_move(Lock& lock);
int number_of_evasions() const; int number_of_evasions() const;
private: private:
@@ -61,12 +63,13 @@ private:
const Position& pos; const Position& pos;
const History& H; const History& H;
Bitboard pinned;
MoveStack ttMoves[2], killers[2]; MoveStack ttMoves[2], killers[2];
int badCaptureThreshold, phase; bool finished;
int phase;
const uint8_t* phasePtr; const uint8_t* phasePtr;
MoveStack *curMove, *lastMove, *lastGoodNonCapture, *badCaptures; MoveStack *curMove, *lastMove, *lastBadCapture;
MoveStack moves[MOVES_MAX]; Bitboard pinned;
MoveStack moves[256], badCaptures[64];
}; };
+212 -82
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -40,26 +40,23 @@ namespace {
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
// Doubled pawn penalty by opposed flag and file // Doubled pawn penalty by file
const Score DoubledPawnPenalty[2][8] = { const Score DoubledPawnPenalty[8] = {
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48), 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(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 opposed flag and file // Isolated pawn penalty by file
const Score IsolatedPawnPenalty[2][8] = { const Score IsolatedPawnPenalty[8] = {
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52), S(25, 30), S(36, 35), S(40, 35), S(40, 35),
S(60, 52), S(60, 52), S(54, 52), S(37, 45) }, S(40, 35), S(40, 35), S(36, 35), S(25, 30)
{ 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 opposed flag and file // Backward pawn penalty by file
const Score BackwardPawnPenalty[2][8] = { const Score BackwardPawnPenalty[8] = {
{ S(30, 42), S(43, 46), S(49, 46), S(49, 46), S(20, 28), S(29, 31), S(33, 31), S(33, 31),
S(49, 46), S(49, 46), S(43, 46), S(30, 42) }, S(33, 31), S(33, 31), S(29, 31), S(20, 28)
{ 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 // Pawn chain membership bonus by file
const Score ChainBonus[8] = { const Score ChainBonus[8] = {
@@ -73,7 +70,36 @@ namespace {
S(34,68), S(83,166), S(0, 0), S( 0, 0) S(34,68), S(83,166), S(0, 0), S( 0, 0)
}; };
#undef S // Pawn storm tables for positions with opposite castling
const int QStormTable[64] = {
0, 0, 0, 0, 0, 0, 0, 0,
-22,-22,-22,-14,-6, 0, 0, 0,
-6,-10,-10,-10,-6, 0, 0, 0,
4, 12, 16, 12, 4, 0, 0, 0,
16, 23, 23, 16, 0, 0, 0, 0,
23, 31, 31, 23, 0, 0, 0, 0,
23, 31, 31, 23, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
const int KStormTable[64] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,-10,-19,-28,-33,-33,
0, 0, 0,-10,-15,-19,-24,-24,
0, 0, 0, 0, 1, 1, 1, 1,
0, 0, 0, 0, 1, 10, 19, 19,
0, 0, 0, 0, 1, 19, 31, 27,
0, 0, 0, 0, 0, 22, 31, 22,
0, 0, 0, 0, 0, 0, 0, 0
};
// Pawn storm open file bonuses by file
const int16_t KStormOpenFileBonus[8] = { 31, 31, 18, 0, 0, 0, 0, 0 };
const int16_t QStormOpenFileBonus[8] = { 0, 0, 0, 0, 0, 26, 42, 26 };
// Pawn storm lever bonuses by file
const int StormLeverBonus[8] = { -8, -8, -13, 0, 0, -13, -8, -8 };
} }
@@ -81,39 +107,49 @@ namespace {
//// Functions //// Functions
//// ////
/// PawnInfoTable c'tor and d'tor instantiated one each thread /// Constructor
PawnInfoTable::PawnInfoTable() { PawnInfoTable::PawnInfoTable(unsigned numOfEntries) {
entries = new PawnInfo[PawnTableSize];
size = numOfEntries;
entries = new PawnInfo[size];
if (!entries) if (!entries)
{ {
std::cerr << "Failed to allocate " << (PawnTableSize * sizeof(PawnInfo)) std::cerr << "Failed to allocate " << (numOfEntries * sizeof(PawnInfo))
<< " bytes for pawn hash table." << std::endl; << " bytes for pawn hash table." << std::endl;
exit(EXIT_FAILURE); Application::exit_with_failure();
} }
memset(entries, 0, PawnTableSize * sizeof(PawnInfo));
} }
/// Destructor
PawnInfoTable::~PawnInfoTable() { PawnInfoTable::~PawnInfoTable() {
delete [] entries; delete [] entries;
} }
/// PawnInfoTable::get_pawn_info() takes a position object as input, computes /// PawnInfo::clear() resets to zero the PawnInfo entry. Note that
/// a PawnInfo object, and returns a pointer to it. The result is also stored /// kingSquares[] is initialized to SQ_NONE instead.
/// in a hash table, so we don't have to recompute everything when the same
/// pawn structure occurs again.
PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const { void PawnInfo::clear() {
memset(this, 0, sizeof(PawnInfo));
kingSquares[WHITE] = kingSquares[BLACK] = SQ_NONE;
}
/// PawnInfoTable::get_pawn_info() takes a position object as input, computes
/// a PawnInfo object, and returns a pointer to it. The result is also
/// stored in a hash table, so we don't have to recompute everything when
/// the same pawn structure occurs again.
PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) {
assert(pos.is_ok()); assert(pos.is_ok());
Key key = pos.get_pawn_key(); Key key = pos.get_pawn_key();
unsigned index = unsigned(key & (PawnTableSize - 1)); int index = int(key & (size - 1));
PawnInfo* pi = entries + index; PawnInfo* pi = entries + index;
// If pi->key matches the position's pawn hash key, it means that we // If pi->key matches the position's pawn hash key, it means that we
@@ -123,20 +159,18 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const {
return pi; return pi;
// Clear the PawnInfo object, and set the key // Clear the PawnInfo object, and set the key
memset(pi, 0, sizeof(PawnInfo)); pi->clear();
pi->halfOpenFiles[WHITE] = pi->halfOpenFiles[BLACK] = 0xFF;
pi->kingSquares[WHITE] = pi->kingSquares[BLACK] = SQ_NONE;
pi->key = key; pi->key = key;
// Calculate pawn attacks // Calculate pawn attacks
Bitboard wPawns = pos.pieces(PAWN, WHITE); Bitboard whitePawns = pos.pieces(PAWN, WHITE);
Bitboard bPawns = pos.pieces(PAWN, BLACK); Bitboard blackPawns = pos.pieces(PAWN, BLACK);
pi->pawnAttacks[WHITE] = ((wPawns << 9) & ~FileABB) | ((wPawns << 7) & ~FileHBB); pi->pawnAttacks[WHITE] = ((whitePawns << 9) & ~FileABB) | ((whitePawns << 7) & ~FileHBB);
pi->pawnAttacks[BLACK] = ((bPawns >> 7) & ~FileABB) | ((bPawns >> 9) & ~FileHBB); pi->pawnAttacks[BLACK] = ((blackPawns >> 7) & ~FileABB) | ((blackPawns >> 9) & ~FileHBB);
// Evaluate pawns for both colors // Evaluate pawns for both colors
pi->value = evaluate_pawns<WHITE>(pos, wPawns, bPawns, pi) pi->value = evaluate_pawns<WHITE>(pos, whitePawns, blackPawns, pi)
- evaluate_pawns<BLACK>(pos, bPawns, wPawns, pi); - evaluate_pawns<BLACK>(pos, blackPawns, whitePawns, pi);
return pi; return pi;
} }
@@ -145,55 +179,123 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const {
template<Color Us> template<Color Us>
Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns, Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnInfo* pi) const { Bitboard theirPawns, PawnInfo* pi) {
Bitboard b;
Square s; Square s;
File f; File f;
Rank r; Rank r;
bool passed, isolated, doubled, opposed, chain, backward, candidate; bool passed, isolated, doubled, chain, backward, candidate;
Score value = SCORE_ZERO; int bonus;
const BitCountType Max15 = CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15; Score value = make_score(0, 0);
const Square* ptr = pos.piece_list_begin(Us, PAWN); const Square* ptr = pos.piece_list_begin(Us, PAWN);
// Initialize pawn storm scores by giving bonuses for open files
for (f = FILE_A; f <= FILE_H; f++)
if (!(ourPawns & file_bb(f)))
{
pi->ksStormValue[Us] += KStormOpenFileBonus[f];
pi->qsStormValue[Us] += QStormOpenFileBonus[f];
pi->halfOpenFiles[Us] |= (1 << f);
}
// Loop through all pawns of the current color and score each pawn // Loop through all pawns of the current color and score each pawn
while ((s = *ptr++) != SQ_NONE) while ((s = *ptr++) != SQ_NONE)
{ {
assert(pos.piece_on(s) == piece_of_color_and_type(Us, PAWN));
f = square_file(s); f = square_file(s);
r = square_rank(s); r = square_rank(s);
// This file cannot be half open assert(pos.piece_on(s) == piece_of_color_and_type(Us, PAWN));
pi->halfOpenFiles[Us] &= ~(1 << f);
// Our rank plus previous one. Used for chain detection. // Passed, isolated or doubled pawn?
b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1)); passed = Position::pawn_is_passed(theirPawns, Us, s);
isolated = Position::pawn_is_isolated(ourPawns, s);
doubled = Position::pawn_is_doubled(ourPawns, Us, s);
// Passed, isolated, doubled or member of a pawn // We calculate kingside and queenside pawn storm
// chain (but not the backward one) ? // scores for both colors. These are used when evaluating
passed = !(theirPawns & passed_pawn_mask(Us, s)); // middle game positions with opposite side castling.
doubled = ourPawns & squares_in_front_of(Us, s); //
opposed = theirPawns & squares_in_front_of(Us, s); // Each pawn is given a base score given by a piece square table
isolated = !(ourPawns & neighboring_files_bb(f)); // (KStormTable[] or QStormTable[]). Pawns which seem to have good
chain = ourPawns & neighboring_files_bb(f) & b; // chances of creating an open file by exchanging itself against an
// enemy pawn on an adjacent file gets an additional bonus.
// Kingside pawn storms
bonus = KStormTable[relative_square(Us, s)];
if (f >= FILE_F)
{
Bitboard b = outpost_mask(Us, s) & theirPawns & (FileFBB | FileGBB | FileHBB);
while (b)
{
Square s2 = pop_1st_bit(&b);
if (!(theirPawns & neighboring_files_bb(s2) & rank_bb(s2)))
{
// The enemy pawn has no pawn beside itself, which makes it
// particularly vulnerable. Big bonus, especially against a
// weakness on the rook file.
if (square_file(s2) == FILE_H)
bonus += 4*StormLeverBonus[f] - 8*square_distance(s, s2);
else
bonus += 2*StormLeverBonus[f] - 4*square_distance(s, s2);
} else
// There is at least one enemy pawn beside the enemy pawn we look
// at, which means that the pawn has somewhat better chances of
// defending itself by advancing. Smaller bonus.
bonus += StormLeverBonus[f] - 2*square_distance(s, s2);
}
}
pi->ksStormValue[Us] += bonus;
// Queenside pawn storms
bonus = QStormTable[relative_square(Us, s)];
if (f <= FILE_C)
{
Bitboard b = outpost_mask(Us, s) & theirPawns & (FileABB | FileBBB | FileCBB);
while (b)
{
Square s2 = pop_1st_bit(&b);
if (!(theirPawns & neighboring_files_bb(s2) & rank_bb(s2)))
{
// The enemy pawn has no pawn beside itself, which makes it
// particularly vulnerable. Big bonus, especially against a
// weakness on the rook file.
if (square_file(s2) == FILE_A)
bonus += 4*StormLeverBonus[f] - 16*square_distance(s, s2);
else
bonus += 2*StormLeverBonus[f] - 8*square_distance(s, s2);
} else
// There is at least one enemy pawn beside the enemy pawn we look
// at, which means that the pawn has somewhat better chances of
// defending itself by advancing. Smaller bonus.
bonus += StormLeverBonus[f] - 4*square_distance(s, s2);
}
}
pi->qsStormValue[Us] += bonus;
// Member of a pawn chain (but not the backward one)? We could speed up
// the test a little by introducing an array of masks indexed by color
// and square for doing the test, but because everything is hashed,
// it probably won't make any noticable difference.
chain = ourPawns
& neighboring_files_bb(f)
& (rank_bb(r) | rank_bb(r - (Us == WHITE ? 1 : -1)));
// Test for backward pawn // Test for backward pawn
// //
backward = false;
// If the pawn is passed, isolated, or member of a pawn chain // If the pawn is passed, isolated, or member of a pawn chain
// it cannot be backward. If can capture an enemy pawn or if // it cannot be backward. If can capture an enemy pawn or if
// there are friendly pawns behind on neighboring files it cannot // there are friendly pawns behind on neighboring files it cannot
// be backward either. // be backward either.
if ( !(passed | isolated | chain) if ( (passed | isolated | chain)
&& !(ourPawns & attack_span_mask(opposite_color(Us), s)) || (ourPawns & behind_bb(Us, r) & neighboring_files_bb(f))
&& !(pos.attacks_from<PAWN>(s, Us) & theirPawns)) || (pos.attacks_from<PAWN>(s, Us) & theirPawns))
backward = false;
else
{ {
// We now know that there are no friendly pawns beside or behind this // We now know that there are no friendly pawns beside or behind this
// pawn on neighboring files. We now check whether the pawn is // pawn on neighboring files. We now check whether the pawn is
// backward by looking in the forward direction on the neighboring // backward by looking in the forward direction on the neighboring
// files, and seeing whether we meet a friendly or an enemy pawn first. // files, and seeing whether we meet a friendly or an enemy pawn first.
b = pos.attacks_from<PAWN>(s, Us); Bitboard b = pos.attacks_from<PAWN>(s, Us);
// Note that we are sure to find something because pawn is not passed // Note that we are sure to find something because pawn is not passed
// nor isolated, so loop is potentially infinite, but it isn't. // nor isolated, so loop is potentially infinite, but it isn't.
@@ -205,34 +307,62 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns; backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns;
} }
assert(passed | opposed | (attack_span_mask(Us, s) & theirPawns));
// Test for candidate passed pawn // Test for candidate passed pawn
candidate = !(opposed | passed) candidate = !passed
&& (b = attack_span_mask(opposite_color(Us), s + pawn_push(Us)) & ourPawns) != EmptyBoardBB && !(theirPawns & file_bb(f))
&& count_1s<Max15>(b) >= count_1s<Max15>(attack_span_mask(Us, s) & theirPawns); && ( count_1s_max_15(neighboring_files_bb(f) & (behind_bb(Us, r) | rank_bb(r)) & ourPawns)
- count_1s_max_15(neighboring_files_bb(f) & in_front_bb(Us, r) & theirPawns)
>= 0);
// Mark the pawn as passed. Pawn will be properly scored in evaluation // In order to prevent doubled passed pawns from receiving a too big
// because we need full attack info to evaluate passed pawns. Only the // bonus, only the frontmost passed pawn on each file is considered as
// frontmost passed pawn on each file is considered a true passed pawn. // a true passed pawn.
if (passed && !doubled) if (passed && (ourPawns & squares_in_front_of(Us, s)))
set_bit(&(pi->passedPawns[Us]), s); passed = false;
// Score this pawn // Score this pawn
if (isolated) if (passed)
value -= IsolatedPawnPenalty[opposed][f]; set_bit(&(pi->passedPawns), s);
if (isolated)
{
value -= IsolatedPawnPenalty[f];
if (!(theirPawns & file_bb(f)))
value -= IsolatedPawnPenalty[f] / 2;
}
if (doubled) if (doubled)
value -= DoubledPawnPenalty[opposed][f]; value -= DoubledPawnPenalty[f];
if (backward) if (backward)
value -= BackwardPawnPenalty[opposed][f]; {
value -= BackwardPawnPenalty[f];
if (!(theirPawns & file_bb(f)))
value -= BackwardPawnPenalty[f] / 2;
}
if (chain) if (chain)
value += ChainBonus[f]; value += ChainBonus[f];
if (candidate) if (candidate)
value += CandidateBonus[relative_rank(Us, s)]; value += CandidateBonus[relative_rank(Us, s)];
} }
return value; return value;
} }
/// PawnInfo::updateShelter calculates and caches king shelter. It is called
/// only when king square changes, about 20% of total get_king_shelter() calls.
int PawnInfo::updateShelter(const Position& pos, Color c, Square ksq) {
unsigned shelter = 0;
Bitboard pawns = pos.pieces(PAWN, c) & this_and_neighboring_files_bb(ksq);
unsigned r = ksq & (7 << 3);
for (int i = 1, k = (c ? -8 : 8); i < 4; i++)
{
r += k;
shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (128 >> i);
}
kingSquares[c] = ksq;
kingShelters[c] = shelter;
return shelter;
}
+32 -60
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -26,47 +26,49 @@
//// ////
#include "bitboard.h" #include "bitboard.h"
#include "position.h"
#include "value.h" #include "value.h"
//// ////
//// Types //// Types
//// ////
const int PawnTableSize = 16384;
/// PawnInfo is a class which contains various information about a pawn /// PawnInfo is a class which contains various information about a pawn
/// structure. Currently, it only includes a middle game and an end game /// structure. Currently, it only includes a middle game and an end game
/// pawn structure evaluation, and a bitboard of passed pawns. We may want /// pawn structure evaluation, and a bitboard of passed pawns. We may want
/// to add further information in the future. A lookup to the pawn hash table /// to add further information in the future. A lookup to the pawn hash table
/// (performed by calling the get_pawn_info method in a PawnInfoTable object) /// (performed by calling the get_pawn_info method in a PawnInfoTable object)
/// returns a pointer to a PawnInfo object. /// returns a pointer to a PawnInfo object.
class Position;
class PawnInfo { class PawnInfo {
friend class PawnInfoTable; friend class PawnInfoTable;
public: public:
PawnInfo() { clear(); }
Score pawns_value() const; Score pawns_value() const;
Value kingside_storm_value(Color c) const;
Value queenside_storm_value(Color c) const;
Bitboard pawn_attacks(Color c) const; Bitboard pawn_attacks(Color c) const;
Bitboard passed_pawns(Color c) const; Bitboard passed_pawns() const;
int file_is_half_open(Color c, File f) const; int file_is_half_open(Color c, File f) const;
int has_open_file_to_left(Color c, File f) const; int has_open_file_to_left(Color c, File f) const;
int has_open_file_to_right(Color c, File f) const; int has_open_file_to_right(Color c, File f) const;
int get_king_shelter(const Position& pos, Color c, Square ksq);
template<Color Us>
Score king_shelter(const Position& pos, Square ksq);
private: private:
template<Color Us> void clear();
Score updateShelter(const Position& pos, Square ksq); int updateShelter(const Position& pos, Color c, Square ksq);
Key key; Key key;
Bitboard passedPawns[2]; Bitboard passedPawns;
Bitboard pawnAttacks[2]; Bitboard pawnAttacks[2];
Square kingSquares[2];
Score value; Score value;
int halfOpenFiles[2]; int16_t ksStormValue[2], qsStormValue[2];
Score kingShelters[2]; uint8_t halfOpenFiles[2];
Square kingSquares[2];
uint8_t kingShelters[2];
}; };
/// The PawnInfoTable class represents a pawn hash table. It is basically /// The PawnInfoTable class represents a pawn hash table. It is basically
@@ -76,21 +78,16 @@ private:
class PawnInfoTable { class PawnInfoTable {
enum SideType { KingSide, QueenSide };
PawnInfoTable(const PawnInfoTable&);
PawnInfoTable& operator=(const PawnInfoTable&);
public: public:
PawnInfoTable(); PawnInfoTable(unsigned numOfEntries);
~PawnInfoTable(); ~PawnInfoTable();
PawnInfo* get_pawn_info(const Position& pos) const; PawnInfo* get_pawn_info(const Position& pos);
void prefetch(Key key) const;
private: private:
template<Color Us> template<Color Us>
Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Bitboard theirPawns, PawnInfo* pi) const; Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Bitboard theirPawns, PawnInfo* pi);
unsigned size;
PawnInfo* entries; PawnInfo* entries;
}; };
@@ -99,23 +96,24 @@ private:
//// Inline functions //// Inline functions
//// ////
inline void PawnInfoTable::prefetch(Key key) const {
unsigned index = unsigned(key & (PawnTableSize - 1));
PawnInfo* pi = entries + index;
::prefetch((char*) pi);
}
inline Score PawnInfo::pawns_value() const { inline Score PawnInfo::pawns_value() const {
return value; return value;
} }
inline Bitboard PawnInfo::passed_pawns() const {
return passedPawns;
}
inline Bitboard PawnInfo::pawn_attacks(Color c) const { inline Bitboard PawnInfo::pawn_attacks(Color c) const {
return pawnAttacks[c]; return pawnAttacks[c];
} }
inline Bitboard PawnInfo::passed_pawns(Color c) const { inline Value PawnInfo::kingside_storm_value(Color c) const {
return passedPawns[c]; return Value(ksStormValue[c]);
}
inline Value PawnInfo::queenside_storm_value(Color c) const {
return Value(qsStormValue[c]);
} }
inline int PawnInfo::file_is_half_open(Color c, File f) const { inline int PawnInfo::file_is_half_open(Color c, File f) const {
@@ -130,34 +128,8 @@ inline int PawnInfo::has_open_file_to_right(Color c, File f) const {
return halfOpenFiles[c] & ~((1 << int(f+1)) - 1); return halfOpenFiles[c] & ~((1 << int(f+1)) - 1);
} }
/// PawnInfo::updateShelter() calculates and caches king shelter. It is called inline int PawnInfo::get_king_shelter(const Position& pos, Color c, Square ksq) {
/// only when king square changes, about 20% of total king_shelter() calls. return (kingSquares[c] == ksq ? kingShelters[c] : updateShelter(pos, c, ksq));
template<Color Us>
Score PawnInfo::updateShelter(const Position& pos, Square ksq) {
const int Shift = (Us == WHITE ? 8 : -8);
Bitboard pawns;
int r, shelter = 0;
if (relative_rank(Us, ksq) <= RANK_4)
{
pawns = pos.pieces(PAWN, Us) & this_and_neighboring_files_bb(ksq);
r = square_rank(ksq) * 8;
for (int i = 1; i < 4; i++)
{
r += Shift;
shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (128 >> i);
}
}
kingSquares[Us] = ksq;
kingShelters[Us] = make_score(shelter, 0);
return kingShelters[Us];
}
template<Color Us>
inline Score PawnInfo::king_shelter(const Position& pos, Square ksq) {
return kingSquares[Us] == ksq ? kingShelters[Us] : updateShelter<Us>(pos, ksq);
} }
#endif // !defined(PAWNS_H_INCLUDED) #endif // !defined(PAWNS_H_INCLUDED)
+49
View File
@@ -0,0 +1,49 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2009 Marco Costalba
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;
////
//// 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 + upcase * 7];
}
PieceType piece_type_from_char(char c) {
size_t idx = PieceChars.find(c);
return idx != string::npos ? PieceType(idx % 7) : NO_PIECE_TYPE;
}
+32 -36
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -25,11 +25,8 @@
//// Includes //// Includes
//// ////
#include <string>
#include "color.h" #include "color.h"
#include "square.h" #include "square.h"
#include "value.h"
//// ////
@@ -37,45 +34,39 @@
//// ////
enum PieceType { enum PieceType {
PIECE_TYPE_NONE = 0, NO_PIECE_TYPE = 0,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6 PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
}; };
enum Piece { enum Piece {
PIECE_NONE_DARK_SQ = 0, WP = 1, WN = 2, WB = 3, WR = 4, WQ = 5, WK = 6, NO_PIECE = 0, WP = 1, WN = 2, WB = 3, WR = 4, WQ = 5, WK = 6,
BP = 9, BN = 10, BB = 11, BR = 12, BQ = 13, BK = 14, PIECE_NONE = 16 BP = 9, BN = 10, BB = 11, BR = 12, BQ = 13, BK = 14,
EMPTY = 16, OUTSIDE = 17
}; };
ENABLE_OPERATORS_ON(PieceType)
ENABLE_OPERATORS_ON(Piece)
//// ////
//// Constants //// Constants
//// ////
/// Important: If the material values are changed, one must also const int SlidingArray[18] = {
/// adjust the piece square tables, and the method game_phase() in the 0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 0
/// Position class! };
///
/// Values modified by Joona Kiiski
const Value PawnValueMidgame = Value(0x0C6);
const Value PawnValueEndgame = Value(0x102);
const Value KnightValueMidgame = Value(0x331);
const Value KnightValueEndgame = Value(0x34E);
const Value BishopValueMidgame = Value(0x344);
const Value BishopValueEndgame = Value(0x359);
const Value RookValueMidgame = Value(0x4F6);
const Value RookValueEndgame = Value(0x4FE);
const Value QueenValueMidgame = Value(0x9D9);
const Value QueenValueEndgame = Value(0x9FE);
//// ////
//// Inline functions //// Inline functions
//// ////
inline Piece operator+ (Piece p, int i) { return Piece(int(p) + i); }
inline void operator++ (Piece &p, int) { p = Piece(int(p) + 1); }
inline Piece operator- (Piece p, int i) { return Piece(int(p) - i); }
inline void operator-- (Piece &p, int) { p = Piece(int(p) - 1); }
inline PieceType operator+ (PieceType p, int i) {return PieceType(int(p) + i);}
inline void operator++ (PieceType &p, int) { p = PieceType(int(p) + 1); }
inline PieceType operator- (PieceType p, int i) {return PieceType(int(p) - i);}
inline void operator-- (PieceType &p, int) { p = PieceType(int(p) - 1); }
inline PieceType type_of_piece(Piece p) { inline PieceType type_of_piece(Piece p) {
return PieceType(int(p) & 7); return PieceType(int(p) & 7);
} }
@@ -88,24 +79,29 @@ inline Piece piece_of_color_and_type(Color c, PieceType pt) {
return Piece((int(c) << 3) | int(pt)); return Piece((int(c) << 3) | int(pt));
} }
inline int piece_is_slider(Piece p) {
return SlidingArray[int(p)];
}
inline SquareDelta pawn_push(Color c) { inline SquareDelta pawn_push(Color c) {
return (c == WHITE ? DELTA_N : DELTA_S); return (c == WHITE ? DELTA_N : DELTA_S);
} }
inline bool piece_type_is_ok(PieceType pt) { inline bool piece_type_is_ok(PieceType pc) {
return pt >= PAWN && pt <= KING; return pc >= PAWN && pc <= KING;
} }
inline bool piece_is_ok(Piece p) { inline bool piece_is_ok(Piece pc) {
return piece_type_is_ok(type_of_piece(p)) && color_is_ok(color_of_piece(p)); return piece_type_is_ok(type_of_piece(pc)) && color_is_ok(color_of_piece(pc));
} }
inline char piece_type_to_char(PieceType pt) {
return std::string(" PNBRQK")[pt];
}
inline PieceType piece_type_from_char(char c) { ////
return PieceType(std::string(" PNBRQK").find(c)); //// Prototypes
} ////
extern char piece_type_to_char(PieceType pt, bool upcase = false);
extern PieceType piece_type_from_char(char c);
#endif // !defined(PIECE_H_INCLUDED) #endif // !defined(PIECE_H_INCLUDED)
+500 -504
View File
File diff suppressed because it is too large Load Diff
+67 -85
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,12 +21,25 @@
#if !defined(POSITION_H_INCLUDED) #if !defined(POSITION_H_INCLUDED)
#define 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 //// Includes
//// ////
#include "bitboard.h" #include "bitboard.h"
#include "color.h" #include "color.h"
#include "direction.h"
#include "move.h" #include "move.h"
#include "piece.h" #include "piece.h"
#include "square.h" #include "square.h"
@@ -37,6 +50,9 @@
//// Constants //// Constants
//// ////
/// FEN string for the initial position
const std::string StartPosition = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
/// Maximum number of plies per game (220 should be enough, because the /// Maximum number of plies per game (220 should be enough, because the
/// maximum search depth is 100, and during position setup we reset the /// maximum search depth is 100, and during position setup we reset the
/// move counter for every non-reversible move). /// move counter for every non-reversible move).
@@ -52,17 +68,17 @@ const int MaxGameLength = 220;
struct CheckInfo { struct CheckInfo {
explicit CheckInfo(const Position&); CheckInfo(const Position&);
Square ksq;
Bitboard dcCandidates; Bitboard dcCandidates;
Bitboard checkSq[8]; Bitboard checkSq[8];
Square ksq;
}; };
/// Castle rights, encoded as bit fields /// Castle rights, encoded as bit fields
enum CastleRights { enum CastleRights {
CASTLES_NONE = 0, NO_CASTLES = 0,
WHITE_OO = 1, WHITE_OO = 1,
BLACK_OO = 2, BLACK_OO = 2,
WHITE_OOO = 4, WHITE_OOO = 4,
@@ -84,13 +100,13 @@ enum Phase {
struct StateInfo { struct StateInfo {
Key pawnKey, materialKey; Key pawnKey, materialKey;
int castleRights, rule50, gamePly, pliesFromNull; int castleRights, rule50, pliesFromNull;
Square epSquare; Square epSquare;
Score value; Score value;
Value npMaterial[2]; Value npMaterial[2];
PieceType capturedType;
Key key; Key key;
PieceType capture;
Bitboard checkersBB; Bitboard checkersBB;
StateInfo* previous; StateInfo* previous;
}; };
@@ -123,9 +139,6 @@ class Position {
friend class MaterialInfo; friend class MaterialInfo;
friend class EndgameFunctions; friend class EndgameFunctions;
Position(); // No default or copy c'tor allowed
Position(const Position& pos);
public: public:
enum GamePhase { enum GamePhase {
MidGame, MidGame,
@@ -133,15 +146,17 @@ public:
}; };
// Constructors // Constructors
Position(const Position& pos, int threadID); Position() {}
Position(const std::string& fen, bool isChess960, int threadID); Position(const Position& pos);
Position(const std::string& fen);
// Text input/output // Text input/output
void from_fen(const std::string& fen, bool isChess960); void from_fen(const std::string& fen);
const std::string to_fen() const; const std::string to_fen() const;
void print(Move m = MOVE_NONE) const; void print(Move m = MOVE_NONE) const;
// Copying // Copying
void copy(const Position& pos);
void flipped_copy(const Position& pos); void flipped_copy(const Position& pos);
// The piece on a given square // The piece on a given square
@@ -196,7 +211,6 @@ public:
// Information about attacks to or from a given square // Information about attacks to or from a given square
Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s) const;
Bitboard attacks_from(Piece p, 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) const;
template<PieceType> Bitboard attacks_from(Square s, Color c) const; template<PieceType> Bitboard attacks_from(Square s, Color c) const;
@@ -210,17 +224,17 @@ public:
bool move_is_passed_pawn_push(Move m) const; bool move_is_passed_pawn_push(Move m) const;
bool move_attacks_square(Move m, Square s) const; bool move_attacks_square(Move m, Square s) const;
// Piece captured with previous moves
PieceType captured_piece_type() const;
// Information about pawns // Information about pawns
bool pawn_is_passed(Color c, Square s) const; bool pawn_is_passed(Color c, Square s) const;
static bool pawn_is_passed(Bitboard theirPawns, Color c, Square s);
static bool pawn_is_isolated(Bitboard ourPawns, Square s);
static bool pawn_is_doubled(Bitboard ourPawns, Color c, Square s);
// Weak squares // Weak squares
bool square_is_weak(Square s, Color c) const; bool square_is_weak(Square s, Color c) const;
// Doing and undoing moves // Doing and undoing moves
void detach(); void saveState();
void do_move(Move m, StateInfo& st); void do_move(Move m, StateInfo& st);
void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck); void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck);
void undo_move(Move m); void undo_move(Move m);
@@ -230,6 +244,7 @@ public:
// Static exchange evaluation // Static exchange evaluation
int see(Square from, Square to) const; int see(Square from, Square to) const;
int see(Move m) const; int see(Move m) const;
int see(Square to) const;
int see_sign(Move m) const; int see_sign(Move m) const;
// Accessing hash keys // Accessing hash keys
@@ -241,33 +256,24 @@ public:
// Incremental evaluation // Incremental evaluation
Score value() const; Score value() const;
Value non_pawn_material(Color c) const; Value non_pawn_material(Color c) const;
static Score pst_delta(Piece piece, Square from, Square to); Score pst_delta(Piece piece, Square from, Square to) const;
// Game termination checks // Game termination checks
bool is_mate() const; bool is_mate() const;
bool is_draw() const; bool is_draw() const;
// Check if side to move could be mated in one // Check if one side threatens a mate in one
bool has_mate_threat(); bool has_mate_threat(Color c);
// Number of plies since the last non-reversible move // Number of plies since the last non-reversible move
int rule_50_counter() const; int rule_50_counter() const;
int startpos_ply_counter() const;
// Other properties of the position // Other properties of the position
bool opposite_colored_bishops() const; bool opposite_colored_bishops() const;
bool has_pawn_on_7th(Color c) const; bool has_pawn_on_7th(Color c) const;
bool is_chess960() const;
// Current thread ID searching on the position
int thread() const;
// Reset the gamePly variable to 0 // Reset the gamePly variable to 0
void reset_game_ply(); 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 // Position consistency check, for debugging
bool is_ok(int* failedStep = NULL) const; bool is_ok(int* failedStep = NULL) const;
@@ -281,12 +287,11 @@ private:
// Initialization helper functions (used while setting up a position) // Initialization helper functions (used while setting up a position)
void clear(); void clear();
void put_piece(Piece p, Square s); void put_piece(Piece p, Square s);
void do_allow_oo(Color c); void allow_oo(Color c);
void do_allow_ooo(Color c); void allow_ooo(Color c);
bool set_castling_rights(char token);
// Helper functions for doing and undoing moves // Helper functions for doing and undoing moves
void do_capture_move(Key& key, PieceType capture, Color them, Square to, bool ep); void do_capture_move(Bitboard& key, PieceType capture, Color them, Square to, bool ep);
void do_castle_move(Move m); void do_castle_move(Move m);
void undo_castle_move(Move m); void undo_castle_move(Move m);
void find_checkers(); void find_checkers();
@@ -300,7 +305,7 @@ private:
Key compute_material_key() const; Key compute_material_key() const;
// Computing incremental evaluation scores and material counts // Computing incremental evaluation scores and material counts
static Score pst(Color c, PieceType pt, Square s); Score pst(Color c, PieceType pt, Square s) const;
Score compute_value() const; Score compute_value() const;
Value compute_non_pawn_material(Color c) const; Value compute_non_pawn_material(Color c) const;
@@ -319,26 +324,21 @@ private:
// Other info // Other info
Color sideToMove; Color sideToMove;
int gamePly;
Key history[MaxGameLength]; Key history[MaxGameLength];
int castleRightsMask[64];
StateInfo startState;
File initialKFile, initialKRFile, initialQRFile; File initialKFile, initialKRFile, initialQRFile;
bool isChess960; StateInfo startState;
int startPosPlyCounter;
int threadID;
int64_t nodes;
StateInfo* st; StateInfo* st;
// Static variables // Static variables
static int castleRightsMask[64];
static Key zobrist[2][8][64]; static Key zobrist[2][8][64];
static Key zobEp[64]; static Key zobEp[64];
static Key zobCastle[16]; static Key zobCastle[16];
static Key zobMaterial[2][8][16];
static Key zobSideToMove; static Key zobSideToMove;
static Score PieceSquareTable[16][64]; static Score PieceSquareTable[16][64];
static Key zobExclusion; static Key zobExclusion;
static const Value seeValues[8];
static const Value PieceValueMidgame[17];
static const Value PieceValueEndgame[17];
}; };
@@ -346,14 +346,6 @@ private:
//// Inline functions //// 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 { inline Piece Position::piece_on(Square s) const {
return board[s]; return board[s];
} }
@@ -367,7 +359,7 @@ inline PieceType Position::type_of_piece_on(Square s) const {
} }
inline bool Position::square_is_empty(Square s) const { inline bool Position::square_is_empty(Square s) const {
return piece_on(s) == PIECE_NONE; return piece_on(s) == EMPTY;
} }
inline bool Position::square_is_occupied(Square s) const { inline bool Position::square_is_occupied(Square s) const {
@@ -375,11 +367,11 @@ inline bool Position::square_is_occupied(Square s) const {
} }
inline Value Position::midgame_value_of_piece_on(Square s) const { inline Value Position::midgame_value_of_piece_on(Square s) const {
return PieceValueMidgame[piece_on(s)]; return piece_value_midgame(piece_on(s));
} }
inline Value Position::endgame_value_of_piece_on(Square s) const { inline Value Position::endgame_value_of_piece_on(Square s) const {
return PieceValueEndgame[piece_on(s)]; return piece_value_endgame(piece_on(s));
} }
inline Color Position::side_to_move() const { inline Color Position::side_to_move() const {
@@ -391,7 +383,7 @@ inline Bitboard Position::occupied_squares() const {
} }
inline Bitboard Position::empty_squares() const { inline Bitboard Position::empty_squares() const {
return ~occupied_squares(); return ~(occupied_squares());
} }
inline Bitboard Position::pieces_of_color(Color c) const { inline Bitboard Position::pieces_of_color(Color c) const {
@@ -418,8 +410,8 @@ inline int Position::piece_count(Color c, PieceType pt) const {
return pieceCount[c][pt]; return pieceCount[c][pt];
} }
inline Square Position::piece_list(Color c, PieceType pt, int idx) const { inline Square Position::piece_list(Color c, PieceType pt, int index) const {
return pieceList[c][pt][idx]; return pieceList[c][pt][index];
} }
inline const Square* Position::piece_list_begin(Color c, PieceType pt) const { inline const Square* Position::piece_list_begin(Color c, PieceType pt) const {
@@ -491,8 +483,20 @@ inline bool Position::pawn_is_passed(Color c, Square s) const {
return !(pieces(PAWN, opposite_color(c)) & passed_pawn_mask(c, s)); return !(pieces(PAWN, opposite_color(c)) & passed_pawn_mask(c, s));
} }
inline bool Position::pawn_is_passed(Bitboard theirPawns, Color c, Square s) {
return !(theirPawns & passed_pawn_mask(c, s));
}
inline bool Position::pawn_is_isolated(Bitboard ourPawns, Square s) {
return !(ourPawns & neighboring_files_bb(s));
}
inline bool Position::pawn_is_doubled(Bitboard ourPawns, Color c, Square s) {
return ourPawns & squares_behind(c, s);
}
inline bool Position::square_is_weak(Square s, Color c) const { inline bool Position::square_is_weak(Square s, Color c) const {
return !(pieces(PAWN, opposite_color(c)) & attack_span_mask(c, s)); return !(pieces(PAWN, c) & outpost_mask(opposite_color(c), s));
} }
inline Key Position::get_key() const { inline Key Position::get_key() const {
@@ -511,11 +515,11 @@ inline Key Position::get_material_key() const {
return st->materialKey; return st->materialKey;
} }
inline Score Position::pst(Color c, PieceType pt, Square s) { inline Score Position::pst(Color c, PieceType pt, Square s) const {
return PieceSquareTable[piece_of_color_and_type(c, pt)][s]; return PieceSquareTable[piece_of_color_and_type(c, pt)][s];
} }
inline Score Position::pst_delta(Piece piece, Square from, Square to) { inline Score Position::pst_delta(Piece piece, Square from, Square to) const {
return PieceSquareTable[piece][to] - PieceSquareTable[piece][from]; return PieceSquareTable[piece][to] - PieceSquareTable[piece][from];
} }
@@ -535,26 +539,20 @@ inline bool Position::move_is_passed_pawn_push(Move m) const {
} }
inline int Position::rule_50_counter() const { inline int Position::rule_50_counter() const {
return st->rule50;
}
inline int Position::startpos_ply_counter() const { return st->rule50;
return startPosPlyCounter;
} }
inline bool Position::opposite_colored_bishops() const { inline bool Position::opposite_colored_bishops() const {
return piece_count(WHITE, BISHOP) == 1 return piece_count(WHITE, BISHOP) == 1
&& piece_count(BLACK, BISHOP) == 1 && piece_count(BLACK, BISHOP) == 1
&& !same_color_squares(piece_list(WHITE, BISHOP, 0), piece_list(BLACK, BISHOP, 0)); && square_color(piece_list(WHITE, BISHOP, 0)) != square_color(piece_list(BLACK, BISHOP, 0));
} }
inline bool Position::has_pawn_on_7th(Color c) 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 pieces(PAWN, c) & relative_rank_bb(c, RANK_7);
return isChess960;
} }
inline bool Position::move_is_capture(Move m) const { inline bool Position::move_is_capture(Move m) const {
@@ -569,20 +567,4 @@ inline bool Position::move_is_capture_or_promotion(Move m) const {
return (m & (0x1F << 12)) ? !move_is_castle(m) : !square_is_empty(move_to(m)); return (m & (0x1F << 12)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
} }
inline PieceType Position::captured_piece_type() const {
return st->capturedType;
}
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) #endif // !defined(POSITION_H_INCLUDED)
+14 -16
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -27,25 +27,18 @@
#include "value.h" #include "value.h"
namespace {
//// ////
//// Constants modified by Joona Kiiski //// Constants modified by Joona Kiiski
//// ////
const Value MP = PawnValueMidgame; static const Value MP = PawnValueMidgame;
const Value MK = KnightValueMidgame; static const Value MK = KnightValueMidgame;
const Value MB = BishopValueMidgame; static const Value MB = BishopValueMidgame;
const Value MR = RookValueMidgame; static const Value MR = RookValueMidgame;
const Value MQ = QueenValueMidgame; static const Value MQ = QueenValueMidgame;
const Value EP = PawnValueEndgame; static const int MgPST[][64] = {
const Value EK = KnightValueEndgame;
const Value EB = BishopValueEndgame;
const Value ER = RookValueEndgame;
const Value EQ = QueenValueEndgame;
const int MgPST[][64] = {
{ }, { },
{// Pawn {// Pawn
// A B C D E F G H // A B C D E F G H
@@ -115,7 +108,13 @@ const int MgPST[][64] = {
} }
}; };
const int EgPST[][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] = {
{ }, { },
{// Pawn {// Pawn
// A B C D E F G H // A B C D E F G H
@@ -185,6 +184,5 @@ const int EgPST[][64] = {
} }
}; };
} // namespace
#endif // !defined(PSQTAB_H_INCLUDED) #endif // !defined(PSQTAB_H_INCLUDED)
-84
View File
@@ -1,84 +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/>.
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)
+106 -109
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -28,7 +28,8 @@
#include <string> #include <string>
#include <sstream> #include <sstream>
#include "movegen.h" #include "history.h"
#include "movepick.h"
#include "san.h" #include "san.h"
using std::string; using std::string;
@@ -40,9 +41,14 @@ using std::string;
namespace { namespace {
enum Ambiguity { 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); Ambiguity move_ambiguity(const Position& pos, Move m);
const string time_string(int milliseconds); const string time_string(int milliseconds);
const string score_string(Value v); const string score_string(Value v);
@@ -57,32 +63,33 @@ namespace {
/// that the move is a legal move from the position. The return value is /// that the move is a legal move from the position. The return value is
/// a string containing the move in short algebraic notation. /// a string containing the move in short algebraic notation.
const string move_to_san(Position& pos, Move m) { const string move_to_san(const Position& pos, Move m) {
assert(pos.is_ok()); assert(pos.is_ok());
assert(move_is_ok(m)); assert(move_is_ok(m));
string san; Square from, to;
Square from = move_from(m); PieceType pt;
Square to = move_to(m);
PieceType pt = type_of_piece(pos.piece_on(from)); from = move_from(m);
to = move_to(m);
pt = type_of_piece(pos.piece_on(move_from(m)));
string san = "";
if (m == MOVE_NONE) if (m == MOVE_NONE)
return "(none)"; return "(none)";
else if (m == MOVE_NULL)
if (m == MOVE_NULL)
return "(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"; san = "O-O-O";
else if (move_is_short_castle(m)) else if (move_is_short_castle(m) || (int(to - from) == 2 && pt == KING))
san = "O-O"; san = "O-O";
else else
{ {
if (pt != PAWN) if (pt != PAWN)
{ {
san += piece_type_to_char(pt); san += piece_type_to_char(pt, true);
switch (move_ambiguity(pos, m)) { switch (move_ambiguity(pos, m)) {
case AMBIGUITY_NONE: case AMBIGUITY_NONE:
break; break;
@@ -99,30 +106,27 @@ const string move_to_san(Position& pos, Move m) {
assert(false); assert(false);
} }
} }
if (pos.move_is_capture(m)) if (pos.move_is_capture(m))
{ {
if (pt == PAWN) if (pt == PAWN)
san += file_to_char(square_file(from)); san += file_to_char(square_file(move_from(m)));
san += "x";
san += 'x';
} }
san += square_to_string(to); san += square_to_string(move_to(m));
if (move_is_promotion(m)) if (move_is_promotion(m))
{ {
san += '='; san += '=';
san += piece_type_to_char(move_promotion_piece(m)); san += piece_type_to_char(move_promotion_piece(m), true);
} }
} }
// Is the move check? We don't use pos.move_is_check(m) here, because
// The move gives check ? We don't use pos.move_is_check() here // Position::move_is_check doesn't detect all checks (not castling moves,
// because we need to test for mate after the move is done. // promotions and en passant captures).
StateInfo st; StateInfo st;
pos.do_move(m, st); Position p(pos);
if (pos.is_check()) p.do_move(m, st);
san += pos.is_mate() ? "#" : "+"; if (p.is_check())
pos.undo_move(m); san += p.is_mate()? "#" : "+";
return san; return san;
} }
@@ -137,43 +141,41 @@ Move move_from_san(const Position& pos, const string& movestr) {
assert(pos.is_ok()); assert(pos.is_ok());
enum { START, TO_FILE, TO_RANK, PROMOTION_OR_CHECK, PROMOTION, CHECK, END }; MovePicker mp = MovePicker(pos, MOVE_NONE, OnePly, H);
static const string pieceLetters = "KQRBN"; Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
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 // Castling moves
if (movestr == "O-O-O" || movestr == "O-O-O+") if (movestr == "O-O-O" || movestr == "O-O-O+")
{ {
for (MoveStack* cur = mlist; cur != last; cur++) Move m;
if (move_is_long_castle(cur->move)) while ((m = mp.get_next_move()) != MOVE_NONE)
return cur->move; if (move_is_long_castle(m) && pos.pl_move_is_legal(m, pinned))
return m;
return MOVE_NONE; return MOVE_NONE;
} }
else if (movestr == "O-O" || movestr == "O-O+") else if (movestr == "O-O" || movestr == "O-O+")
{ {
for (MoveStack* cur = mlist; cur != last; cur++) Move m;
if (move_is_short_castle(cur->move)) while ((m = mp.get_next_move()) != MOVE_NONE)
return cur->move; if (move_is_short_castle(m) && pos.pl_move_is_legal(m, pinned))
return m;
return MOVE_NONE; return MOVE_NONE;
} }
// Normal moves. We use a simple FSM to parse the san string // 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 = NO_PIECE_TYPE, promotion = NO_PIECE_TYPE;
File fromFile = FILE_NONE, toFile = FILE_NONE;
Rank fromRank = RANK_NONE, toRank = RANK_NONE;
Square to;
int state = START;
for (size_t i = 0; i < movestr.length(); i++) for (size_t i = 0; i < movestr.length(); i++)
{ {
char type, c = movestr[i]; char type, c = movestr[i];
if (pieceLetters.find(c) != string::npos) if (pieceLetters.find(c) != string::npos)
type = 'P'; type = 'P';
else if (c >= 'a' && c <= 'h') else if (c >= 'a' && c <= 'h')
@@ -193,7 +195,7 @@ Move move_from_san(const Position& pos, const string& movestr) {
else if (state == PROMOTION) else if (state == PROMOTION)
{ {
promotion = piece_type_from_char(c); promotion = piece_type_from_char(c);
state = (i < movestr.length() - 1 ? CHECK : END); state = (i < movestr.length() - 1) ? CHECK : END;
} }
else else
return MOVE_NONE; return MOVE_NONE;
@@ -233,8 +235,7 @@ Move move_from_san(const Position& pos, const string& movestr) {
else else
return MOVE_NONE; return MOVE_NONE;
break; break;
case 'x': case 'x': case 'X':
case 'X':
if (state == TO_RANK) if (state == TO_RANK)
{ {
// Previous file was for disambiguation, or it's a pawn capture // Previous file was for disambiguation, or it's a pawn capture
@@ -250,8 +251,7 @@ Move move_from_san(const Position& pos, const string& movestr) {
else else
return MOVE_NONE; return MOVE_NONE;
break; break;
case '+': case '+': case '#':
case '#':
if (state == PROMOTION_OR_CHECK || state == CHECK) if (state == PROMOTION_OR_CHECK || state == CHECK)
state = END; state = END;
else else
@@ -266,25 +266,22 @@ Move move_from_san(const Position& pos, const string& movestr) {
if (state != END) if (state != END)
return MOVE_NONE; return MOVE_NONE;
// Look for an unambiguous matching move // Look for a matching move
Move m, move = MOVE_NONE;
to = make_square(toFile, toRank); to = make_square(toFile, toRank);
matches = 0; int matches = 0;
for (MoveStack* cur = mlist; cur != last; cur++) 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))))
{ {
from = move_from(cur->move); move = m;
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++; matches++;
} }
} return (matches == 1 ? move : MOVE_NONE);
return matches == 1 ? move : MOVE_NONE;
} }
@@ -302,23 +299,23 @@ const string line_to_san(const Position& pos, Move line[], int startColumn, bool
string moveStr; string moveStr;
size_t length = 0; size_t length = 0;
size_t maxLength = 80 - startColumn; size_t maxLength = 80 - startColumn;
Position p(pos, pos.thread()); Position p(pos);
for (Move* m = line; *m != MOVE_NONE; m++) for (int i = 0; line[i] != MOVE_NONE; i++)
{ {
moveStr = move_to_san(p, *m); moveStr = move_to_san(p, line[i]);
length += moveStr.length() + 1; length += moveStr.length() + 1;
if (breakLines && length > maxLength) if (breakLines && length > maxLength)
{ {
s << "\n" << std::setw(startColumn) << " "; s << '\n' << std::setw(startColumn) << ' ';
length = moveStr.length() + 1; length = moveStr.length() + 1;
} }
s << moveStr << ' '; s << moveStr << ' ';
if (*m == MOVE_NULL) if (line[i] == MOVE_NULL)
p.do_null_move(st); p.do_null_move(st);
else else
p.do_move(*m, st); p.do_move(line[i], st);
} }
return s.str(); return s.str();
} }
@@ -329,30 +326,26 @@ const string line_to_san(const Position& pos, Move line[], int startColumn, bool
/// when the UCI parameter "Use Search Log" is "true"). /// when the UCI parameter "Use Search Log" is "true").
const string pretty_pv(const Position& pos, int time, int depth, const string pretty_pv(const Position& pos, int time, int depth,
Value score, ValueType type, Move pv[]) { uint64_t nodes, Value score, ValueType type, Move pv[]) {
const int64_t K = 1000;
const int64_t M = 1000000;
std::stringstream s; std::stringstream s;
// Depth // Depth
s << std::setw(2) << depth << " "; s << std::setw(2) << depth << " ";
// Score // Score
s << (type == VALUE_TYPE_LOWER ? ">" : type == VALUE_TYPE_UPPER ? "<" : " ") s << ((type == VALUE_TYPE_LOWER)? ">" : ((type == VALUE_TYPE_UPPER)? "<" : " "));
<< std::setw(7) << score_string(score); s << std::setw(7) << score_string(score);
// Time // Time
s << std::setw(8) << time_string(time) << " "; s << std::setw(8) << time_string(time) << " ";
// Nodes // Nodes
if (pos.nodes_searched() < M) if (nodes < 1000000ULL)
s << std::setw(8) << pos.nodes_searched() / 1 << " "; s << std::setw(8) << nodes << " ";
else if (pos.nodes_searched() < K * M) else if (nodes < 1000000000ULL)
s << std::setw(7) << pos.nodes_searched() / K << "K "; s << std::setw(7) << nodes/1000ULL << 'k' << " ";
else else
s << std::setw(7) << pos.nodes_searched() / M << "M "; s << std::setw(7) << nodes/1000000ULL << 'M' << " ";
// PV // PV
s << line_to_san(pos, pv, 30, true); s << line_to_san(pos, pv, 30, true);
@@ -365,50 +358,54 @@ namespace {
Ambiguity move_ambiguity(const Position& pos, Move m) { Ambiguity move_ambiguity(const Position& pos, Move m) {
MoveStack mlist[MOVES_MAX], *last;
Move candidates[8];
Square from = move_from(m); Square from = move_from(m);
Square to = move_to(m); Square to = move_to(m);
Piece pc = pos.piece_on(from); Piece pc = pos.piece_on(from);
int matches = 0, f = 0, r = 0;
// If there is only one piece 'pc' then move cannot be ambiguous // King moves are never ambiguous, because there is never two kings of
if (pos.piece_count(pos.side_to_move(), type_of_piece(pc)) == 1) // the same color.
if (type_of_piece(pc) == KING)
return AMBIGUITY_NONE; return AMBIGUITY_NONE;
// Collect all legal moves of piece 'pc' with destination 'to' MovePicker mp = MovePicker(pos, MOVE_NONE, OnePly, H);
last = generate_moves(pos, mlist); Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
for (MoveStack* cur = mlist; cur != last; cur++) Move mv, moveList[8];
if (move_to(cur->move) == to && pos.piece_on(move_from(cur->move)) == pc)
candidates[matches++] = cur->move;
if (matches == 1) 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)
return AMBIGUITY_NONE; return AMBIGUITY_NONE;
for (int i = 0; i < matches; i++) int f = 0, r = 0;
for (int i = 0; i < n; i++)
{ {
if (square_file(move_from(candidates[i])) == square_file(from)) if (square_file(move_from(moveList[i])) == square_file(from))
f++; f++;
if (square_rank(move_from(candidates[i])) == square_rank(from)) if (square_rank(move_from(moveList[i])) == square_rank(from))
r++; r++;
} }
if (f == 1)
return AMBIGUITY_FILE;
return f == 1 ? AMBIGUITY_FILE : r == 1 ? AMBIGUITY_RANK : AMBIGUITY_BOTH; if (r == 1)
return AMBIGUITY_RANK;
return AMBIGUITY_BOTH;
} }
const string time_string(int millisecs) { const string time_string(int milliseconds) {
const int MSecMinute = 1000 * 60;
const int MSecHour = 1000 * 60 * 60;
std::stringstream s; std::stringstream s;
s << std::setfill('0'); s << std::setfill('0');
int hours = millisecs / MSecHour; int hours = milliseconds / (1000*60*60);
int minutes = (millisecs - hours * MSecHour) / MSecMinute; int minutes = (milliseconds - hours*1000*60*60) / (1000*60);
int seconds = (millisecs - hours * MSecHour - minutes * MSecMinute) / 1000; int seconds = (milliseconds - hours*1000*60*60 - minutes*1000*60) / 1000;
if (hours) if (hours)
s << hours << ':'; s << hours << ':';
+3 -3
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -36,9 +36,9 @@
//// Prototypes //// Prototypes
//// ////
extern const std::string move_to_san(Position& pos, Move m); extern const std::string move_to_san(const Position& pos, Move m);
extern Move move_from_san(const Position& pos, const std::string& str); 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 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, Value score, ValueType type, Move pv[]); extern const std::string pretty_pv(const Position& pos, int time, int depth, uint64_t nodes, Value score, ValueType type, Move pv[]);
#endif // !defined(SAN_H_INCLUDED) #endif // !defined(SAN_H_INCLUDED)
+52
View File
@@ -0,0 +1,52 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2009 Marco Costalba
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(SCALE_H_INCLUDED)
#define SCALE_H_INCLUDED
////
//// Includes
////
#include "value.h"
////
//// Types
////
enum ScaleFactor {
SCALE_FACTOR_ZERO = 0,
SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128,
SCALE_FACTOR_NONE = 255
};
////
//// Inline functions
////
inline Value apply_scale_factor(Value v, ScaleFactor f) {
return Value((v * f) / int(SCALE_FACTOR_NORMAL));
}
#endif // !defined(SCALE_H_INCLUDED)
+2281 -1901
View File
File diff suppressed because it is too large Load Diff
+15 -16
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -27,7 +27,6 @@
#include "depth.h" #include "depth.h"
#include "move.h" #include "move.h"
#include "value.h"
//// ////
@@ -35,7 +34,8 @@
//// ////
const int PLY_MAX = 100; const int PLY_MAX = 100;
const int PLY_MAX_PLUS_2 = PLY_MAX + 2; const int PLY_MAX_PLUS_2 = 102;
const int KILLER_MAX = 2;
//// ////
@@ -46,20 +46,17 @@ const int PLY_MAX_PLUS_2 = PLY_MAX + 2;
/// from nodes shallower and deeper in the tree during the search. Each /// from nodes shallower and deeper in the tree during the search. Each
/// search thread has its own array of SearchStack objects, indexed by the /// search thread has its own array of SearchStack objects, indexed by the
/// current ply. /// current ply.
struct EvalInfo;
struct SplitPoint;
struct SearchStack { struct SearchStack {
Move pv[PLY_MAX_PLUS_2];
Move currentMove; Move currentMove;
Move mateKiller; Move mateKiller;
Move excludedMove; Move threatMove;
Move bestMove; Move killers[KILLER_MAX];
Move killers[2];
Depth reduction; Depth reduction;
Value eval;
Value evalMargin; void init(int ply);
bool skipNullMove; void initKillers();
SplitPoint* sp;
}; };
@@ -67,11 +64,13 @@ struct SearchStack {
//// Prototypes //// Prototypes
//// ////
extern void init_search();
extern void init_threads(); extern void init_threads();
extern void exit_threads(); extern void stop_threads();
extern bool think(const Position &pos, bool infinite, bool ponder, int side_to_move,
int time[], int increment[], int movesToGo, int maxDepth,
int maxNodes, int maxTime, Move searchMoves[]);
extern int perft(Position &pos, Depth depth); extern int perft(Position &pos, Depth depth);
extern bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[], extern int64_t nodes_searched();
int movesToGo, int maxDepth, int maxNodes, int maxTime, Move searchMoves[]);
#endif // !defined(SEARCH_H_INCLUDED) #endif // !defined(SEARCH_H_INCLUDED)
+48 -30
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -57,38 +57,55 @@ enum Rank {
}; };
enum SquareDelta { enum SquareDelta {
DELTA_SSW = -021, DELTA_SS = -020, DELTA_SSE = -017, DELTA_SWW = -012,
DELTA_N = 8, DELTA_E = 1, DELTA_S = -8, DELTA_W = -1, DELTA_NONE = 0, 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_NN = DELTA_N + DELTA_N, DELTA_N = 010, DELTA_NE = 011, DELTA_NEE = 012, DELTA_NNW = 017,
DELTA_NE = DELTA_N + DELTA_E, DELTA_NN = 020, DELTA_NNE = 021
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)
ENABLE_OPERATORS_ON(File)
ENABLE_OPERATORS_ON(Rank)
ENABLE_OPERATORS_ON(SquareDelta)
//// ////
//// Constants //// Constants
//// ////
const int FlipMask = 56; const int FlipMask = 070;
const int FlopMask = 7; const int FlopMask = 07;
//// ////
//// Inline functions //// Inline functions
//// ////
inline Square operator+ (Square x, SquareDelta i) { return x + Square(i); } inline File operator+ (File x, int i) { return File(int(x) + i); }
inline void operator+= (Square& x, SquareDelta i) { x = x + Square(i); } inline File operator+ (File x, File y) { return x + int(y); }
inline Square operator- (Square x, SquareDelta i) { return x - Square(i); } inline void operator++ (File &x, int) { x = File(int(x) + 1); }
inline void operator-= (Square& x, SquareDelta i) { x = x - Square(i); } inline void operator+= (File &x, int i) { x = File(int(x) + i); }
inline File operator- (File x, int i) { return File(int(x) - i); }
inline void operator-- (File &x, int) { x = File(int(x) - 1); }
inline void operator-= (File &x, int i) { x = File(int(x) - i); }
inline Rank operator+ (Rank x, int i) { return Rank(int(x) + i); }
inline Rank operator+ (Rank x, Rank y) { return x + int(y); }
inline void operator++ (Rank &x, int) { x = Rank(int(x) + 1); }
inline void operator+= (Rank &x, int i) { x = Rank(int(x) + i); }
inline Rank operator- (Rank x, int i) { return Rank(int(x) - i); }
inline void operator-- (Rank &x, int) { x = Rank(int(x) - 1); }
inline void operator-= (Rank &x, int i) { x = Rank(int(x) - i); }
inline Square operator+ (Square x, int i) { return Square(int(x) + i); }
inline void operator++ (Square &x, int) { x = Square(int(x) + 1); }
inline void operator+= (Square &x, int i) { x = Square(int(x) + i); }
inline Square operator- (Square x, int i) { return Square(int(x) - i); }
inline void operator-- (Square &x, int) { x = Square(int(x) - 1); }
inline void operator-= (Square &x, int i) { x = Square(int(x) - i); }
inline Square operator+ (Square x, SquareDelta i) { return Square(int(x) + i); }
inline void operator+= (Square &x, SquareDelta i) { x = Square(int(x) + i); }
inline Square operator- (Square x, SquareDelta i) { return Square(int(x) - i); }
inline void operator-= (Square &x, SquareDelta i) { x = Square(int(x) - i); }
inline SquareDelta operator- (Square x, Square y) {
return SquareDelta(int(x) - int(y));
}
inline Square make_square(File f, Rank r) { inline Square make_square(File f, Rank r) {
return Square(int(f) | (int(r) << 3)); return Square(int(f) | (int(r) << 3));
@@ -118,13 +135,8 @@ inline Rank relative_rank(Color c, Square s) {
return square_rank(relative_square(c, s)); return square_rank(relative_square(c, s));
} }
inline SquareColor square_color(Square s) { inline Color square_color(Square s) {
return SquareColor((int(square_file(s)) + int(square_rank(s))) & 1); return Color((int(square_file(s)) + int(square_rank(s))) & 1);
}
inline bool same_color_squares(Square s1, Square s2) {
int s = int(s1) ^ int(s2);
return (((s >> 3) ^ s) & 1) == 0;
} }
inline int file_distance(File f1, File f2) { inline int file_distance(File f1, File f2) {
@@ -163,9 +175,15 @@ inline char rank_to_char(Rank r) {
return char(r - RANK_1 + int('1')); 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) { inline const std::string square_to_string(Square s) {
return std::string(1, file_to_char(square_file(s))) std::string str;
+ std::string(1, rank_to_char(square_rank(s))); str += file_to_char(square_file(s));
str += rank_to_char(square_rank(s));
return str;
} }
inline bool file_is_ok(File f) { inline bool file_is_ok(File f) {
+24 -40
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -26,8 +26,6 @@
//// Includes //// Includes
//// ////
#include <cstring>
#include "lock.h" #include "lock.h"
#include "movepick.h" #include "movepick.h"
#include "position.h" #include "position.h"
@@ -38,8 +36,7 @@
//// Constants and variables //// Constants and variables
//// ////
const int MAX_THREADS = 16; const int THREAD_MAX = 8;
const int MAX_ACTIVE_SPLIT_POINTS = 8;
//// ////
@@ -47,49 +44,36 @@ const int MAX_ACTIVE_SPLIT_POINTS = 8;
//// ////
struct SplitPoint { struct SplitPoint {
// Const data after splitPoint has been setup
SplitPoint *parent; SplitPoint *parent;
const Position* pos; Position pos;
Depth depth; SearchStack sstack[THREAD_MAX][PLY_MAX_PLUS_2];
bool pvNode, mateThreat;
Value beta;
int ply;
int master;
Move threatMove;
SearchStack sstack[MAX_THREADS][PLY_MAX_PLUS_2];
// Const pointers to shared data
MovePicker* mp;
SearchStack *parentSstack; SearchStack *parentSstack;
int ply;
// Shared data Depth depth;
volatile Value alpha, beta, bestValue, futilityValue;
Value approximateEval;
bool pvNode;
int master, slaves[THREAD_MAX];
Lock lock; Lock lock;
volatile int64_t nodes; MovePicker *mp;
volatile Value alpha; volatile int moves;
volatile Value bestValue; volatile int cpus;
volatile int moveCount; bool finished;
volatile bool betaCutoff;
volatile int slaves[MAX_THREADS];
}; };
// ThreadState type is used to represent thread's current state
enum ThreadState
{
THREAD_INITIALIZING, // thread is initializing itself
THREAD_SEARCHING, // thread is performing work
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 { struct Thread {
volatile ThreadState state; SplitPoint *splitPoint;
SplitPoint* volatile splitPoint;
volatile int activeSplitPoints; volatile int activeSplitPoints;
SplitPoint splitPoints[MAX_ACTIVE_SPLIT_POINTS]; uint64_t nodes;
uint64_t betaCutOffs[2];
bool failHighPly1;
volatile bool stop;
volatile bool running;
volatile bool idle;
volatile bool workIsWaiting;
volatile bool printCurrentLine;
unsigned char pad[64]; // set some distance among local data for each thread
}; };
-170
View File
@@ -1,170 +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 <cmath>
#include "misc.h"
#include "timeman.h"
#include "ucioption.h"
////
//// Local definitions
////
namespace {
/// Constants
const int MoveHorizon = 50; // Plan time management at most this many moves ahead
const float MaxRatio = 3.0f; // When in trouble, we can step over reserved time with this ratio
const float StealRatio = 0.33f; // However we must not steal time from remaining moves over this ratio
// MoveImportance[] is based on naive statistical analysis of "how many games are still undecided
// after n half-moves". Game is considered "undecided" as long as neither side has >275cp advantage.
// Data was extracted from CCRL game database with some simple filtering criteria.
const int MoveImportance[512] = {
7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780,
7780, 7780, 7780, 7780, 7778, 7778, 7776, 7776, 7776, 7773, 7770, 7768, 7766, 7763, 7757, 7751,
7743, 7735, 7724, 7713, 7696, 7689, 7670, 7656, 7627, 7605, 7571, 7549, 7522, 7493, 7462, 7425,
7385, 7350, 7308, 7272, 7230, 7180, 7139, 7094, 7055, 7010, 6959, 6902, 6841, 6778, 6705, 6651,
6569, 6508, 6435, 6378, 6323, 6253, 6152, 6085, 5995, 5931, 5859, 5794, 5717, 5646, 5544, 5462,
5364, 5282, 5172, 5078, 4988, 4901, 4831, 4764, 4688, 4609, 4536, 4443, 4365, 4293, 4225, 4155,
4085, 4005, 3927, 3844, 3765, 3693, 3634, 3560, 3479, 3404, 3331, 3268, 3207, 3146, 3077, 3011,
2947, 2894, 2828, 2776, 2727, 2676, 2626, 2589, 2538, 2490, 2442, 2394, 2345, 2302, 2243, 2192,
2156, 2115, 2078, 2043, 2004, 1967, 1922, 1893, 1845, 1809, 1772, 1736, 1702, 1674, 1640, 1605,
1566, 1536, 1509, 1479, 1452, 1423, 1388, 1362, 1332, 1304, 1289, 1266, 1250, 1228, 1206, 1180,
1160, 1134, 1118, 1100, 1080, 1068, 1051, 1034, 1012, 1001, 980, 960, 945, 934, 916, 900, 888,
878, 865, 852, 828, 807, 787, 770, 753, 744, 731, 722, 706, 700, 683, 676, 671, 664, 652, 641,
634, 627, 613, 604, 591, 582, 568, 560, 552, 540, 534, 529, 519, 509, 495, 484, 474, 467, 460,
450, 438, 427, 419, 410, 406, 399, 394, 387, 382, 377, 372, 366, 359, 353, 348, 343, 337, 333,
328, 321, 315, 309, 303, 298, 293, 287, 284, 281, 277, 273, 265, 261, 255, 251, 247, 241, 240,
235, 229, 218, 217, 213, 212, 208, 206, 197, 193, 191, 189, 185, 184, 180, 177, 172, 170, 170,
170, 166, 163, 159, 158, 156, 155, 151, 146, 141, 138, 136, 132, 130, 128, 125, 123, 122, 118,
118, 118, 117, 115, 114, 108, 107, 105, 105, 105, 102, 97, 97, 95, 94, 93, 91, 88, 86, 83, 80,
80, 79, 79, 79, 78, 76, 75, 72, 72, 71, 70, 68, 65, 63, 61, 61, 59, 59, 59, 58, 56, 55, 54, 54,
52, 49, 48, 48, 48, 48, 45, 45, 45, 44, 43, 41, 41, 41, 41, 40, 40, 38, 37, 36, 34, 34, 34, 33,
31, 29, 29, 29, 28, 28, 28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 24, 24, 23, 23, 22, 21, 20, 20,
19, 19, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, 17, 17, 16, 16, 15, 15, 14, 14, 14, 12, 12, 11,
9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 1 };
int move_importance(int ply) { return MoveImportance[Min(ply, 511)]; }
/// Function Prototypes
enum TimeType { OptimumTime, MaxTime };
template<TimeType>
int remaining(int myTime, int movesToGo, int currentPly);
}
////
//// Functions
////
void TimeManager::pv_instability(int curChanges, int prevChanges) {
unstablePVExtraTime = curChanges * (optimumSearchTime / 2)
+ prevChanges * (optimumSearchTime / 3);
}
void TimeManager::init(int myTime, int myInc, int movesToGo, int currentPly)
{
/* We support four different kind of time controls:
Inc == 0 && movesToGo == 0 means: x basetime [sudden death!]
Inc == 0 && movesToGo != 0 means: (x moves) / (y minutes)
Inc > 0 && movesToGo == 0 means: x basetime + z inc.
Inc > 0 && movesToGo != 0 means: (x moves) / (y minutes) + z inc
Time management is adjusted by following UCI parameters:
emergencyMoveHorizon :Be prepared to always play at least this many moves
emergencyBaseTime :Always attempt to keep at least this much time (in ms) at clock
emergencyMoveTime :Plus attempt to keep at least this much time for each remaining emergency move
minThinkingTime :No matter what, use at least this much thinking before doing the move
*/
int hypMTG, hypMyTime, t1, t2;
// Read uci parameters
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;
optimumSearchTime = maximumSearchTime = myTime;
// We calculate optimum time usage for different hypothetic "moves to go"-values and choose the
// minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values.
for (hypMTG = 1; hypMTG <= (movesToGo ? Min(movesToGo, MoveHorizon) : MoveHorizon); hypMTG++)
{
// Calculate thinking time for hypothetic "moves to go"-value
hypMyTime = Max(myTime + (hypMTG - 1) * myInc - emergencyBaseTime - Min(hypMTG, emergencyMoveHorizon) * emergencyMoveTime, 0);
t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, currentPly);
t2 = minThinkingTime + remaining<MaxTime>(hypMyTime, hypMTG, currentPly);
optimumSearchTime = Min(optimumSearchTime, t1);
maximumSearchTime = Min(maximumSearchTime, t2);
}
if (Options["Ponder"].value<bool>())
optimumSearchTime += optimumSearchTime / 4;
// Make sure that maxSearchTime is not over absoluteMaxSearchTime
optimumSearchTime = Min(optimumSearchTime, maximumSearchTime);
}
////
//// Local functions
////
namespace {
template<TimeType T>
int remaining(int myTime, int movesToGo, int currentPly)
{
const float TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
const float TStealRatio = (T == OptimumTime ? 0 : StealRatio);
int thisMoveImportance = move_importance(currentPly);
int otherMovesImportance = 0;
for (int i = 1; i < movesToGo; i++)
otherMovesImportance += move_importance(currentPly + 2 * i);
float ratio1 = (TMaxRatio * thisMoveImportance) / float(TMaxRatio * thisMoveImportance + otherMovesImportance);
float ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / float(thisMoveImportance + otherMovesImportance);
return int(floor(myTime * Min(ratio1, ratio2)));
}
}
+126 -26
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -23,8 +23,11 @@
//// ////
#include <cassert> #include <cassert>
#include <cmath>
#include <cstring> #include <cstring>
#include <xmmintrin.h>
#include "movegen.h"
#include "tt.h" #include "tt.h"
// The main transposition table // The main transposition table
@@ -36,7 +39,7 @@ TranspositionTable TT;
TranspositionTable::TranspositionTable() { TranspositionTable::TranspositionTable() {
size = 0; size = writes = 0;
entries = 0; entries = 0;
generation = 0; generation = 0;
} }
@@ -52,12 +55,12 @@ TranspositionTable::~TranspositionTable() {
void TranspositionTable::set_size(size_t mbSize) { void TranspositionTable::set_size(size_t mbSize) {
assert(mbSize >= 4 && mbSize <= 8192);
size_t newSize = 1024; size_t newSize = 1024;
// Transposition table consists of clusters and // We store a cluster of ClusterSize number of TTEntry for each position
// each cluster consists of ClusterSize number of TTEntries. // and newSize is the maximum number of storable positions.
// Each non-empty entry contains information of exactly one position.
// newSize is the number of clusters we are going to allocate.
while ((2 * newSize) * sizeof(TTCluster) <= (mbSize << 20)) while ((2 * newSize) * sizeof(TTCluster) <= (mbSize << 20))
newSize *= 2; newSize *= 2;
@@ -70,7 +73,7 @@ void TranspositionTable::set_size(size_t mbSize) {
{ {
std::cerr << "Failed to allocate " << mbSize std::cerr << "Failed to allocate " << mbSize
<< " MB for transposition table." << std::endl; << " MB for transposition table." << std::endl;
exit(EXIT_FAILURE); Application::exit_with_failure();
} }
clear(); clear();
} }
@@ -80,7 +83,7 @@ void TranspositionTable::set_size(size_t mbSize) {
/// TranspositionTable::clear overwrites the entire transposition table /// TranspositionTable::clear overwrites the entire transposition table
/// with zeroes. It is called whenever the table is resized, or when the /// with zeroes. It is called whenever the table is resized, or when the
/// user asks the program to clear the table (from the UCI interface). /// user asks the program to clear the table (from the UCI interface).
/// Perhaps we should also clear it when the "ucinewgame" command is received? /// Perhaps we should also clear it when the "ucinewgame" command is recieved?
void TranspositionTable::clear() { void TranspositionTable::clear() {
@@ -88,19 +91,28 @@ void TranspositionTable::clear() {
} }
/// TranspositionTable::store writes a new entry containing position key and /// TranspositionTable::first_entry returns a pointer to the first
/// valuable information of current position. /// entry of a cluster given a position. The low 32 bits of the key
/// The Lowest order bits of position key are used to decide on which cluster /// are used to get the index in the table.
/// the position will be placed.
/// When a new entry is written and there are no empty entries available in cluster, inline TTEntry* TranspositionTable::first_entry(const Key posKey) const {
/// it replaces the least valuable of entries.
/// A TTEntry t1 is considered to be more valuable than a TTEntry t2 if t1 is from the return entries[uint32_t(posKey) & (size - 1)].data;
}
/// TranspositionTable::store writes a new entry containing a position,
/// a value, a value type, a search depth, and a best move to the
/// transposition table. Transposition table is organized in clusters of
/// four TTEntry objects, and when a new entry is written, it replaces
/// the least valuable of the four entries in a cluster. A TTEntry t1 is
/// considered to be more valuable than a TTEntry t2 if t1 is from the
/// current search and t2 is from a previous search, or if the depth of t1 /// current search and t2 is from a previous search, or if the depth of t1
/// is bigger than the depth of t2. /// is bigger than the depth of t2. A TTEntry of type VALUE_TYPE_EVAL
/// never replaces another entry for the same position.
void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d, Move m, Value statV, Value kingD) { void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d, Move m) {
int c1, c2, c3;
TTEntry *tte, *replace; TTEntry *tte, *replace;
uint32_t posKey32 = posKey >> 32; // Use the high 32 bits as key uint32_t posKey32 = posKey >> 32; // Use the high 32 bits as key
@@ -109,25 +121,28 @@ void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d,
{ {
if (!tte->key() || tte->key() == posKey32) // empty or overwrite old if (!tte->key() || tte->key() == posKey32) // empty or overwrite old
{ {
// Preserve any existing ttMove // Do not overwrite when new type is VALUE_TYPE_EV_LO
if (tte->key() && t == VALUE_TYPE_EV_LO)
return;
if (m == MOVE_NONE) if (m == MOVE_NONE)
m = tte->move(); m = tte->move();
tte->save(posKey32, v, t, d, m, generation, statV, kingD); *tte = TTEntry(posKey32, v, t, d, m, generation);
return; return;
} }
else if (i == 0) // replace would be a no-op in this common case
if (i == 0) // Replacing first entry is default and already set before entering for-loop
continue; continue;
c1 = (replace->generation() == generation ? 2 : 0); int c1 = (replace->generation() == generation ? 2 : 0);
c2 = (tte->generation() == generation ? -2 : 0); int c2 = (tte->generation() == generation ? -2 : 0);
c3 = (tte->depth() < replace->depth() ? 1 : 0); int c3 = (tte->depth() < replace->depth() ? 1 : 0);
if (c1 + c2 + c3 > 0) if (c1 + c2 + c3 > 0)
replace = tte; replace = tte;
} }
replace->save(posKey32, v, t, d, m, generation, statV, kingD); *replace = TTEntry(posKey32, v, t, d, m, generation);
writes++;
} }
@@ -148,11 +163,96 @@ TTEntry* TranspositionTable::retrieve(const Key posKey) const {
} }
/// TranspositionTable::prefetch looks up the current position in the
/// transposition table and load it 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. When we will
/// subsequently call retrieve() the TT data will be already
/// quickly accessible in L1/L2 CPU cache.
void TranspositionTable::prefetch(const Key posKey) const {
#if defined(__INTEL_COMPILER) || defined(__ICL)
// This hack prevents prefetches to be optimized away by the
// Intel compiler. Both MSVC and gcc seems not affected.
__asm__ ("");
#endif
char const* addr = (char*)first_entry(posKey);
_mm_prefetch(addr, _MM_HINT_T2);
_mm_prefetch(addr+64, _MM_HINT_T2); // 64 bytes ahead
}
/// TranspositionTable::new_search() is called at the beginning of every new /// TranspositionTable::new_search() is called at the beginning of every new
/// search. It increments the "generation" variable, which is used to /// search. It increments the "generation" variable, which is used to
/// distinguish transposition table entries from previous searches from /// distinguish transposition table entries from previous searches from
/// entries from the current search. /// entries from the current search.
void TranspositionTable::new_search() { void TranspositionTable::new_search() {
generation++; generation++;
writes = 0;
}
/// TranspositionTable::insert_pv() is called at the end of a search
/// iteration, and inserts the PV back into the PV. This makes sure
/// the old PV moves are searched first, even if the old TT entries
/// have been overwritten.
void TranspositionTable::insert_pv(const Position& pos, Move pv[]) {
StateInfo st;
Position p(pos);
for (int i = 0; pv[i] != MOVE_NONE; i++)
{
TTEntry *tte = retrieve(p.get_key());
if (!tte || tte->move() != pv[i])
store(p.get_key(), VALUE_NONE, VALUE_TYPE_NONE, Depth(-127*OnePly), pv[i]);
p.do_move(pv[i], st);
}
}
/// TranspositionTable::extract_pv() extends a PV by adding moves from the
/// transposition table at the end. This should ensure that the PV is almost
/// always at least two plies long, which is important, because otherwise we
/// will often get single-move PVs when the search stops while failing high,
/// and a single-move PV means that we don't have a ponder move.
void TranspositionTable::extract_pv(const Position& pos, Move pv[], const int PLY_MAX) {
const TTEntry* tte;
StateInfo st;
Position p(pos);
int ply = 0;
// Update position to the end of current PV
while (pv[ply] != MOVE_NONE)
p.do_move(pv[ply++], st);
// Try to add moves from TT while possible
while ( (tte = retrieve(p.get_key())) != NULL
&& tte->move() != MOVE_NONE
&& move_is_legal(p, tte->move())
&& (!p.is_draw() || ply < 2)
&& ply < PLY_MAX)
{
pv[ply] = tte->move();
p.do_move(pv[ply++], st);
}
pv[ply] = MOVE_NONE;
}
/// TranspositionTable::full() returns the permill of all transposition table
/// entries which have received at least one write during the current search.
/// It is used to display the "info hashfull ..." information in UCI.
int TranspositionTable::full() const {
double N = double(size) * ClusterSize;
return int(1000 * (1 - exp(writes * log(1.0 - 1.0/N))));
} }
+38 -61
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -26,7 +26,7 @@
//// ////
#include "depth.h" #include "depth.h"
#include "move.h" #include "position.h"
#include "value.h" #include "value.h"
@@ -36,112 +36,89 @@
/// The TTEntry class is the class of transposition table entries /// The TTEntry class is the class of transposition table entries
/// ///
/// A TTEntry needs 128 bits to be stored /// A TTEntry needs 96 bits to be stored
/// ///
/// bit 0-31: key /// bit 0-31: key
/// bit 32-63: data /// bit 32-63: data
/// bit 64-79: value /// bit 64-79: value
/// bit 80-95: depth /// bit 80-95: depth
/// bit 96-111: static value
/// bit 112-127: margin of static value
/// ///
/// the 32 bits of the data field are so defined /// the 32 bits of the data field are so defined
/// ///
/// bit 0-16: move /// bit 0-16: move
/// bit 17-20: not used /// bit 17-19: not used
/// bit 21-22: value type /// bit 20-22: value type
/// bit 23-31: generation /// bit 23-31: generation
class TTEntry { class TTEntry {
public: public:
void save(uint32_t k, Value v, ValueType t, Depth d, Move m, int g, Value statV, Value kd) { TTEntry() {}
TTEntry(uint32_t k, Value v, ValueType t, Depth d, Move m, int generation)
: key_ (k), data((m & 0x1FFFF) | (t << 20) | (generation << 23)),
value_(int16_t(v)), depth_(int16_t(d)) {}
key32 = k; uint32_t key() const { return key_; }
data = (m & 0x1FFFF) | (t << 21) | (g << 23); Depth depth() const { return Depth(depth_); }
value16 = (int16_t)v;
depth16 = (int16_t)d;
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); }
Move move() const { return Move(data & 0x1FFFF); } Move move() const { return Move(data & 0x1FFFF); }
Value value() const { return Value(value16); } Value value() const { return Value(value_); }
ValueType type() const { return ValueType((data >> 21) & 3); } ValueType type() const { return ValueType((data >> 20) & 7); }
int generation() const { return data >> 23; } int generation() const { return (data >> 23); }
Value static_value() const { return Value(staticValue); }
Value static_value_margin() const { return Value(staticValueMargin); }
private: private:
uint32_t key32; uint32_t key_;
uint32_t data; uint32_t data;
int16_t value16; int16_t value_;
int16_t depth16; int16_t depth_;
int16_t staticValue;
int16_t staticValueMargin;
}; };
/// This is the number of TTEntry slots for each cluster /// This is the number of TTEntry slots for each position
const int ClusterSize = 4; const int ClusterSize = 5;
/// TTCluster consists of ClusterSize number of TTEntries. /// Each group of ClusterSize number of TTEntry form a TTCluster
/// Size of TTCluster must not be bigger than a cache line size. /// that is indexed by a single position key. Cluster is padded
/// In case it is less, it should be padded to guarantee always aligned accesses. /// to a cache line size so to guarantee always aligned accesses.
struct TTCluster { struct TTCluster {
TTEntry data[ClusterSize]; TTEntry data[ClusterSize];
char cache_line_padding[64 - sizeof(TTEntry[ClusterSize])];
}; };
/// The transposition table class. This is basically just a huge array /// The transposition table class. This is basically just a huge array
/// containing TTCluster objects, and a few methods for writing new entries /// containing TTEntry objects, and a few methods for writing new entries
/// and reading new ones. /// and reading new ones.
class TranspositionTable { class TranspositionTable {
TranspositionTable(const TranspositionTable&);
TranspositionTable& operator=(const TranspositionTable&);
public: public:
TranspositionTable(); TranspositionTable();
~TranspositionTable(); ~TranspositionTable();
void set_size(size_t mbSize); void set_size(size_t mbSize);
void clear(); void clear();
void store(const Key posKey, Value v, ValueType type, Depth d, Move m, Value statV, Value kingD); void store(const Key posKey, Value v, ValueType type, Depth d, Move m);
TTEntry* retrieve(const Key posKey) const; TTEntry* retrieve(const Key posKey) const;
void prefetch(const Key posKey) const;
void new_search(); void new_search();
TTEntry* first_entry(const Key posKey) const; void insert_pv(const Position& pos, Move pv[]);
void refresh(const TTEntry* tte) const; void extract_pv(const Position& pos, Move pv[], const int PLY_MAX);
int full() const;
private: private:
inline TTEntry* first_entry(const Key posKey) const;
// Be sure 'writes' is at least one cache line away
// from read only variables.
unsigned char pad_before[64 - sizeof(unsigned)];
unsigned writes; // heavy SMP read/write access here
unsigned char pad_after[64];
size_t size; size_t size;
TTCluster* entries; TTCluster* entries;
uint8_t generation; // To properly compare, size must be smaller then TT stored value uint8_t generation;
}; };
extern TranspositionTable TT; extern TranspositionTable TT;
/// TranspositionTable::first_entry returns a pointer to the first
/// entry of a cluster given a position. The lowest order bits of the key
/// are used to get the index of the cluster.
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) #endif // !defined(TT_H_INCLUDED)
+10 -70
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(TYPES_H_INCLUDED) #if !defined(TYPES_H_INCLUDED)
#define TYPES_H_INCLUDED #define TYPES_H_INCLUDED
@@ -26,10 +27,6 @@
#else #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 __int8 int8_t;
typedef unsigned __int8 uint8_t; typedef unsigned __int8 uint8_t;
typedef __int16 int16; typedef __int16 int16;
@@ -50,39 +47,22 @@ typedef uint64_t Key;
// Bitboard type // Bitboard type
typedef uint64_t Bitboard; typedef uint64_t Bitboard;
#include <cstdlib>
//// ////
//// Configuration //// Compiler specific defines
//// ////
//// For Linux and OSX configuration is done automatically using Makefile. // Quiet a warning on Intel compiler
//// To get started type "make help". #if !defined(__SIZEOF_INT__ )
//// #define __SIZEOF_INT__ 0
//// For windows part of the configuration is detected automatically, but #endif
//// some switches need to be set manually:
////
//// -DNDEBUG | Disable debugging mode. Use always.
////
//// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want the
//// | executable to run on some very old machines.
////
//// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction.
//// | Works only in 64-bit mode. For compiling requires hardware
//// | with popcnt support. Around 4% speed-up.
////
//// -DOLD_LOCKS | By default under Windows are used the fast Slim Reader/Writer (SRW)
//// | Locks and Condition Variables: these are not supported by Windows XP
//// | and older, to compile for those platforms you should enable OLD_LOCKS.
// Automatic detection for 64-bit under Windows // Check for 64 bits for different compilers: Intel, MSVC and gcc
#if defined(_WIN64) #if defined(__x86_64) || defined(_M_X64) || defined(_WIN64) || (__SIZEOF_INT__ > 4)
#define IS_64BIT #define IS_64BIT
#endif #endif
// Automatic detection for use of bsfq asm-instruction under Windows. #if defined(IS_64BIT) && (defined(__GNUC__) || defined(__INTEL_COMPILER))
// Works only in 64-bit mode. Does not work with MSVC.
#if defined(_WIN64) && defined(__INTEL_COMPILER)
#define USE_BSFQ #define USE_BSFQ
#endif #endif
@@ -93,44 +73,4 @@ typedef uint64_t Bitboard;
#define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64))) #define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64)))
#endif #endif
// Define a __cpuid() function for gcc compilers, for Intel and MSVC
// is already available as an intrinsic.
#if defined(_MSC_VER)
#include <intrin.h>
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
inline void __cpuid(int CPUInfo[4], int InfoType)
{
int* eax = CPUInfo + 0;
int* ebx = CPUInfo + 1;
int* ecx = CPUInfo + 2;
int* edx = CPUInfo + 3;
*eax = InfoType;
*ecx = 0;
__asm__("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
: "0" (*eax), "2" (*ecx));
}
#else
inline void __cpuid(int CPUInfo[4], int)
{
CPUInfo[0] = CPUInfo[1] = CPUInfo[2] = CPUInfo[3] = 0;
}
#endif
// Operators used by enum types like Depth, Piece, Square and so on.
#define ENABLE_OPERATORS_ON(T) \
inline T operator+ (const T d1, const T d2) { return T(int(d1) + int(d2)); } \
inline T operator- (const T d1, const T d2) { return T(int(d1) - int(d2)); } \
inline T operator* (int i, const T d) { return T(i * int(d)); } \
inline T operator* (const T d, int i) { return T(int(d) * i); } \
inline T operator/ (const T d, int i) { return T(int(d) / i); } \
inline T operator- (const T d) { return T(-int(d)); } \
inline T operator++ (T& d, int) {d = T(int(d) + 1); return d; } \
inline T operator-- (T& d, int) { d = T(int(d) - 1); return d; } \
inline void operator+= (T& d1, const T d2) { d1 = d1 + d2; } \
inline void operator-= (T& d1, const T d2) { d1 = d1 - d2; } \
inline void operator*= (T& d, int i) { d = T(int(d) * i); } \
inline void operator/= (T& d, int i) { d = T(int(d) / i); }
#endif // !defined(TYPES_H_INCLUDED) #endif // !defined(TYPES_H_INCLUDED)
+162 -141
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
#include <sstream> #include <sstream>
#include <string> #include <string>
#include "book.h"
#include "evaluate.h" #include "evaluate.h"
#include "misc.h" #include "misc.h"
#include "move.h" #include "move.h"
@@ -34,95 +35,59 @@
#include "position.h" #include "position.h"
#include "san.h" #include "san.h"
#include "search.h" #include "search.h"
#include "uci.h"
#include "ucioption.h" #include "ucioption.h"
using namespace std; using namespace std;
////
//// Local definitions:
////
namespace { namespace {
// FEN string for the initial position // UCIInputParser is a class for parsing UCI input. The class
const string StartPositionFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
// UCIParser is a class for parsing UCI input. The class
// is actually a string stream built on a given input string. // is actually a string stream built on a given input string.
typedef istringstream UCIParser;
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;
// Local functions // Local functions
void set_option(UCIParser& uip); bool handle_command(const string& command);
void set_position(Position& pos, UCIParser& uip); void set_option(UCIInputParser& uip);
bool go(Position& pos, UCIParser& uip); void set_position(UCIInputParser& uip);
void perft(Position& pos, UCIParser& uip); bool go(UCIInputParser& uip);
void perft(UCIInputParser& uip);
} }
/// execute_uci_command() takes a string as input, uses a UCIParser ////
/// object to parse this text string as a UCI command, and calls //// Functions
/// the appropriate functions. In addition to the UCI commands, ////
/// the function also supports a few debug commands.
bool execute_uci_command(const string& cmd) { /// 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.
static Position pos(StartPositionFEN, false, 0); // The root position void uci_main_loop() {
UCIParser up(cmd);
string token;
if (!(up >> token)) // operator>>() skips any whitespace RootPosition.from_fen(StartPosition);
return true; string command;
if (token == "quit") do {
return false; // Wait for a command from stdin
if (!getline(cin, command))
command = "quit";
if (token == "go") } while (handle_command(command));
return go(pos, up);
if (token == "uci")
{
cout << "id name " << engine_name()
<< "\nid author Tord Romstad, Marco Costalba, Joona Kiiski\n";
print_uci_options();
cout << "uciok" << endl;
}
else if (token == "ucinewgame")
pos.from_fen(StartPositionFEN, false);
else if (token == "isready")
cout << "readyok" << endl;
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")
pos.print();
else if (token == "flip")
{
Position p(pos, pos.thread());
pos.flipped_copy(p);
}
else if (token == "eval")
{
Value evalMargin;
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 << pos.get_key()
<< "\nmaterial key: " << pos.get_material_key()
<< "\npawn key: " << pos.get_pawn_key() << endl;
else if (token == "perft")
perft(pos, up);
else
cout << "Unknown command: " << cmd << endl;
return true;
} }
@@ -132,103 +97,157 @@ bool execute_uci_command(const string& cmd) {
namespace { 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);
string token;
if (!(uip >> token)) // operator>>() skips any whitespace
return true;
if (token == "quit")
return false;
if (token == "go")
return go(uip);
if (token == "uci")
{
cout << "id name " << engine_name()
<< "\nid author Tord Romstad, Marco Costalba, Joona Kiiski\n";
print_uci_options();
cout << "uciok" << endl;
}
else if (token == "ucinewgame")
{
push_button("New Game");
Position::init_piece_square_tables();
RootPosition.from_fen(StartPosition);
}
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 == "d")
RootPosition.print();
else if (token == "flip")
{
Position p(RootPosition);
RootPosition.flipped_copy(p);
}
else if (token == "eval")
{
EvalInfo ei;
cout << "Incremental mg: " << mg_value(RootPosition.value())
<< "\nIncremental eg: " << eg_value(RootPosition.value())
<< "\nFull eval: " << evaluate(RootPosition, ei, 0) << endl;
}
else if (token == "key")
cout << "key: " << hex << RootPosition.get_key()
<< "\nmaterial key: " << RootPosition.get_material_key()
<< "\npawn key: " << RootPosition.get_pawn_key() << endl;
else if (token == "perft")
perft(uip);
else
cout << "Unknown command: " << command << endl;
return true;
}
// set_position() is called when Stockfish receives the "position" UCI // set_position() is called when Stockfish receives the "position" UCI
// command. The input parameter is a UCIParser. It is assumed // command. The input parameter is a UCIInputParser. It is assumed
// that this parser has consumed the first token of the UCI command // that this parser has consumed the first token of the UCI command
// ("position"), and is ready to read the second token ("startpos" // ("position"), and is ready to read the second token ("startpos"
// or "fen", if the input is well-formed). // or "fen", if the input is well-formed).
void set_position(Position& pos, UCIParser& up) { void set_position(UCIInputParser& uip) {
string token; string token;
if (!(up >> token) || (token != "startpos" && token != "fen")) if (!(uip >> token)) // operator>>() skips any whitespace
return; return;
if (token == "startpos") if (token == "startpos")
{ RootPosition.from_fen(StartPosition);
pos.from_fen(StartPositionFEN, false); else if (token == "fen")
if (!(up >> token))
return;
}
else // fen
{ {
string fen; string fen;
while (up >> token && token != "moves") while (uip >> token && token != "moves")
{ {
fen += token; fen += token;
fen += ' '; fen += ' ';
} }
pos.from_fen(fen, Options["UCI_Chess960"].value<bool>()); RootPosition.from_fen(fen);
} }
if (uip.good())
{
if (token != "moves") if (token != "moves")
return; uip >> token;
// Parse optional move list if (token == "moves")
{
Move move; Move move;
StateInfo st; StateInfo st;
while (up >> token) while (uip >> token)
{ {
move = move_from_uci(pos, token); move = move_from_string(RootPosition, token);
pos.do_move(move, st); RootPosition.do_move(move, st);
if (pos.rule_50_counter() == 0) if (RootPosition.rule_50_counter() == 0)
pos.reset_game_ply(); RootPosition.reset_game_ply();
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 // Our StateInfo st is about going out of scope so copy
// its content inside pos before it disappears. // its content inside RootPosition before they disappear.
pos.detach(); RootPosition.saveState();
}
}
} }
// set_option() is called when Stockfish receives the "setoption" UCI // set_option() is called when Stockfish receives the "setoption" UCI
// command. The input parameter is a UCIParser. It is assumed // command. The input parameter is a UCIInputParser. It is assumed
// that this parser has consumed the first token of the UCI command // that this parser has consumed the first token of the UCI command
// ("setoption"), and is ready to read the second token ("name", if // ("setoption"), and is ready to read the second token ("name", if
// the input is well-formed). // the input is well-formed).
void set_option(UCIParser& up) { void set_option(UCIInputParser& uip) {
string token, name, value; string token, name, value;
if (!(up >> token) || token != "name") // operator>>() skips any whitespace if (!(uip >> token)) // operator>>() skips any whitespace
return; return;
if (!(up >> name)) if (token == "name" && uip >> name)
return; {
while (uip >> token && token != "value")
// Handle names with included spaces
while (up >> token && token != "value")
name += (" " + token); name += (" " + token);
if (Options.find(name) == Options.end()) if (token == "value" && uip >> value)
{ {
cout << "No such option: " << name << endl; while (uip >> token)
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); value += (" " + token);
Options[name].set_value(value); set_option_value(name, value);
} else
push_button(name);
}
} }
// go() is called when Stockfish receives the "go" UCI command. The // go() is called when Stockfish receives the "go" UCI command. The
// input parameter is a UCIParser. It is assumed that this // input parameter is a UCIInputParser. It is assumed that this
// parser has consumed the first token of the UCI command ("go"), // parser has consumed the first token of the UCI command ("go"),
// and is ready to read the second token. The function sets the // and is ready to read the second token. The function sets the
// thinking time and other parameters from the input string, and // thinking time and other parameters from the input string, and
@@ -236,69 +255,71 @@ namespace {
// parameters. Returns false if a quit command is received while // parameters. Returns false if a quit command is received while
// thinking, returns true otherwise. // thinking, returns true otherwise.
bool go(Position& pos, UCIParser& up) { bool go(UCIInputParser& uip) {
string token; string token;
int time[2] = {0, 0}, inc[2] = {0, 0}; int time[2] = {0, 0}, inc[2] = {0, 0};
int movesToGo = 0, depth = 0, nodes = 0, moveTime = 0; int movesToGo = 0, depth = 0, nodes = 0, moveTime = 0;
bool infinite = false, ponder = false; bool infinite = false, ponder = false;
Move searchMoves[MOVES_MAX]; Move searchMoves[500];
searchMoves[0] = MOVE_NONE; searchMoves[0] = MOVE_NONE;
while (up >> token) while (uip >> token)
{ {
if (token == "infinite") if (token == "infinite")
infinite = true; infinite = true;
else if (token == "ponder") else if (token == "ponder")
ponder = true; ponder = true;
else if (token == "wtime") else if (token == "wtime")
up >> time[0]; uip >> time[0];
else if (token == "btime") else if (token == "btime")
up >> time[1]; uip >> time[1];
else if (token == "winc") else if (token == "winc")
up >> inc[0]; uip >> inc[0];
else if (token == "binc") else if (token == "binc")
up >> inc[1]; uip >> inc[1];
else if (token == "movestogo") else if (token == "movestogo")
up >> movesToGo; uip >> movesToGo;
else if (token == "depth") else if (token == "depth")
up >> depth; uip >> depth;
else if (token == "nodes") else if (token == "nodes")
up >> nodes; uip >> nodes;
else if (token == "movetime") else if (token == "movetime")
up >> moveTime; uip >> moveTime;
else if (token == "searchmoves") else if (token == "searchmoves")
{ {
int numOfMoves = 0; int numOfMoves = 0;
while (up >> token) while (uip >> token)
searchMoves[numOfMoves++] = move_from_uci(pos, token); searchMoves[numOfMoves++] = move_from_string(RootPosition, token);
searchMoves[numOfMoves] = MOVE_NONE; searchMoves[numOfMoves] = MOVE_NONE;
} }
} }
assert(pos.is_ok()); assert(RootPosition.is_ok());
return think(pos, infinite, ponder, time, inc, movesToGo, return think(RootPosition, infinite, ponder, RootPosition.side_to_move(),
depth, nodes, moveTime, searchMoves); time, inc, movesToGo, depth, nodes, moveTime, searchMoves);
} }
void perft(Position& pos, UCIParser& up) { void perft(UCIInputParser& uip) {
string token;
int depth, tm, n; int depth, tm, n;
Position pos = RootPosition;
if (!(up >> depth)) if (!(uip >> depth))
return; return;
tm = get_system_time(); tm = get_system_time();
n = perft(pos, depth * ONE_PLY); n = perft(pos, depth * OnePly);
tm = get_system_time() - tm; tm = get_system_time() - tm;
std::cout << "\nNodes " << n std::cout << "\nNodes " << n
<< "\nTime (ms) " << tm << "\nTime (ms) " << tm
<< "\nNodes/second " << int(n / (tm / 1000.0)) << std::endl; << "\nNodes/second " << (int)(n/(tm/1000.0)) << std::endl;
} }
} }
+31
View File
@@ -0,0 +1,31 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2009 Marco Costalba
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)
+282 -99
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,42 +17,136 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cctype>
#include <iostream> ////
//// Includes
////
#include <algorithm>
#include <cassert>
#include <map>
#include <string>
#include <sstream> #include <sstream>
#include <vector>
#include "misc.h" #include "misc.h"
#include "thread.h" #include "thread.h"
#include "ucioption.h" #include "ucioption.h"
using std::string; using std::string;
using std::cout;
using std::endl;
OptionsMap Options; // Global object ////
//// Local definitions
////
namespace {
// Our case insensitive less() function as required by UCI protocol ///
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { /// Types
///
int c1, c2; enum OptionType { SPIN, COMBO, CHECK, STRING, BUTTON };
size_t i = 0;
while (i < s1.size() && i < s2.size()) typedef std::vector<string> ComboValues;
{
c1 = tolower(s1[i]);
c2 = tolower(s2[i++]);
if (c1 != c2) struct Option {
return c1 < c2;
} string name, defaultValue, currentValue;
return s1.size() < s2.size(); OptionType type;
size_t idx;
int minValue, maxValue;
ComboValues comboValues;
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 this->idx < o.idx; }
};
typedef std::map<string, Option> Options;
///
/// Constants
///
// 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["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["King Safety Curve"] = Option("Quadratic", COMBO);
o["King Safety Curve"].comboValues.push_back("Quadratic");
o["King Safety Curve"].comboValues.push_back("Linear"); /*, "From File"*/
o["King Safety Coefficient"] = Option(40, 1, 100);
o["King Safety X Intercept"] = Option(0, 0, 20);
o["King Safety Max Slope"] = Option(30, 10, 100);
o["King Safety Max Value"] = Option(500, 100, 1000);
o["Queen Contact Check Bonus"] = Option(3, 0, 8);
o["Queen Check Bonus"] = Option(2, 0, 4);
o["Rook Check Bonus"] = Option(1, 0, 4);
o["Bishop Check Bonus"] = Option(1, 0, 4);
o["Knight Check Bonus"] = Option(1, 0, 4);
o["Discovered Check Bonus"] = Option(3, 0, 8);
o["Mate Threat Bonus"] = Option(3, 0, 8);
o["Check Extension (PV nodes)"] = Option(2, 0, 2);
o["Check Extension (non-PV nodes)"] = Option(1, 0, 2);
o["Single Reply Extension (PV nodes)"] = Option(2, 0, 2);
o["Single Reply Extension (non-PV nodes)"] = Option(2, 0, 2);
o["Mate Threat Extension (PV nodes)"] = Option(0, 0, 2);
o["Mate Threat Extension (non-PV nodes)"] = Option(0, 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["Full Depth Moves (PV nodes)"] = Option(10, 1, 100);
o["Full Depth Moves (non-PV nodes)"] = Option(3, 1, 100);
o["Threat Depth"] = Option(5, 0, 100);
o["Randomness"] = Option(0, 0, 10);
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, THREAD_MAX);
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["UCI_ShowCurrLine"] = Option(false);
o["UCI_Chess960"] = Option(false);
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;
} }
///
/// Variables
///
// stringify() converts a numeric value of type T to a std::string Options options;
// stringify converts a value of type T to a std::string
template<typename T> template<typename T>
static string stringify(const T& v) { string stringify(const T& v) {
std::ostringstream ss; std::ostringstream ss;
ss << v; ss << v;
@@ -60,56 +154,55 @@ static string stringify(const T& v) {
} }
/// init_uci_options() initializes the UCI options to their hard coded default // get_option_value implements the various get_option_value_<type>
/// values and initializes the default value of "Threads" and "Minimum Split Depth" // functions defined later, because only the option value
/// parameters according to the number of CPU cores. // type changes a template seems a proper solution.
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;
}
}
////
//// Functions
////
/// init_uci_options() initializes the UCI options. Currently, the only
/// thing this function does is to initialize the default value of the
/// "Threads" parameter to the number of available CPU cores.
void init_uci_options() { void init_uci_options() {
Options["Use Search Log"] = Option(false); load_defaults(options);
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);
// Set some SMP parameters accordingly to the detected CPU count // Set optimal value for parameter "Minimum Split Depth"
Option& thr = Options["Threads"]; // according to number of available cores.
Option& msd = Options["Minimum Split Depth"]; assert(options.find("Threads") != options.end());
assert(options.find("Minimum Split Depth") != options.end());
Option& thr = options["Threads"];
Option& msd = options["Minimum Split Depth"];
thr.defaultValue = thr.currentValue = stringify(cpu_count()); thr.defaultValue = thr.currentValue = stringify(cpu_count());
@@ -119,60 +212,150 @@ void init_uci_options() {
/// print_uci_options() prints all the UCI options to the standard output, /// print_uci_options() prints all the UCI options to the standard output,
/// in chronological insertion order (the idx field) and in the format /// in the format defined by the UCI protocol.
/// defined by the UCI protocol.
void print_uci_options() { void print_uci_options() {
for (size_t i = 0; i <= Options.size(); i++) static const char optionTypeName[][16] = {
for (OptionsMap::const_iterator it = Options.begin(); it != Options.end(); ++it) "spin", "combo", "check", "string", "button"
if (it->second.idx == i) };
// 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.
std::vector<Option> vec;
for (Options::const_iterator it = options.begin(); it != options.end(); ++it)
vec.push_back(it->second);
std::sort(vec.begin(), vec.end());
for (std::vector<Option>::const_iterator it = vec.begin(); it != vec.end(); ++it)
{ {
const Option& o = it->second; std::cout << "\noption name " << it->name
cout << "\noption name " << it->first << " type " << o.type; << " type " << optionTypeName[it->type];
if (o.type != "button") if (it->type == BUTTON)
cout << " default " << o.defaultValue; continue;
if (o.type == "spin") if (it->type == CHECK)
cout << " min " << o.minValue << " max " << o.maxValue; std::cout << " default " << (it->defaultValue == "1" ? "true" : "false");
else
std::cout << " default " << it->defaultValue;
break; if (it->type == SPIN)
std::cout << " min " << it->minValue << " max " << it->maxValue;
else if (it->type == COMBO)
for (ComboValues::const_iterator itc = it->comboValues.begin();
itc != it->comboValues.end(); ++itc)
std::cout << " var " << *itc;
} }
cout << endl; std::cout << std::endl;
} }
/// Option class c'tors /// get_option_value_bool() returns the current value of a UCI parameter of
/// type "check".
Option::Option(const char* def) : type("string"), idx(Options.size()), minValue(0), maxValue(0) bool get_option_value_bool(const string& optionName) {
{ defaultValue = currentValue = def; }
Option::Option(bool def, string t) : type(t), idx(Options.size()), minValue(0), maxValue(0) return get_option_value<bool>(optionName);
{ 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); }
/// set_value() updates currentValue of the Option object. Normally it's up to /// get_option_value_int() returns the value of a UCI parameter as an integer.
/// the GUI to check for option's limits, but we could receive the new value /// Normally, this function will be used for a parameter of type "spin", but
/// directly from the user by teminal window. So let's check the bounds anyway. /// it could also be used with a "combo" parameter, where all the available
/// values are integers.
void Option::set_value(const string& value) { int get_option_value_int(const string& optionName) {
assert(!type.empty()); return get_option_value<int>(optionName);
}
if ( (type == "check" || type == "button")
&& !(value == "true" || value == "false"))
return;
if (type == "spin") /// 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. Note that
/// the function does not check that the new value is legal for the given
/// parameter: This is assumed to be the responsibility of the GUI.
void set_option_value(const string& name, const string& value) {
// 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";
if (options.find(name) == options.end())
{ {
int v = atoi(value.c_str()); std::cout << "No such option: " << name << std::endl;
if (v < minValue || v > maxValue)
return; return;
} }
currentValue = value; // 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;
}
namespace {
// Define constructors of Option class.
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) {}
} }
+16 -52
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,64 +17,28 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(UCIOPTION_H_INCLUDED) #if !defined(UCIOPTION_H_INCLUDED)
#define UCIOPTION_H_INCLUDED #define UCIOPTION_H_INCLUDED
#include <cassert> ////
#include <cstdlib> //// Includes
#include <map> ////
#include <string> #include <string>
class Option { ////
public: //// Prototypes
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 init_uci_options();
extern void print_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) #endif // !defined(UCIOPTION_H_INCLUDED)
+96
View File
@@ -0,0 +1,96 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2009 Marco Costalba
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 <sstream>
#include <string>
#include "value.h"
////
//// Functions
////
/// value_to_tt() adjusts a mate score from "plies to mate from the root" to
/// "plies to mate from the current ply". Non-mate scores are unchanged.
/// The function is called before storing a value to the transposition table.
Value value_to_tt(Value v, int ply) {
if(v >= value_mate_in(100))
return v + ply;
else if(v <= value_mated_in(100))
return v - ply;
else
return v;
}
/// value_from_tt() is the inverse of value_to_tt(): It adjusts a mate score
/// from the transposition table to a mate score corrected for the current
/// ply depth.
Value value_from_tt(Value v, int ply) {
if(v >= value_mate_in(100))
return v - ply;
else if(v <= value_mated_in(100))
return v + ply;
else
return v;
}
/// value_to_centipawns() converts a value from Stockfish's somewhat unusual
/// scale of pawn = 256 to the more conventional pawn = 100.
int value_to_centipawns(Value v) {
return (int(v) * 100) / int(PawnValueMidgame);
}
/// value_from_centipawns() converts a centipawn value to Stockfish's internal
/// evaluation scale. It's used when reading the values of UCI options
/// containing material values (e.g. futility pruning margins).
Value value_from_centipawns(int cp) {
return Value((cp * 256) / 100);
}
/// value_to_string() converts a value to a string suitable for use with the
/// UCI protocol.
const std::string value_to_string(Value v) {
std::stringstream s;
if(abs(v) < VALUE_MATE - 200)
s << "cp " << value_to_centipawns(v);
else {
s << "mate ";
if(v > 0)
s << (VALUE_MATE - v + 1) / 2;
else
s << -(VALUE_MATE + v) / 2;
}
return s.str();
}
+119 -30
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,6 +21,13 @@
#if !defined(VALUE_H_INCLUDED) #if !defined(VALUE_H_INCLUDED)
#define VALUE_H_INCLUDED #define VALUE_H_INCLUDED
////
//// Includes
////
#include "piece.h"
//// ////
//// Types //// Types
//// ////
@@ -29,12 +36,14 @@ enum ValueType {
VALUE_TYPE_NONE = 0, VALUE_TYPE_NONE = 0,
VALUE_TYPE_UPPER = 1, // Upper bound VALUE_TYPE_UPPER = 1, // Upper bound
VALUE_TYPE_LOWER = 2, // Lower bound VALUE_TYPE_LOWER = 2, // Lower bound
VALUE_TYPE_EXACT = VALUE_TYPE_UPPER | VALUE_TYPE_LOWER VALUE_TYPE_EXACT = 3, // Exact score
VALUE_TYPE_EVAL = 4, // Evaluation cache
VALUE_TYPE_EV_UP = 5, // Evaluation cache for upper bound
VALUE_TYPE_EV_LO = 6 // Evaluation cache for lower bound
}; };
enum Value { enum Value {
VALUE_ZERO = 0,
VALUE_DRAW = 0, VALUE_DRAW = 0,
VALUE_KNOWN_WIN = 15000, VALUE_KNOWN_WIN = 15000,
VALUE_MATE = 30000, VALUE_MATE = 30000,
@@ -43,16 +52,6 @@ enum Value {
VALUE_ENSURE_SIGNED = -1 VALUE_ENSURE_SIGNED = -1
}; };
ENABLE_OPERATORS_ON(Value)
enum ScaleFactor {
SCALE_FACTOR_ZERO = 0,
SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128,
SCALE_FACTOR_NONE = 255
};
/// Score enum keeps a midgame and an endgame value in a single /// Score enum keeps a midgame and an endgame value in a single
/// integer (enum), first LSB 16 bits are used to store endgame /// integer (enum), first LSB 16 bits are used to store endgame
@@ -60,11 +59,7 @@ enum ScaleFactor {
// Compiler is free to choose the enum type as long as can keep // Compiler is free to choose the enum type as long as can keep
// its data, so ensure Score to be an integer type. // its data, so ensure Score to be an integer type.
enum Score { enum Score { ENSURE_32_BITS_SIZE_P = (1 << 16), ENSURE_32_BITS_SIZE_N = -(1 << 16)};
SCORE_ZERO = 0,
SCORE_ENSURE_32_BITS_SIZE_P = (1 << 16),
SCORE_ENSURE_32_BITS_SIZE_N = -(1 << 16)
};
// Extracting the _signed_ lower and upper 16 bits it not so trivial // Extracting the _signed_ lower and upper 16 bits it not so trivial
// because according to the standard a simple cast to short is // because according to the standard a simple cast to short is
@@ -81,6 +76,13 @@ inline Value eg_value(Score s) { return Value((int)(unsigned(s) & 0x7fffu) - (in
inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); } inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
inline Score operator-(Score s) { return Score(-int(s)); }
inline Score operator+(Score s1, Score s2) { return Score(int(s1) + int(s2)); }
inline Score operator-(Score s1, Score s2) { return Score(int(s1) - int(s2)); }
inline void operator+=(Score& s1, Score s2) { s1 = Score(int(s1) + int(s2)); }
inline void operator-=(Score& s1, Score s2) { s1 = Score(int(s1) - int(s2)); }
inline Score operator*(int i, Score s) { return Score(i * int(s)); }
// Division must be handled separately for each term // Division must be handled separately for each term
inline Score operator/(Score s, int i) { return make_score(mg_value(s) / i, eg_value(s) / i); } inline Score operator/(Score s, int i) { return make_score(mg_value(s) / i, eg_value(s) / i); }
@@ -88,16 +90,53 @@ inline Score operator/(Score s, int i) { return make_score(mg_value(s) / i, eg_v
// a very high risk of overflow. So user should explicitly convert to integer. // a very high risk of overflow. So user should explicitly convert to integer.
inline Score operator*(Score s1, Score s2); inline Score operator*(Score s1, Score s2);
// Rest of operators are standard:
inline Score operator+ (const Score d1, const Score d2) { return Score(int(d1) + int(d2)); } ////
inline Score operator- (const Score d1, const Score d2) { return Score(int(d1) - int(d2)); } //// Constants and variables
inline Score operator* (int i, const Score d) { return Score(i * int(d)); } ////
inline Score operator* (const Score d, int i) { return Score(int(d) * i); }
inline Score operator- (const Score d) { return Score(-int(d)); } /// Piece values, middle game and endgame
inline void operator+= (Score& d1, const Score d2) { d1 = d1 + d2; }
inline void operator-= (Score& d1, const Score d2) { d1 = d1 - d2; } /// Important: If the material values are changed, one must also
inline void operator*= (Score& d, int i) { d = Score(int(d) * i); } /// adjust the piece square tables, and the method game_phase() in the
inline void operator/= (Score& d, int i) { d = Score(int(d) / i); } /// Position class!
///
/// Values modified by Joona Kiiski
const Value PawnValueMidgame = Value(0x0C6);
const Value PawnValueEndgame = Value(0x102);
const Value KnightValueMidgame = Value(0x331);
const Value KnightValueEndgame = Value(0x34E);
const Value BishopValueMidgame = Value(0x344);
const Value BishopValueEndgame = Value(0x359);
const Value RookValueMidgame = Value(0x4F6);
const Value RookValueEndgame = Value(0x4FE);
const Value QueenValueMidgame = Value(0x9D9);
const Value QueenValueEndgame = Value(0x9FE);
const Value PieceValueMidgame[17] = {
Value(0),
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
RookValueMidgame, QueenValueMidgame,
Value(0), Value(0), Value(0),
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
RookValueMidgame, QueenValueMidgame,
Value(0), Value(0), Value(0)
};
const Value PieceValueEndgame[17] = {
Value(0),
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
RookValueEndgame, QueenValueEndgame,
Value(0), Value(0), Value(0),
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
RookValueEndgame, QueenValueEndgame,
Value(0), Value(0), Value(0)
};
/// Bonus for having the side to move (modified by Joona Kiiski)
const Score TempoValue = make_score(48, 22);
//// ////
@@ -105,15 +144,65 @@ inline void operator/= (Score& d, int i) { d = Score(int(d) / i); }
//// ////
inline Value operator+ (Value v, int i) { return Value(int(v) + i); } inline Value operator+ (Value v, int i) { return Value(int(v) + i); }
inline Value operator+ (Value v1, Value v2) { return Value(int(v1) + int(v2)); }
inline void operator+= (Value &v1, Value v2) {
v1 = Value(int(v1) + int(v2));
}
inline Value operator- (Value v, int i) { return Value(int(v) - i); } inline Value operator- (Value v, int i) { return Value(int(v) - i); }
inline Value operator- (Value v) { return Value(-int(v)); }
inline Value operator- (Value v1, Value v2) { return Value(int(v1) - int(v2)); }
inline void operator-= (Value &v1, Value v2) {
v1 = Value(int(v1) - int(v2));
}
inline Value operator* (Value v, int i) { return Value(int(v) * i); }
inline void operator*= (Value &v, int i) { v = Value(int(v) * i); }
inline Value operator* (int i, Value v) { return Value(int(v) * i); }
inline Value operator/ (Value v, int i) { return Value(int(v) / i); }
inline void operator/= (Value &v, int i) { v = Value(int(v) / i); }
inline Value value_mate_in(int ply) { inline Value value_mate_in(int ply) {
return VALUE_MATE - ply; return Value(VALUE_MATE - Value(ply));
} }
inline Value value_mated_in(int ply) { inline Value value_mated_in(int ply) {
return -VALUE_MATE + ply; return Value(-VALUE_MATE + Value(ply));
} }
inline bool is_upper_bound(ValueType vt) {
return (int(vt) & int(VALUE_TYPE_UPPER)) != 0;
}
inline bool is_lower_bound(ValueType vt) {
return (int(vt) & int(VALUE_TYPE_LOWER)) != 0;
}
inline Value piece_value_midgame(PieceType pt) {
return PieceValueMidgame[pt];
}
inline Value piece_value_endgame(PieceType pt) {
return PieceValueEndgame[pt];
}
inline Value piece_value_midgame(Piece p) {
return PieceValueMidgame[p];
}
inline Value piece_value_endgame(Piece p) {
return PieceValueEndgame[p];
}
////
//// Prototypes
////
extern Value value_to_tt(Value v, int ply);
extern Value value_from_tt(Value v, int ply);
extern int value_to_centipawns(Value v);
extern Value value_from_centipawns(int cp);
extern const std::string value_to_string(Value v);
#endif // !defined(VALUE_H_INCLUDED) #endif // !defined(VALUE_H_INCLUDED)