Moved DroidFish project to trunk/

This commit is contained in:
Peter Osterlund 2011-11-12 19:44:06 +00:00
parent 7a793cd44e
commit 252789e186
134 changed files with 29968 additions and 0 deletions

8
DroidFish/.classpath Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry combineaccessrules="false" kind="src" path="/CuckooChessEngine"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
<stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;launchConfigurationWorkingSet editPageId=&quot;org.eclipse.ui.resourceWorkingSetPage&quot; factoryID=&quot;org.eclipse.ui.internal.WorkingSetFactory&quot; id=&quot;1278546956920_168&quot; label=&quot;working set&quot; name=&quot;working set&quot;&gt;&#10;&lt;item factoryID=&quot;org.eclipse.ui.internal.model.ResourceFactory&quot; path=&quot;/DroidFish/libs&quot; type=&quot;2&quot;/&gt;&#10;&lt;/launchConfigurationWorkingSet&gt;}"/>
<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;launchConfigurationWorkingSet editPageId=&quot;org.eclipse.ui.resourceWorkingSetPage&quot; factoryID=&quot;org.eclipse.ui.internal.WorkingSetFactory&quot; id=&quot;1278546974224_175&quot; label=&quot;workingSet&quot; name=&quot;workingSet&quot;&gt;&#10;&lt;item factoryID=&quot;org.eclipse.ui.internal.model.ResourceFactory&quot; path=&quot;/DroidFish/jni&quot; type=&quot;2&quot;/&gt;&#10;&lt;/launchConfigurationWorkingSet&gt;}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="/home/petero/phone/htc/android-ndk-r7/ndk-build"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,auto,"/>
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/DroidFish}"/>
</launchConfiguration>

43
DroidFish/.project Normal file
View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>DroidFish</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>auto,full,incremental,</triggers>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value>&lt;project&gt;/.externalToolBuilders/Native_Builder.launch</value>
</dictionary>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,8 @@
#Fri Jul 09 17:18:36 CEST 2010
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.6

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.petero.droidfish"
android:versionCode="42"
android:versionName="1.36"
android:installLocation="auto">
<supports-screens android:largeScreens="true"
android:anyDensity="true" />
<application android:icon="@drawable/icon"
android:label="@string/app_name"
android:debuggable="false">
<activity android:name=".DroidFish"
android:label="@string/app_name"
android:configChanges="orientation">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/x-chess-pgn" />
</intent-filter>
</activity>
<activity android:name=".activities.Preferences"
android:label="Preferences">
</activity>
<activity android:name=".activities.EditBoard"
android:label="Edit Board"
android:configChanges="orientation">
</activity>
<activity android:name=".activities.EditPGNLoad"
android:label="Edit File / Load Game"
android:configChanges="orientation">
</activity>
<activity android:name=".activities.EditPGNSave"
android:label="Edit File / Save Game"
android:configChanges="orientation">
</activity>
<activity android:name=".activities.LoadScid"
android:label="Load Scid Game"
android:configChanges="orientation">
</activity>
<activity android:name=".activities.CPUWarning"
android:label="CPU Warning">
</activity>
<activity android:name=".activities.EditHeaders"
android:label="Edit Headers">
</activity>
<activity android:name=".activities.EditComments"
android:label="Edit Comments">
</activity>
</application>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-sdk android:minSdkVersion="3"
android:targetSdkVersion="10"/>
</manifest>

Binary file not shown.

17
DroidFish/jni/Android.mk Normal file
View File

@ -0,0 +1,17 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := jni
LOCAL_SRC_FILES := jni.cpp
LOCAL_CFLAGS := \
-mandroid \
-DTARGET_OS=android -D__ANDROID__ \
-isystem $(SYSROOT)/usr/include
LOCAL_STATIC_LIBRARIES := stockfish
include $(BUILD_SHARED_LIBRARY)
include jni/stockfish/Android.mk

View File

@ -0,0 +1,2 @@
APP_ABI := all
APP_STL := stlport_static

169
DroidFish/jni/jni.cpp Normal file
View File

@ -0,0 +1,169 @@
/*
DroidFish - An Android chess program.
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <jni.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h>
#include <deque>
#include <vector>
#include <sys/time.h>
int main(int argc, char* argv[]);
static int fdFromChild = -1;
static int fdToChild = -1;
/*
* Class: org_petero_droidfish_engine_NativePipedProcess
* Method: startProcess
* Signature: ()V
*/
extern "C" JNIEXPORT void JNICALL Java_org_petero_droidfish_engine_NativePipedProcess_startProcess
(JNIEnv* env, jobject obj)
{
int fd1[2]; /* parent -> child */
int fd2[2]; /* child -> parent */
if (pipe(fd1) < 0)
exit(1);
if (pipe(fd2) < 0)
exit(1);
int childpid = fork();
if (childpid == -1) {
exit(1);
}
if (childpid == 0) {
close(fd1[1]);
close(fd2[0]);
close(0); dup(fd1[0]); close(fd1[0]);
close(1); dup(fd2[1]); close(fd2[1]);
close(2); dup(1);
static char* argv[] = {(char*)"stockfish", NULL};
nice(10);
main(1, argv);
_exit(0);
} else {
close(fd1[0]);
close(fd2[1]);
fdFromChild = fd2[0];
fdToChild = fd1[1];
fcntl(fdFromChild, F_SETFL, O_NONBLOCK);
}
}
static std::deque<char> inBuf;
static bool getNextChar(int& c, int timeoutMillis) {
if (inBuf.empty()) {
fd_set readfds, writefds;
FD_ZERO(&readfds);
FD_SET(fdFromChild, &readfds);
struct timeval tv;
tv.tv_sec = timeoutMillis / 1000;
tv.tv_usec = (timeoutMillis % 1000) * 1000;
int ret = select(fdFromChild + 1, &readfds, NULL, NULL, &tv);
if (ret < 0)
return false;
static char buf[4096];
int len = read(fdFromChild, &buf[0], sizeof(buf));
for (int i = 0; i < len; i++)
inBuf.push_back(buf[i]);
}
if (inBuf.empty()) {
c = -1;
return true;
}
c = inBuf.front();
inBuf.pop_front();
return true;
}
static std::vector<char> lineBuf;
/*
* Class: org_petero_droidfish_engine_NativePipedProcess
* Method: readFromProcess
* Signature: (I)Ljava/lang/String;
*/
extern "C" JNIEXPORT jstring JNICALL Java_org_petero_droidfish_engine_NativePipedProcess_readFromProcess
(JNIEnv* env, jobject obj, jint timeoutMillis)
{
struct timeval tv0, tv1;
while (true) {
int c;
gettimeofday(&tv0, NULL);
if (!getNextChar(c, timeoutMillis))
return 0; // Error
gettimeofday(&tv1, NULL);
int elapsedMillis = (tv1.tv_sec - tv0.tv_sec) * 1000 + (tv1.tv_usec - tv0.tv_usec) / 1000;
if (elapsedMillis > 0) {
timeoutMillis -= elapsedMillis;
if (timeoutMillis < 0) timeoutMillis = 0;
}
if (c == -1) { // Timeout
static char emptyString = 0;
return (*env).NewStringUTF(&emptyString);
}
if (c == '\n' || (c == '\r')) {
if (lineBuf.size() > 0) {
lineBuf.push_back(0);
jstring ret = (*env).NewStringUTF(&lineBuf[0]);
lineBuf.clear();
return ret;
}
} else {
lineBuf.push_back((char)c);
}
}
}
/*
* Class: org_petero_droidfish_engine_NativePipedProcess
* Method: writeToProcess
* Signature: (Ljava/lang/String;)V
*/
extern "C" JNIEXPORT void JNICALL Java_org_petero_droidfish_engine_NativePipedProcess_writeToProcess
(JNIEnv* env, jobject obj, jstring msg)
{
const char* str = (*env).GetStringUTFChars(msg, NULL);
if (str) {
int len = strlen(str);
int written = 0;
while (written < len) {
int n = write(fdToChild, &str[written], len - written);
if (n <= 0)
break;
written += n;
}
(*env).ReleaseStringUTFChars(msg, str);
}
}
/*
* Class: org_petero_droidfish_engine_NativePipedProcess
* Method: getNPhysicalProcessors
* Signature: ()I
*/
extern "C" JNIEXPORT jint JNICALL Java_org_petero_droidfish_engine_NativePipedProcess_getNPhysicalProcessors
(JNIEnv *, jclass)
{
return sysconf(_SC_NPROCESSORS_ONLN);
}

View File

@ -0,0 +1,22 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := stockfish
LOCAL_SRC_FILES := \
evaluate.cpp move.cpp search.cpp \
benchmark.cpp movegen.cpp tt.cpp \
bitbase.cpp main.cpp movepick.cpp uci.cpp \
bitboard.cpp pawns.cpp ucioption.cpp \
book.cpp material.cpp position.cpp \
endgame.cpp misc.cpp timeman.cpp thread.cpp
LOCAL_CFLAGS := -I$(LOCAL_PATH)/../stlport/stlport \
-mandroid \
-DTARGET_OS=android -D__ANDROID__ \
-isystem $(SYSROOT)/usr/include \
-DNO_PREFETCH=1
LOCAL_STATIC_LIBRARIES := stlport
include $(BUILD_STATIC_LIBRARY)

View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

@ -0,0 +1,153 @@
/*
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/>.
*/
#include <fstream>
#include <iostream>
#include <vector>
#include "position.h"
#include "search.h"
#include "ucioption.h"
using namespace std;
static const string Defaults[] = {
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -",
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -",
"4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
"rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14",
"r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14",
"r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15",
"r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13",
"r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16",
"4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w - - 1 17",
"2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b KQ - 0 11",
"r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16",
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22",
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26",
""
};
/// benchmark() runs a simple benchmark by letting Stockfish analyze a set
/// of positions for a given limit each. There are five parameters; the
/// transposition table size, the number of search threads that should
/// be used, the limit value spent for each position (optional, default
/// is ply 12), an optional file name where to look for positions in fen
/// format (default are the BenchmarkPositions defined above) and the type
/// of the limit value: depth (default), time in secs or number of nodes.
/// The analysis is written to a file named bench.txt.
void benchmark(int argc, char* argv[]) {
vector<string> fenList;
SearchLimits limits;
int64_t totalNodes;
int time;
// Load default positions
for (int i = 0; !Defaults[i].empty(); i++)
fenList.push_back(Defaults[i]);
// Assign default values to missing arguments
string ttSize = argc > 2 ? argv[2] : "128";
string threads = argc > 3 ? argv[3] : "1";
string valStr = argc > 4 ? argv[4] : "12";
string fenFile = argc > 5 ? argv[5] : "default";
string valType = argc > 6 ? argv[6] : "depth";
Options["Hash"].set_value(ttSize);
Options["Threads"].set_value(threads);
Options["OwnBook"].set_value("false");
// Search should be limited by nodes, time or depth ?
if (valType == "nodes")
limits.maxNodes = atoi(valStr.c_str());
else if (valType == "time")
limits.maxTime = 1000 * atoi(valStr.c_str()); // maxTime is in ms
else
limits.maxDepth = atoi(valStr.c_str());
// Do we need to load positions from a given FEN file ?
if (fenFile != "default")
{
string fen;
ifstream f(fenFile.c_str());
if (f.is_open())
{
fenList.clear();
while (getline(f, fen))
if (!fen.empty())
fenList.push_back(fen);
f.close();
}
else
{
cerr << "Unable to open FEN file " << fenFile << endl;
exit(EXIT_FAILURE);
}
}
// Ok, let's start the benchmark !
totalNodes = 0;
time = get_system_time();
for (size_t i = 0; i < fenList.size(); i++)
{
Move moves[] = { MOVE_NONE };
Position pos(fenList[i], false, 0);
cerr << "\nBench position: " << i + 1 << '/' << fenList.size() << endl;
if (valType == "perft")
{
int64_t cnt = perft(pos, limits.maxDepth * ONE_PLY);
totalNodes += cnt;
cerr << "\nPerft " << limits.maxDepth << " nodes counted: " << cnt << endl;
}
else
{
if (!think(pos, limits, moves))
break;
totalNodes += pos.nodes_searched();
}
}
time = get_system_time() - time;
cerr << "\n==============================="
<< "\nTotal time (ms) : " << time
<< "\nNodes searched : " << totalNodes
<< "\nNodes/second : " << (int)(totalNodes / (time / 1000.0)) << endl << endl;
// MS Visual C++ debug window always unconditionally closes when program
// exits, this is bad because we want to read results before.
#if (defined(WINDOWS) || defined(WIN32) || defined(WIN64))
cerr << "Press any key to exit" << endl;
cin >> time;
#endif
}

View File

@ -0,0 +1,284 @@
/*
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/>.
*/
#include <cassert>
#include "bitboard.h"
#include "types.h"
namespace {
enum Result {
RESULT_UNKNOWN,
RESULT_INVALID,
RESULT_WIN,
RESULT_LOSS,
RESULT_DRAW
};
struct KPKPosition {
void from_index(int index);
bool is_legal() const;
bool is_immediate_draw() const;
bool is_immediate_win() const;
Bitboard wk_attacks() const { return StepAttacksBB[WK][whiteKingSquare]; }
Bitboard bk_attacks() const { return StepAttacksBB[BK][blackKingSquare]; }
Bitboard pawn_attacks() const { return StepAttacksBB[WP][pawnSquare]; }
Square whiteKingSquare, blackKingSquare, pawnSquare;
Color sideToMove;
};
// The possible pawns squares are 24, the first 4 files and ranks from 2 to 7
const int IndexMax = 2 * 24 * 64 * 64; // color * wp_sq * wk_sq * bk_sq
// Each uint32_t stores results of 32 positions, one per bit
uint32_t KPKBitbase[IndexMax / 32];
Result classify_wtm(const KPKPosition& pos, const Result bb[]);
Result classify_btm(const KPKPosition& pos, const Result bb[]);
int compute_index(Square wksq, Square bksq, Square wpsq, Color stm);
}
uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm) {
int index = compute_index(wksq, bksq, wpsq, stm);
return KPKBitbase[index / 32] & (1 << (index & 31));
}
void init_kpk_bitbase() {
Result bb[IndexMax];
KPKPosition pos;
bool repeat;
// Initialize table
for (int 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 (int 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);
// Map 32 position results into one KPKBitbase[] entry
for (int i = 0; i < IndexMax / 32; i++)
for (int j = 0; j < 32; j++)
if (bb[32 * i + j] == RESULT_WIN || bb[32 * i + j] == RESULT_LOSS)
KPKBitbase[i] |= (1 << j);
}
namespace {
// A KPK bitbase index is an integer in [0, IndexMax] range
//
// Information is mapped in this way
//
// bit 0: side to move (WHITE or BLACK)
// bit 1- 6: black king square (from SQ_A1 to SQ_H8)
// bit 7-12: white king square (from SQ_A1 to SQ_H8)
// bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn rank - 1 (from RANK_2 - 1 to RANK_7 - 1)
int compute_index(Square wksq, Square bksq, Square wpsq, Color stm) {
assert(square_file(wpsq) <= FILE_D);
int p = int(square_file(wpsq)) + 4 * int(square_rank(wpsq) - 1);
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) {
int s = (index / 8192) % 24;
sideToMove = Color(index % 2);
blackKingSquare = Square((index / 2) % 64);
whiteKingSquare = Square((index / 128) % 64);
pawnSquare = make_square(File(s % 4), Rank(s / 4 + 1));
}
bool KPKPosition::is_legal() const {
if ( whiteKingSquare == pawnSquare
|| whiteKingSquare == blackKingSquare
|| blackKingSquare == pawnSquare)
return false;
if (sideToMove == WHITE)
{
if ( bit_is_set(wk_attacks(), blackKingSquare)
|| bit_is_set(pawn_attacks(), blackKingSquare))
return false;
}
else if (bit_is_set(bk_attacks(), whiteKingSquare))
return false;
return true;
}
bool KPKPosition::is_immediate_draw() const {
if (sideToMove == BLACK)
{
Bitboard wka = wk_attacks();
Bitboard bka = bk_attacks();
// Case 1: Stalemate
if ((bka & ~(wka | pawn_attacks())) == EmptyBoardBB)
return true;
// Case 2: King can capture pawn
if (bit_is_set(bka, pawnSquare) && !bit_is_set(wka, pawnSquare))
return true;
}
else
{
// Case 1: Stalemate (possible pawn files are only from A to D)
if ( whiteKingSquare == SQ_A8
&& pawnSquare == SQ_A7
&& (blackKingSquare == SQ_C7 || blackKingSquare == SQ_C8))
return true;
}
return false;
}
bool KPKPosition::is_immediate_win() const {
// The position is an immediate win if it is white to move and the
// white pawn can be promoted without getting captured.
return sideToMove == WHITE
&& square_rank(pawnSquare) == RANK_7
&& whiteKingSquare != pawnSquare + DELTA_N
&& ( square_distance(blackKingSquare, pawnSquare + DELTA_N) > 1
|| bit_is_set(wk_attacks(), pawnSquare + DELTA_N));
}
Result classify_wtm(const KPKPosition& pos, const Result bb[]) {
// 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
// classified as RESULT_DRAW, the current position is classified RESULT_DRAW
// otherwise the current position is classified as RESULT_UNKNOWN.
bool unknownFound = false;
Bitboard b;
Square s;
Result r;
// King moves
b = pos.wk_attacks();
while (b)
{
s = pop_1st_bit(&b);
r = bb[compute_index(s, pos.blackKingSquare, pos.pawnSquare, BLACK)];
if (r == RESULT_LOSS)
return RESULT_WIN;
if (r == RESULT_UNKNOWN)
unknownFound = true;
}
// Pawn moves
if (square_rank(pos.pawnSquare) < RANK_7)
{
s = pos.pawnSquare + DELTA_N;
r = bb[compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK)];
if (r == RESULT_LOSS)
return RESULT_WIN;
if (r == RESULT_UNKNOWN)
unknownFound = true;
// Double pawn push
if ( square_rank(s) == RANK_3
&& s != pos.whiteKingSquare
&& s != pos.blackKingSquare)
{
s += DELTA_N;
r = bb[compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK)];
if (r == RESULT_LOSS)
return RESULT_WIN;
if (r == RESULT_UNKNOWN)
unknownFound = true;
}
}
return unknownFound ? RESULT_UNKNOWN : RESULT_DRAW;
}
Result classify_btm(const KPKPosition& pos, const Result bb[]) {
// 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
// classified as RESULT_WIN, the current position is classified as
// RESULT_LOSS. Otherwise, the current position is classified as
// RESULT_UNKNOWN.
bool unknownFound = false;
Bitboard b;
Square s;
Result r;
// King moves
b = pos.bk_attacks();
while (b)
{
s = pop_1st_bit(&b);
r = bb[compute_index(pos.whiteKingSquare, s, pos.pawnSquare, WHITE)];
if (r == RESULT_DRAW)
return RESULT_DRAW;
if (r == RESULT_UNKNOWN)
unknownFound = true;
}
return unknownFound ? RESULT_UNKNOWN : RESULT_LOSS;
}
}

View File

@ -0,0 +1,486 @@
/*
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/>.
*/
#include <iostream>
#include "bitboard.h"
#include "bitcount.h"
#if defined(IS_64BIT)
const uint64_t BMult[64] = {
0x0440049104032280ULL, 0x1021023C82008040ULL, 0x0404040082000048ULL,
0x48C4440084048090ULL, 0x2801104026490000ULL, 0x4100880442040800ULL,
0x0181011002E06040ULL, 0x9101004104200E00ULL, 0x1240848848310401ULL,
0x2000142828050024ULL, 0x00001004024D5000ULL, 0x0102044400800200ULL,
0x8108108820112000ULL, 0xA880818210C00046ULL, 0x4008008801082000ULL,
0x0060882404049400ULL, 0x0104402004240810ULL, 0x000A002084250200ULL,
0x00100B0880801100ULL, 0x0004080201220101ULL, 0x0044008080A00000ULL,
0x0000202200842000ULL, 0x5006004882D00808ULL, 0x0000200045080802ULL,
0x0086100020200601ULL, 0xA802080A20112C02ULL, 0x0080411218080900ULL,
0x000200A0880080A0ULL, 0x9A01010000104000ULL, 0x0028008003100080ULL,
0x0211021004480417ULL, 0x0401004188220806ULL, 0x00825051400C2006ULL,
0x00140C0210943000ULL, 0x0000242800300080ULL, 0x00C2208120080200ULL,
0x2430008200002200ULL, 0x1010100112008040ULL, 0x8141050100020842ULL,
0x0000822081014405ULL, 0x800C049E40400804ULL, 0x4A0404028A000820ULL,
0x0022060201041200ULL, 0x0360904200840801ULL, 0x0881A08208800400ULL,
0x0060202C00400420ULL, 0x1204440086061400ULL, 0x0008184042804040ULL,
0x0064040315300400ULL, 0x0C01008801090A00ULL, 0x0808010401140C00ULL,
0x04004830C2020040ULL, 0x0080005002020054ULL, 0x40000C14481A0490ULL,
0x0010500101042048ULL, 0x1010100200424000ULL, 0x0000640901901040ULL,
0x00000A0201014840ULL, 0x00840082AA011002ULL, 0x010010840084240AULL,
0x0420400810420608ULL, 0x8D40230408102100ULL, 0x4A00200612222409ULL,
0x0A08520292120600ULL
};
const uint64_t RMult[64] = {
0x0A8002C000108020ULL, 0x4440200140003000ULL, 0x8080200010011880ULL,
0x0380180080141000ULL, 0x1A00060008211044ULL, 0x410001000A0C0008ULL,
0x9500060004008100ULL, 0x0100024284A20700ULL, 0x0000802140008000ULL,
0x0080C01002A00840ULL, 0x0402004282011020ULL, 0x9862000820420050ULL,
0x0001001448011100ULL, 0x6432800200800400ULL, 0x040100010002000CULL,
0x0002800D0010C080ULL, 0x90C0008000803042ULL, 0x4010004000200041ULL,
0x0003010010200040ULL, 0x0A40828028001000ULL, 0x0123010008000430ULL,
0x0024008004020080ULL, 0x0060040001104802ULL, 0x00582200028400D1ULL,
0x4000802080044000ULL, 0x0408208200420308ULL, 0x0610038080102000ULL,
0x3601000900100020ULL, 0x0000080080040180ULL, 0x00C2020080040080ULL,
0x0080084400100102ULL, 0x4022408200014401ULL, 0x0040052040800082ULL,
0x0B08200280804000ULL, 0x008A80A008801000ULL, 0x4000480080801000ULL,
0x0911808800801401ULL, 0x822A003002001894ULL, 0x401068091400108AULL,
0x000004A10A00004CULL, 0x2000800640008024ULL, 0x1486408102020020ULL,
0x000100A000D50041ULL, 0x00810050020B0020ULL, 0x0204000800808004ULL,
0x00020048100A000CULL, 0x0112000831020004ULL, 0x0009000040810002ULL,
0x0440490200208200ULL, 0x8910401000200040ULL, 0x6404200050008480ULL,
0x4B824A2010010100ULL, 0x04080801810C0080ULL, 0x00000400802A0080ULL,
0x8224080110026400ULL, 0x40002C4104088200ULL, 0x01002100104A0282ULL,
0x1208400811048021ULL, 0x3201014A40D02001ULL, 0x0005100019200501ULL,
0x0101000208001005ULL, 0x0002008450080702ULL, 0x001002080301D00CULL,
0x410201CE5C030092ULL
};
const int BShift[64] = {
58, 59, 59, 59, 59, 59, 59, 58, 59, 59, 59, 59, 59, 59, 59, 59,
59, 59, 57, 57, 57, 57, 59, 59, 59, 59, 57, 55, 55, 57, 59, 59,
59, 59, 57, 55, 55, 57, 59, 59, 59, 59, 57, 57, 57, 57, 59, 59,
59, 59, 59, 59, 59, 59, 59, 59, 58, 59, 59, 59, 59, 59, 59, 58
};
const int RShift[64] = {
52, 53, 53, 53, 53, 53, 53, 52, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 52, 53, 53, 53, 53, 53, 53, 52
};
#else // if !defined(IS_64BIT)
const uint64_t BMult[64] = {
0x54142844C6A22981ULL, 0x710358A6EA25C19EULL, 0x704F746D63A4A8DCULL,
0xBFED1A0B80F838C5ULL, 0x90561D5631E62110ULL, 0x2804260376E60944ULL,
0x84A656409AA76871ULL, 0xF0267F64C28B6197ULL, 0x70764EBB762F0585ULL,
0x92AA09E0CFE161DEULL, 0x41EE1F6BB266F60EULL, 0xDDCBF04F6039C444ULL,
0x5A3FAB7BAC0D988AULL, 0xD3727877FA4EAA03ULL, 0xD988402D868DDAAEULL,
0x812B291AFA075C7CULL, 0x94FAF987B685A932ULL, 0x3ED867D8470D08DBULL,
0x92517660B8901DE8ULL, 0x2D97E43E058814B4ULL, 0x880A10C220B25582ULL,
0xC7C6520D1F1A0477ULL, 0xDBFC7FBCD7656AA6ULL, 0x78B1B9BFB1A2B84FULL,
0x2F20037F112A0BC1ULL, 0x657171EA2269A916ULL, 0xC08302B07142210EULL,
0x0880A4403064080BULL, 0x3602420842208C00ULL, 0x852800DC7E0B6602ULL,
0x595A3FBBAA0F03B2ULL, 0x9F01411558159D5EULL, 0x2B4A4A5F88B394F2ULL,
0x4AFCBFFC292DD03AULL, 0x4A4094A3B3F10522ULL, 0xB06F00B491F30048ULL,
0xD5B3820280D77004ULL, 0x8B2E01E7C8E57A75ULL, 0x2D342794E886C2E6ULL,
0xC302C410CDE21461ULL, 0x111F426F1379C274ULL, 0xE0569220ABB31588ULL,
0x5026D3064D453324ULL, 0xE2076040C343CD8AULL, 0x93EFD1E1738021EEULL,
0xB680804BED143132ULL, 0x44E361B21986944CULL, 0x44C60170EF5C598CULL,
0xF4DA475C195C9C94ULL, 0xA3AFBB5F72060B1DULL, 0xBC75F410E41C4FFCULL,
0xB51C099390520922ULL, 0x902C011F8F8EC368ULL, 0x950B56B3D6F5490AULL,
0x3909E0635BF202D0ULL, 0x5744F90206EC10CCULL, 0xDC59FD76317ABBC1ULL,
0x881C7C67FCBFC4F6ULL, 0x47CA41E7E440D423ULL, 0xEB0C88112048D004ULL,
0x51C60E04359AEF1AULL, 0x1AA1FE0E957A5554ULL, 0xDD9448DB4F5E3104ULL,
0xDC01F6DCA4BEBBDCULL,
};
const uint64_t RMult[64] = {
0xD7445CDEC88002C0ULL, 0xD0A505C1F2001722ULL, 0xE065D1C896002182ULL,
0x9A8C41E75A000892ULL, 0x8900B10C89002AA8ULL, 0x9B28D1C1D60005A2ULL,
0x015D6C88DE002D9AULL, 0xB1DBFC802E8016A9ULL, 0x149A1042D9D60029ULL,
0xB9C08050599E002FULL, 0x132208C3AF300403ULL, 0xC1000CE2E9C50070ULL,
0x9D9AA13C99020012ULL, 0xB6B078DAF71E0046ULL, 0x9D880182FB6E002EULL,
0x52889F467E850037ULL, 0xDA6DC008D19A8480ULL, 0x468286034F902420ULL,
0x7140AC09DC54C020ULL, 0xD76FFFFA39548808ULL, 0xEA901C4141500808ULL,
0xC91004093F953A02ULL, 0x02882AFA8F6BB402ULL, 0xAEBE335692442C01ULL,
0x0E904A22079FB91EULL, 0x13A514851055F606ULL, 0x76C782018C8FE632ULL,
0x1DC012A9D116DA06ULL, 0x3C9E0037264FFFA6ULL, 0x2036002853C6E4A2ULL,
0xE3FE08500AFB47D4ULL, 0xF38AF25C86B025C2ULL, 0xC0800E2182CF9A40ULL,
0x72002480D1F60673ULL, 0x2500200BAE6E9B53ULL, 0xC60018C1EEFCA252ULL,
0x0600590473E3608AULL, 0x46002C4AB3FE51B2ULL, 0xA200011486BCC8D2ULL,
0xB680078095784C63ULL, 0x2742002639BF11AEULL, 0xC7D60021A5BDB142ULL,
0xC8C04016BB83D820ULL, 0xBD520028123B4842ULL, 0x9D1600344AC2A832ULL,
0x6A808005631C8A05ULL, 0x604600A148D5389AULL, 0xE2E40103D40DEA65ULL,
0x945B5A0087C62A81ULL, 0x012DC200CD82D28EULL, 0x2431C600B5F9EF76ULL,
0xFB142A006A9B314AULL, 0x06870E00A1C97D62ULL, 0x2A9DB2004A2689A2ULL,
0xD3594600CAF5D1A2ULL, 0xEE0E4900439344A7ULL, 0x89C4D266CA25007AULL,
0x3E0013A2743F97E3ULL, 0x0180E31A0431378AULL, 0x3A9E465A4D42A512ULL,
0x98D0A11A0C0D9CC2ULL, 0x8E711C1ABA19B01EULL, 0x8DCDC836DD201142ULL,
0x5AC08A4735370479ULL,
};
const int BShift[64] = {
26, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 25, 25, 25, 25, 27, 27, 27, 27, 25, 23, 23, 25, 27, 27,
27, 27, 25, 23, 23, 25, 27, 27, 27, 27, 25, 25, 25, 25, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 26
};
const int RShift[64] = {
20, 21, 21, 21, 21, 21, 21, 20, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 20, 21, 21, 21, 21, 21, 21, 20
};
#endif // defined(IS_64BIT)
// Global bitboards definitions with static storage duration are
// automatically set to zero before enter main().
Bitboard RMask[64];
int RAttackIndex[64];
Bitboard RAttacks[0x19000];
Bitboard BMask[64];
int BAttackIndex[64];
Bitboard BAttacks[0x1480];
Bitboard SetMaskBB[65];
Bitboard ClearMaskBB[65];
Bitboard SquaresByColorBB[2];
Bitboard FileBB[8];
Bitboard RankBB[8];
Bitboard NeighboringFilesBB[8];
Bitboard ThisAndNeighboringFilesBB[8];
Bitboard InFrontBB[2][8];
Bitboard StepAttacksBB[16][64];
Bitboard BetweenBB[64][64];
Bitboard SquaresInFrontMask[2][64];
Bitboard PassedPawnMask[2][64];
Bitboard AttackSpanMask[2][64];
Bitboard BishopPseudoAttacks[64];
Bitboard RookPseudoAttacks[64];
Bitboard QueenPseudoAttacks[64];
uint8_t BitCount8Bit[256];
namespace {
void init_masks();
void init_step_attacks();
void init_pseudo_attacks();
void init_between_bitboards();
Bitboard index_to_bitboard(int index, Bitboard mask);
Bitboard sliding_attacks(int sq, Bitboard occupied, int deltas[][2],
int fmin, int fmax, int rmin, int rmax);
void init_sliding_attacks(Bitboard attacks[], int attackIndex[], Bitboard mask[],
const int shift[], const Bitboard mult[], int deltas[][2]);
}
/// print_bitboard() prints a bitboard in an easily readable format to the
/// standard output. This is sometimes useful for debugging.
void print_bitboard(Bitboard b) {
for (Rank r = RANK_8; r >= RANK_1; r--)
{
std::cout << "+---+---+---+---+---+---+---+---+" << '\n';
for (File f = FILE_A; f <= FILE_H; f++)
std::cout << "| " << (bit_is_set(b, make_square(f, r)) ? 'X' : ' ') << ' ';
std::cout << "|\n";
}
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
}
/// first_1() finds the least significant nonzero bit in a nonzero bitboard.
/// pop_1st_bit() finds and clears the least significant nonzero bit in a
/// nonzero bitboard.
#if defined(IS_64BIT) && !defined(USE_BSFQ)
static CACHE_LINE_ALIGNMENT
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, 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, 51, 60, 42, 59, 58
};
Square first_1(Bitboard b) {
return Square(BitTable[((b & -b) * 0x218a392cd3d5dbfULL) >> 58]);
}
Square pop_1st_bit(Bitboard* b) {
Bitboard bb = *b;
*b &= (*b - 1);
return Square(BitTable[((bb & -bb) * 0x218a392cd3d5dbfULL) >> 58]);
}
#elif !defined(USE_BSFQ)
static CACHE_LINE_ALIGNMENT
const int BitTable[64] = {
63, 30, 3, 32, 25, 41, 22, 33, 15, 50, 42, 13, 11, 53, 19, 34, 61, 29, 2,
51, 21, 43, 45, 10, 18, 47, 1, 54, 9, 57, 0, 35, 62, 31, 40, 4, 49, 5,
52, 26, 60, 6, 23, 44, 46, 27, 56, 16, 7, 39, 48, 24, 59, 14, 12, 55, 38,
28, 58, 20, 37, 17, 36, 8
};
Square first_1(Bitboard b) {
b ^= (b - 1);
uint32_t fold = int(b) ^ int(b >> 32);
return Square(BitTable[(fold * 0x783a9b23) >> 26]);
}
// Use type-punning
union b_union {
Bitboard b;
struct {
#if defined (BIGENDIAN)
uint32_t h;
uint32_t l;
#else
uint32_t l;
uint32_t h;
#endif
} dw;
};
Square pop_1st_bit(Bitboard* bb) {
b_union u;
Square ret;
u.b = *bb;
if (u.dw.l)
{
ret = Square(BitTable[((u.dw.l ^ (u.dw.l - 1)) * 0x783a9b23) >> 26]);
u.dw.l &= (u.dw.l - 1);
*bb = u.b;
return ret;
}
ret = Square(BitTable[((~(u.dw.h ^ (u.dw.h - 1))) * 0x783a9b23) >> 26]);
u.dw.h &= (u.dw.h - 1);
*bb = u.b;
return ret;
}
#endif // !defined(USE_BSFQ)
/// init_bitboards() initializes various bitboard arrays. It is called during
/// program initialization.
void init_bitboards() {
int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
init_masks();
init_step_attacks();
init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift, RMult, rookDeltas);
init_sliding_attacks(BAttacks, BAttackIndex, BMask, BShift, BMult, bishopDeltas);
init_pseudo_attacks();
init_between_bitboards();
}
namespace {
// All functions below are used to precompute various bitboards during
// program initialization. Some of the functions may be difficult to
// understand, but they all seem to work correctly, and it should never
// be necessary to touch any of them.
void init_masks() {
SquaresByColorBB[DARK] = 0xAA55AA55AA55AA55ULL;
SquaresByColorBB[LIGHT] = ~SquaresByColorBB[DARK];
FileBB[FILE_A] = FileABB;
RankBB[RANK_1] = Rank1BB;
for (int f = FILE_B; f <= FILE_H; f++)
{
FileBB[f] = FileBB[f - 1] << 1;
RankBB[f] = RankBB[f - 1] << 8;
}
for (int f = FILE_A; f <= FILE_H; f++)
{
NeighboringFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
ThisAndNeighboringFilesBB[f] = FileBB[f] | NeighboringFilesBB[f];
}
for (int rw = RANK_7, rb = RANK_2; rw >= RANK_1; rw--, rb++)
{
InFrontBB[WHITE][rw] = InFrontBB[WHITE][rw + 1] | RankBB[rw + 1];
InFrontBB[BLACK][rb] = InFrontBB[BLACK][rb - 1] | RankBB[rb - 1];
}
SetMaskBB[SQ_NONE] = EmptyBoardBB;
ClearMaskBB[SQ_NONE] = ~SetMaskBB[SQ_NONE];
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
SetMaskBB[s] = (1ULL << s);
ClearMaskBB[s] = ~SetMaskBB[s];
}
for (Color c = WHITE; c <= BLACK; c++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
SquaresInFrontMask[c][s] = in_front_bb(c, s) & file_bb(s);
PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_neighboring_files_bb(s);
AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
}
for (Bitboard b = 0; b < 256; b++)
BitCount8Bit[b] = (uint8_t)count_1s<CNT32>(b);
}
void init_step_attacks() {
const int step[][9] = {
{0},
{7,9,0}, {17,15,10,6,-6,-10,-15,-17,0}, {0}, {0}, {0},
{9,7,-7,-9,8,1,-1,-8,0}, {0}, {0},
{-7,-9,0}, {17,15,10,6,-6,-10,-15,-17,0}, {0}, {0}, {0},
{9,7,-7,-9,8,1,-1,-8,0}
};
for (Square s = SQ_A1; s <= SQ_H8; s++)
for (Piece pc = WP; pc <= BK; pc++)
for (int k = 0; step[pc][k] != 0; k++)
{
Square to = s + Square(step[pc][k]);
if (square_is_ok(to) && square_distance(s, to) < 3)
set_bit(&StepAttacksBB[pc][s], to);
}
}
Bitboard sliding_attacks(int sq, Bitboard occupied, int deltas[][2],
int fmin, int fmax, int rmin, int rmax) {
int dx, dy, f, r;
int rk = sq / 8;
int fl = sq % 8;
Bitboard attacks = EmptyBoardBB;
for (int i = 0; i < 4; i++)
{
dx = deltas[i][0];
dy = deltas[i][1];
f = fl + dx;
r = rk + dy;
while ( (dx == 0 || (f >= fmin && f <= fmax))
&& (dy == 0 || (r >= rmin && r <= rmax)))
{
attacks |= (1ULL << (f + r * 8));
if (occupied & (1ULL << (f + r * 8)))
break;
f += dx;
r += dy;
}
}
return attacks;
}
Bitboard index_to_bitboard(int index, Bitboard mask) {
Bitboard result = EmptyBoardBB;
int sq, cnt = 0;
while (mask)
{
sq = pop_1st_bit(&mask);
if (index & (1 << cnt++))
result |= (1ULL << sq);
}
return result;
}
void init_sliding_attacks(Bitboard attacks[], int attackIndex[], Bitboard mask[],
const int shift[], const Bitboard mult[], int deltas[][2]) {
Bitboard b, v;
int i, j, index;
for (i = index = 0; i < 64; i++)
{
attackIndex[i] = index;
mask[i] = sliding_attacks(i, 0, deltas, 1, 6, 1, 6);
j = 1 << ((CpuIs64Bit ? 64 : 32) - shift[i]);
for (int k = 0; k < j; k++)
{
b = index_to_bitboard(k, mask[i]);
v = CpuIs64Bit ? b * mult[i] : unsigned(b * mult[i] ^ (b >> 32) * (mult[i] >> 32));
attacks[index + (v >> shift[i])] = sliding_attacks(i, b, deltas, 0, 7, 0, 7);
}
index += j;
}
}
void init_pseudo_attacks() {
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
BishopPseudoAttacks[s] = bishop_attacks_bb(s, EmptyBoardBB);
RookPseudoAttacks[s] = rook_attacks_bb(s, EmptyBoardBB);
QueenPseudoAttacks[s] = queen_attacks_bb(s, EmptyBoardBB);
}
}
void init_between_bitboards() {
Square s1, s2, s3, d;
int f, r;
for (s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (bit_is_set(QueenPseudoAttacks[s1], s2))
{
f = file_distance(s1, s2);
r = rank_distance(s1, s2);
d = (s2 - s1) / Max(f, r);
for (s3 = s1 + d; s3 != s2; s3 += d)
set_bit(&(BetweenBB[s1][s2]), s3);
}
}
}

View File

@ -0,0 +1,295 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(BITBOARD_H_INCLUDED)
#define BITBOARD_H_INCLUDED
#include "types.h"
const Bitboard EmptyBoardBB = 0;
const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = FileABB << 1;
const Bitboard FileCBB = FileABB << 2;
const Bitboard FileDBB = FileABB << 3;
const Bitboard FileEBB = FileABB << 4;
const Bitboard FileFBB = FileABB << 5;
const Bitboard FileGBB = FileABB << 6;
const Bitboard FileHBB = FileABB << 7;
const Bitboard Rank1BB = 0xFF;
const Bitboard Rank2BB = Rank1BB << (8 * 1);
const Bitboard Rank3BB = Rank1BB << (8 * 2);
const Bitboard Rank4BB = Rank1BB << (8 * 3);
const Bitboard Rank5BB = Rank1BB << (8 * 4);
const Bitboard Rank6BB = Rank1BB << (8 * 5);
const Bitboard Rank7BB = Rank1BB << (8 * 6);
const Bitboard Rank8BB = Rank1BB << (8 * 7);
extern Bitboard SquaresByColorBB[2];
extern Bitboard FileBB[8];
extern Bitboard NeighboringFilesBB[8];
extern Bitboard ThisAndNeighboringFilesBB[8];
extern Bitboard RankBB[8];
extern Bitboard InFrontBB[2][8];
extern Bitboard SetMaskBB[65];
extern Bitboard ClearMaskBB[65];
extern Bitboard StepAttacksBB[16][64];
extern Bitboard BetweenBB[64][64];
extern Bitboard SquaresInFrontMask[2][64];
extern Bitboard PassedPawnMask[2][64];
extern Bitboard AttackSpanMask[2][64];
extern const uint64_t RMult[64];
extern const int RShift[64];
extern Bitboard RMask[64];
extern int RAttackIndex[64];
extern Bitboard RAttacks[0x19000];
extern const uint64_t BMult[64];
extern const int BShift[64];
extern Bitboard BMask[64];
extern int BAttackIndex[64];
extern Bitboard BAttacks[0x1480];
extern Bitboard BishopPseudoAttacks[64];
extern Bitboard RookPseudoAttacks[64];
extern Bitboard QueenPseudoAttacks[64];
extern uint8_t BitCount8Bit[256];
/// Functions for testing whether a given bit is set in a bitboard, and for
/// setting and clearing bits.
inline Bitboard bit_is_set(Bitboard b, Square s) {
return b & SetMaskBB[s];
}
inline void set_bit(Bitboard *b, Square s) {
*b |= SetMaskBB[s];
}
inline void clear_bit(Bitboard *b, Square s) {
*b &= ClearMaskBB[s];
}
/// Functions used to update a bitboard after a move. This is faster
/// then calling a sequence of clear_bit() + set_bit()
inline Bitboard make_move_bb(Square from, Square to) {
return SetMaskBB[from] | SetMaskBB[to];
}
inline void do_move_bb(Bitboard *b, Bitboard move_bb) {
*b ^= move_bb;
}
/// rank_bb() and file_bb() take a file or a square as input and return
/// a bitboard representing all squares on the given file or rank.
inline Bitboard rank_bb(Rank r) {
return RankBB[r];
}
inline Bitboard rank_bb(Square s) {
return RankBB[square_rank(s)];
}
inline Bitboard file_bb(File f) {
return FileBB[f];
}
inline Bitboard file_bb(Square s) {
return FileBB[square_file(s)];
}
/// neighboring_files_bb takes a file or a square as input and returns a
/// bitboard representing all squares on the neighboring files.
inline Bitboard neighboring_files_bb(File f) {
return NeighboringFilesBB[f];
}
inline Bitboard neighboring_files_bb(Square s) {
return NeighboringFilesBB[square_file(s)];
}
/// this_and_neighboring_files_bb takes a file or a square as input and returns
/// a bitboard representing all squares on the given and neighboring files.
inline Bitboard this_and_neighboring_files_bb(File f) {
return ThisAndNeighboringFilesBB[f];
}
inline Bitboard this_and_neighboring_files_bb(Square s) {
return ThisAndNeighboringFilesBB[square_file(s)];
}
/// in_front_bb() takes a color and a rank or square as input, and returns a
/// bitboard representing all the squares on all ranks in front of the rank
/// (or square), from the given color's point of view. For instance,
/// in_front_bb(WHITE, RANK_5) will give all squares on ranks 6, 7 and 8, while
/// in_front_bb(BLACK, SQ_D3) will give all squares on ranks 1 and 2.
inline Bitboard in_front_bb(Color c, Rank r) {
return InFrontBB[c][r];
}
inline Bitboard in_front_bb(Color c, Square s) {
return InFrontBB[c][square_rank(s)];
}
/// Functions for computing sliding attack bitboards. rook_attacks_bb(),
/// bishop_attacks_bb() and queen_attacks_bb() all take a square and a
/// bitboard of occupied squares as input, and return a bitboard representing
/// all squares attacked by a rook, bishop or queen on the given square.
#if defined(IS_64BIT)
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & RMask[s];
return RAttacks[RAttackIndex[s] + ((b * RMult[s]) >> RShift[s])];
}
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & BMask[s];
return BAttacks[BAttackIndex[s] + ((b * BMult[s]) >> BShift[s])];
}
#else // if !defined(IS_64BIT)
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & RMask[s];
return RAttacks[RAttackIndex[s] +
(unsigned(int(b) * int(RMult[s]) ^ int(b >> 32) * int(RMult[s] >> 32)) >> RShift[s])];
}
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & BMask[s];
return BAttacks[BAttackIndex[s] +
(unsigned(int(b) * int(BMult[s]) ^ int(b >> 32) * int(BMult[s] >> 32)) >> BShift[s])];
}
#endif
inline Bitboard queen_attacks_bb(Square s, Bitboard blockers) {
return rook_attacks_bb(s, blockers) | bishop_attacks_bb(s, blockers);
}
/// squares_between returns a bitboard representing all squares between
/// two squares. For instance, squares_between(SQ_C4, SQ_F7) returns a
/// bitboard with the bits for square d5 and e6 set. If s1 and s2 are not
/// on the same line, file or diagonal, EmptyBoardBB is returned.
inline Bitboard squares_between(Square s1, Square s2) {
return BetweenBB[s1][s2];
}
/// 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,
/// from the point of view of the given color. Definition of the table is:
/// SquaresInFrontOf[c][s] = in_front_bb(c, s) & file_bb(s)
inline Bitboard squares_in_front_of(Color c, Square s) {
return SquaresInFrontMask[c][s];
}
/// 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
/// the given square is a passed pawn. Definition of the table is:
/// PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_neighboring_files_bb(s)
inline Bitboard passed_pawn_mask(Color c, Square s) {
return PassedPawnMask[c][s];
}
/// attack_span_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
/// when it moves along its file starting from the given square. Definition is:
/// AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
inline Bitboard attack_span_mask(Color c, Square s) {
return AttackSpanMask[c][s];
}
/// squares_aligned returns true if the squares s1, s2 and s3 are aligned
/// either on a straight or on a diagonal line.
inline bool squares_aligned(Square s1, Square s2, Square s3) {
return (BetweenBB[s1][s2] | BetweenBB[s1][s3] | BetweenBB[s2][s3])
& ((1ULL << s1) | (1ULL << s2) | (1ULL << s3));
}
/// first_1() finds the least significant nonzero bit in a nonzero bitboard.
/// pop_1st_bit() finds and clears the least significant nonzero bit in a
/// nonzero bitboard.
#if defined(USE_BSFQ)
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
FORCE_INLINE Square first_1(Bitboard b) {
unsigned long index;
_BitScanForward64(&index, b);
return (Square) index;
}
#else
FORCE_INLINE Square first_1(Bitboard b) { // Assembly code by Heinz van Saanen
Bitboard dummy;
__asm__("bsfq %1, %0": "=r"(dummy): "rm"(b) );
return (Square) dummy;
}
#endif
FORCE_INLINE Square pop_1st_bit(Bitboard* b) {
const Square s = first_1(*b);
*b &= ~(1ULL<<s);
return s;
}
#else // if !defined(USE_BSFQ)
extern Square first_1(Bitboard b);
extern Square pop_1st_bit(Bitboard* b);
#endif
extern void print_bitboard(Bitboard b);
extern void init_bitboards();
#endif // !defined(BITBOARD_H_INCLUDED)

View File

@ -0,0 +1,98 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(BITCOUNT_H_INCLUDED)
#define BITCOUNT_H_INCLUDED
#include "types.h"
enum BitCountType {
CNT64,
CNT64_MAX15,
CNT32,
CNT32_MAX15,
CNT_POPCNT
};
/// count_1s() counts the number of nonzero bits in a bitboard.
/// 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<>
inline int count_1s<CNT64>(Bitboard b) {
b -= ((b>>1) & 0x5555555555555555ULL);
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL;
b *= 0x0101010101010101ULL;
return int(b >> 56);
}
template<>
inline int count_1s<CNT64_MAX15>(Bitboard b) {
b -= (b>>1) & 0x5555555555555555ULL;
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b *= 0x1111111111111111ULL;
return int(b >> 60);
}
template<>
inline int count_1s<CNT32>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555;
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
v = ((v >> 4) + v) & 0x0F0F0F0F; // 0-8 in 8 bits
v += (((w >> 4) + w) & 0x0F0F0F0F); // 0-16 in 8 bits
v *= 0x01010101; // mul is fast on amd procs
return int(v >> 24);
}
template<>
inline int count_1s<CNT32_MAX15>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555;
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
v += w; // 0-8 in 4 bits
v *= 0x11111111;
return int(v >> 28);
}
template<>
inline int count_1s<CNT_POPCNT>(Bitboard b) {
#if !defined(USE_POPCNT)
return int(b != 0); // Avoid 'b not used' warning
#elif defined(_MSC_VER) && defined(__INTEL_COMPILER)
return _mm_popcnt_u64(b);
#elif defined(_MSC_VER)
return (int)__popcnt64(b);
#elif defined(__GNUC__)
unsigned long ret;
__asm__("popcnt %1, %0" : "=r" (ret) : "r" (b));
return ret;
#endif
}
#endif // !defined(BITCOUNT_H_INCLUDED)

View File

@ -0,0 +1,525 @@
/*
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/>.
*/
/*
The code in this file is based on the opening book code in PolyGlot
by Fabien Letouzey. PolyGlot is available under the GNU General
Public License, and can be downloaded from http://wbec-ridderkerk.nl
*/
#include <cassert>
#include <iostream>
#include "book.h"
#include "movegen.h"
using namespace std;
namespace {
// Random numbers from PolyGlot, used to compute book hash keys
const uint64_t Random64[781] = {
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL,
0x1A083822CEAFE02DULL, 0x9605D5F0E25EC3B0ULL, 0xD021FF5CD13A2ED5ULL,
0x40BDF15D4A672E32ULL, 0x011355146FD56395ULL, 0x5DB4832046F3D9E5ULL,
0x239F8B2D7FF719CCULL, 0x05D1A1AE85B49AA1ULL, 0x679F848F6E8FC971ULL,
0x7449BBFF801FED0BULL, 0x7D11CDB1C3B7ADF0ULL, 0x82C7709E781EB7CCULL,
0xF3218F1C9510786CULL, 0x331478F3AF51BBE6ULL, 0x4BB38DE5E7219443ULL,
0xAA649C6EBCFD50FCULL, 0x8DBD98A352AFD40BULL, 0x87D2074B81D79217ULL,
0x19F3C751D3E92AE1ULL, 0xB4AB30F062B19ABFULL, 0x7B0500AC42047AC4ULL,
0xC9452CA81A09D85DULL, 0x24AA6C514DA27500ULL, 0x4C9F34427501B447ULL,
0x14A68FD73C910841ULL, 0xA71B9B83461CBD93ULL, 0x03488B95B0F1850FULL,
0x637B2B34FF93C040ULL, 0x09D1BC9A3DD90A94ULL, 0x3575668334A1DD3BULL,
0x735E2B97A4C45A23ULL, 0x18727070F1BD400BULL, 0x1FCBACD259BF02E7ULL,
0xD310A7C2CE9B6555ULL, 0xBF983FE0FE5D8244ULL, 0x9F74D14F7454A824ULL,
0x51EBDC4AB9BA3035ULL, 0x5C82C505DB9AB0FAULL, 0xFCF7FE8A3430B241ULL,
0x3253A729B9BA3DDEULL, 0x8C74C368081B3075ULL, 0xB9BC6C87167C33E7ULL,
0x7EF48F2B83024E20ULL, 0x11D505D4C351BD7FULL, 0x6568FCA92C76A243ULL,
0x4DE0B0F40F32A7B8ULL, 0x96D693460CC37E5DULL, 0x42E240CB63689F2FULL,
0x6D2BDCDAE2919661ULL, 0x42880B0236E4D951ULL, 0x5F0F4A5898171BB6ULL,
0x39F890F579F92F88ULL, 0x93C5B5F47356388BULL, 0x63DC359D8D231B78ULL,
0xEC16CA8AEA98AD76ULL, 0x5355F900C2A82DC7ULL, 0x07FB9F855A997142ULL,
0x5093417AA8A7ED5EULL, 0x7BCBC38DA25A7F3CULL, 0x19FC8A768CF4B6D4ULL,
0x637A7780DECFC0D9ULL, 0x8249A47AEE0E41F7ULL, 0x79AD695501E7D1E8ULL,
0x14ACBAF4777D5776ULL, 0xF145B6BECCDEA195ULL, 0xDABF2AC8201752FCULL,
0x24C3C94DF9C8D3F6ULL, 0xBB6E2924F03912EAULL, 0x0CE26C0B95C980D9ULL,
0xA49CD132BFBF7CC4ULL, 0xE99D662AF4243939ULL, 0x27E6AD7891165C3FULL,
0x8535F040B9744FF1ULL, 0x54B3F4FA5F40D873ULL, 0x72B12C32127FED2BULL,
0xEE954D3C7B411F47ULL, 0x9A85AC909A24EAA1ULL, 0x70AC4CD9F04F21F5ULL,
0xF9B89D3E99A075C2ULL, 0x87B3E2B2B5C907B1ULL, 0xA366E5B8C54F48B8ULL,
0xAE4A9346CC3F7CF2ULL, 0x1920C04D47267BBDULL, 0x87BF02C6B49E2AE9ULL,
0x092237AC237F3859ULL, 0xFF07F64EF8ED14D0ULL, 0x8DE8DCA9F03CC54EULL,
0x9C1633264DB49C89ULL, 0xB3F22C3D0B0B38EDULL, 0x390E5FB44D01144BULL,
0x5BFEA5B4712768E9ULL, 0x1E1032911FA78984ULL, 0x9A74ACB964E78CB3ULL,
0x4F80F7A035DAFB04ULL, 0x6304D09A0B3738C4ULL, 0x2171E64683023A08ULL,
0x5B9B63EB9CEFF80CULL, 0x506AACF489889342ULL, 0x1881AFC9A3A701D6ULL,
0x6503080440750644ULL, 0xDFD395339CDBF4A7ULL, 0xEF927DBCF00C20F2ULL,
0x7B32F7D1E03680ECULL, 0xB9FD7620E7316243ULL, 0x05A7E8A57DB91B77ULL,
0xB5889C6E15630A75ULL, 0x4A750A09CE9573F7ULL, 0xCF464CEC899A2F8AULL,
0xF538639CE705B824ULL, 0x3C79A0FF5580EF7FULL, 0xEDE6C87F8477609DULL,
0x799E81F05BC93F31ULL, 0x86536B8CF3428A8CULL, 0x97D7374C60087B73ULL,
0xA246637CFF328532ULL, 0x043FCAE60CC0EBA0ULL, 0x920E449535DD359EULL,
0x70EB093B15B290CCULL, 0x73A1921916591CBDULL, 0x56436C9FE1A1AA8DULL,
0xEFAC4B70633B8F81ULL, 0xBB215798D45DF7AFULL, 0x45F20042F24F1768ULL,
0x930F80F4E8EB7462ULL, 0xFF6712FFCFD75EA1ULL, 0xAE623FD67468AA70ULL,
0xDD2C5BC84BC8D8FCULL, 0x7EED120D54CF2DD9ULL, 0x22FE545401165F1CULL,
0xC91800E98FB99929ULL, 0x808BD68E6AC10365ULL, 0xDEC468145B7605F6ULL,
0x1BEDE3A3AEF53302ULL, 0x43539603D6C55602ULL, 0xAA969B5C691CCB7AULL,
0xA87832D392EFEE56ULL, 0x65942C7B3C7E11AEULL, 0xDED2D633CAD004F6ULL,
0x21F08570F420E565ULL, 0xB415938D7DA94E3CULL, 0x91B859E59ECB6350ULL,
0x10CFF333E0ED804AULL, 0x28AED140BE0BB7DDULL, 0xC5CC1D89724FA456ULL,
0x5648F680F11A2741ULL, 0x2D255069F0B7DAB3ULL, 0x9BC5A38EF729ABD4ULL,
0xEF2F054308F6A2BCULL, 0xAF2042F5CC5C2858ULL, 0x480412BAB7F5BE2AULL,
0xAEF3AF4A563DFE43ULL, 0x19AFE59AE451497FULL, 0x52593803DFF1E840ULL,
0xF4F076E65F2CE6F0ULL, 0x11379625747D5AF3ULL, 0xBCE5D2248682C115ULL,
0x9DA4243DE836994FULL, 0x066F70B33FE09017ULL, 0x4DC4DE189B671A1CULL,
0x51039AB7712457C3ULL, 0xC07A3F80C31FB4B4ULL, 0xB46EE9C5E64A6E7CULL,
0xB3819A42ABE61C87ULL, 0x21A007933A522A20ULL, 0x2DF16F761598AA4FULL,
0x763C4A1371B368FDULL, 0xF793C46702E086A0ULL, 0xD7288E012AEB8D31ULL,
0xDE336A2A4BC1C44BULL, 0x0BF692B38D079F23ULL, 0x2C604A7A177326B3ULL,
0x4850E73E03EB6064ULL, 0xCFC447F1E53C8E1BULL, 0xB05CA3F564268D99ULL,
0x9AE182C8BC9474E8ULL, 0xA4FC4BD4FC5558CAULL, 0xE755178D58FC4E76ULL,
0x69B97DB1A4C03DFEULL, 0xF9B5B7C4ACC67C96ULL, 0xFC6A82D64B8655FBULL,
0x9C684CB6C4D24417ULL, 0x8EC97D2917456ED0ULL, 0x6703DF9D2924E97EULL,
0xC547F57E42A7444EULL, 0x78E37644E7CAD29EULL, 0xFE9A44E9362F05FAULL,
0x08BD35CC38336615ULL, 0x9315E5EB3A129ACEULL, 0x94061B871E04DF75ULL,
0xDF1D9F9D784BA010ULL, 0x3BBA57B68871B59DULL, 0xD2B7ADEEDED1F73FULL,
0xF7A255D83BC373F8ULL, 0xD7F4F2448C0CEB81ULL, 0xD95BE88CD210FFA7ULL,
0x336F52F8FF4728E7ULL, 0xA74049DAC312AC71ULL, 0xA2F61BB6E437FDB5ULL,
0x4F2A5CB07F6A35B3ULL, 0x87D380BDA5BF7859ULL, 0x16B9F7E06C453A21ULL,
0x7BA2484C8A0FD54EULL, 0xF3A678CAD9A2E38CULL, 0x39B0BF7DDE437BA2ULL,
0xFCAF55C1BF8A4424ULL, 0x18FCF680573FA594ULL, 0x4C0563B89F495AC3ULL,
0x40E087931A00930DULL, 0x8CFFA9412EB642C1ULL, 0x68CA39053261169FULL,
0x7A1EE967D27579E2ULL, 0x9D1D60E5076F5B6FULL, 0x3810E399B6F65BA2ULL,
0x32095B6D4AB5F9B1ULL, 0x35CAB62109DD038AULL, 0xA90B24499FCFAFB1ULL,
0x77A225A07CC2C6BDULL, 0x513E5E634C70E331ULL, 0x4361C0CA3F692F12ULL,
0xD941ACA44B20A45BULL, 0x528F7C8602C5807BULL, 0x52AB92BEB9613989ULL,
0x9D1DFA2EFC557F73ULL, 0x722FF175F572C348ULL, 0x1D1260A51107FE97ULL,
0x7A249A57EC0C9BA2ULL, 0x04208FE9E8F7F2D6ULL, 0x5A110C6058B920A0ULL,
0x0CD9A497658A5698ULL, 0x56FD23C8F9715A4CULL, 0x284C847B9D887AAEULL,
0x04FEABFBBDB619CBULL, 0x742E1E651C60BA83ULL, 0x9A9632E65904AD3CULL,
0x881B82A13B51B9E2ULL, 0x506E6744CD974924ULL, 0xB0183DB56FFC6A79ULL,
0x0ED9B915C66ED37EULL, 0x5E11E86D5873D484ULL, 0xF678647E3519AC6EULL,
0x1B85D488D0F20CC5ULL, 0xDAB9FE6525D89021ULL, 0x0D151D86ADB73615ULL,
0xA865A54EDCC0F019ULL, 0x93C42566AEF98FFBULL, 0x99E7AFEABE000731ULL,
0x48CBFF086DDF285AULL, 0x7F9B6AF1EBF78BAFULL, 0x58627E1A149BBA21ULL,
0x2CD16E2ABD791E33ULL, 0xD363EFF5F0977996ULL, 0x0CE2A38C344A6EEDULL,
0x1A804AADB9CFA741ULL, 0x907F30421D78C5DEULL, 0x501F65EDB3034D07ULL,
0x37624AE5A48FA6E9ULL, 0x957BAF61700CFF4EULL, 0x3A6C27934E31188AULL,
0xD49503536ABCA345ULL, 0x088E049589C432E0ULL, 0xF943AEE7FEBF21B8ULL,
0x6C3B8E3E336139D3ULL, 0x364F6FFA464EE52EULL, 0xD60F6DCEDC314222ULL,
0x56963B0DCA418FC0ULL, 0x16F50EDF91E513AFULL, 0xEF1955914B609F93ULL,
0x565601C0364E3228ULL, 0xECB53939887E8175ULL, 0xBAC7A9A18531294BULL,
0xB344C470397BBA52ULL, 0x65D34954DAF3CEBDULL, 0xB4B81B3FA97511E2ULL,
0xB422061193D6F6A7ULL, 0x071582401C38434DULL, 0x7A13F18BBEDC4FF5ULL,
0xBC4097B116C524D2ULL, 0x59B97885E2F2EA28ULL, 0x99170A5DC3115544ULL,
0x6F423357E7C6A9F9ULL, 0x325928EE6E6F8794ULL, 0xD0E4366228B03343ULL,
0x565C31F7DE89EA27ULL, 0x30F5611484119414ULL, 0xD873DB391292ED4FULL,
0x7BD94E1D8E17DEBCULL, 0xC7D9F16864A76E94ULL, 0x947AE053EE56E63CULL,
0xC8C93882F9475F5FULL, 0x3A9BF55BA91F81CAULL, 0xD9A11FBB3D9808E4ULL,
0x0FD22063EDC29FCAULL, 0xB3F256D8ACA0B0B9ULL, 0xB03031A8B4516E84ULL,
0x35DD37D5871448AFULL, 0xE9F6082B05542E4EULL, 0xEBFAFA33D7254B59ULL,
0x9255ABB50D532280ULL, 0xB9AB4CE57F2D34F3ULL, 0x693501D628297551ULL,
0xC62C58F97DD949BFULL, 0xCD454F8F19C5126AULL, 0xBBE83F4ECC2BDECBULL,
0xDC842B7E2819E230ULL, 0xBA89142E007503B8ULL, 0xA3BC941D0A5061CBULL,
0xE9F6760E32CD8021ULL, 0x09C7E552BC76492FULL, 0x852F54934DA55CC9ULL,
0x8107FCCF064FCF56ULL, 0x098954D51FFF6580ULL, 0x23B70EDB1955C4BFULL,
0xC330DE426430F69DULL, 0x4715ED43E8A45C0AULL, 0xA8D7E4DAB780A08DULL,
0x0572B974F03CE0BBULL, 0xB57D2E985E1419C7ULL, 0xE8D9ECBE2CF3D73FULL,
0x2FE4B17170E59750ULL, 0x11317BA87905E790ULL, 0x7FBF21EC8A1F45ECULL,
0x1725CABFCB045B00ULL, 0x964E915CD5E2B207ULL, 0x3E2B8BCBF016D66DULL,
0xBE7444E39328A0ACULL, 0xF85B2B4FBCDE44B7ULL, 0x49353FEA39BA63B1ULL,
0x1DD01AAFCD53486AULL, 0x1FCA8A92FD719F85ULL, 0xFC7C95D827357AFAULL,
0x18A6A990C8B35EBDULL, 0xCCCB7005C6B9C28DULL, 0x3BDBB92C43B17F26ULL,
0xAA70B5B4F89695A2ULL, 0xE94C39A54A98307FULL, 0xB7A0B174CFF6F36EULL,
0xD4DBA84729AF48ADULL, 0x2E18BC1AD9704A68ULL, 0x2DE0966DAF2F8B1CULL,
0xB9C11D5B1E43A07EULL, 0x64972D68DEE33360ULL, 0x94628D38D0C20584ULL,
0xDBC0D2B6AB90A559ULL, 0xD2733C4335C6A72FULL, 0x7E75D99D94A70F4DULL,
0x6CED1983376FA72BULL, 0x97FCAACBF030BC24ULL, 0x7B77497B32503B12ULL,
0x8547EDDFB81CCB94ULL, 0x79999CDFF70902CBULL, 0xCFFE1939438E9B24ULL,
0x829626E3892D95D7ULL, 0x92FAE24291F2B3F1ULL, 0x63E22C147B9C3403ULL,
0xC678B6D860284A1CULL, 0x5873888850659AE7ULL, 0x0981DCD296A8736DULL,
0x9F65789A6509A440ULL, 0x9FF38FED72E9052FULL, 0xE479EE5B9930578CULL,
0xE7F28ECD2D49EECDULL, 0x56C074A581EA17FEULL, 0x5544F7D774B14AEFULL,
0x7B3F0195FC6F290FULL, 0x12153635B2C0CF57ULL, 0x7F5126DBBA5E0CA7ULL,
0x7A76956C3EAFB413ULL, 0x3D5774A11D31AB39ULL, 0x8A1B083821F40CB4ULL,
0x7B4A38E32537DF62ULL, 0x950113646D1D6E03ULL, 0x4DA8979A0041E8A9ULL,
0x3BC36E078F7515D7ULL, 0x5D0A12F27AD310D1ULL, 0x7F9D1A2E1EBE1327ULL,
0xDA3A361B1C5157B1ULL, 0xDCDD7D20903D0C25ULL, 0x36833336D068F707ULL,
0xCE68341F79893389ULL, 0xAB9090168DD05F34ULL, 0x43954B3252DC25E5ULL,
0xB438C2B67F98E5E9ULL, 0x10DCD78E3851A492ULL, 0xDBC27AB5447822BFULL,
0x9B3CDB65F82CA382ULL, 0xB67B7896167B4C84ULL, 0xBFCED1B0048EAC50ULL,
0xA9119B60369FFEBDULL, 0x1FFF7AC80904BF45ULL, 0xAC12FB171817EEE7ULL,
0xAF08DA9177DDA93DULL, 0x1B0CAB936E65C744ULL, 0xB559EB1D04E5E932ULL,
0xC37B45B3F8D6F2BAULL, 0xC3A9DC228CAAC9E9ULL, 0xF3B8B6675A6507FFULL,
0x9FC477DE4ED681DAULL, 0x67378D8ECCEF96CBULL, 0x6DD856D94D259236ULL,
0xA319CE15B0B4DB31ULL, 0x073973751F12DD5EULL, 0x8A8E849EB32781A5ULL,
0xE1925C71285279F5ULL, 0x74C04BF1790C0EFEULL, 0x4DDA48153C94938AULL,
0x9D266D6A1CC0542CULL, 0x7440FB816508C4FEULL, 0x13328503DF48229FULL,
0xD6BF7BAEE43CAC40ULL, 0x4838D65F6EF6748FULL, 0x1E152328F3318DEAULL,
0x8F8419A348F296BFULL, 0x72C8834A5957B511ULL, 0xD7A023A73260B45CULL,
0x94EBC8ABCFB56DAEULL, 0x9FC10D0F989993E0ULL, 0xDE68A2355B93CAE6ULL,
0xA44CFE79AE538BBEULL, 0x9D1D84FCCE371425ULL, 0x51D2B1AB2DDFB636ULL,
0x2FD7E4B9E72CD38CULL, 0x65CA5B96B7552210ULL, 0xDD69A0D8AB3B546DULL,
0x604D51B25FBF70E2ULL, 0x73AA8A564FB7AC9EULL, 0x1A8C1E992B941148ULL,
0xAAC40A2703D9BEA0ULL, 0x764DBEAE7FA4F3A6ULL, 0x1E99B96E70A9BE8BULL,
0x2C5E9DEB57EF4743ULL, 0x3A938FEE32D29981ULL, 0x26E6DB8FFDF5ADFEULL,
0x469356C504EC9F9DULL, 0xC8763C5B08D1908CULL, 0x3F6C6AF859D80055ULL,
0x7F7CC39420A3A545ULL, 0x9BFB227EBDF4C5CEULL, 0x89039D79D6FC5C5CULL,
0x8FE88B57305E2AB6ULL, 0xA09E8C8C35AB96DEULL, 0xFA7E393983325753ULL,
0xD6B6D0ECC617C699ULL, 0xDFEA21EA9E7557E3ULL, 0xB67C1FA481680AF8ULL,
0xCA1E3785A9E724E5ULL, 0x1CFC8BED0D681639ULL, 0xD18D8549D140CAEAULL,
0x4ED0FE7E9DC91335ULL, 0xE4DBF0634473F5D2ULL, 0x1761F93A44D5AEFEULL,
0x53898E4C3910DA55ULL, 0x734DE8181F6EC39AULL, 0x2680B122BAA28D97ULL,
0x298AF231C85BAFABULL, 0x7983EED3740847D5ULL, 0x66C1A2A1A60CD889ULL,
0x9E17E49642A3E4C1ULL, 0xEDB454E7BADC0805ULL, 0x50B704CAB602C329ULL,
0x4CC317FB9CDDD023ULL, 0x66B4835D9EAFEA22ULL, 0x219B97E26FFC81BDULL,
0x261E4E4C0A333A9DULL, 0x1FE2CCA76517DB90ULL, 0xD7504DFA8816EDBBULL,
0xB9571FA04DC089C8ULL, 0x1DDC0325259B27DEULL, 0xCF3F4688801EB9AAULL,
0xF4F5D05C10CAB243ULL, 0x38B6525C21A42B0EULL, 0x36F60E2BA4FA6800ULL,
0xEB3593803173E0CEULL, 0x9C4CD6257C5A3603ULL, 0xAF0C317D32ADAA8AULL,
0x258E5A80C7204C4BULL, 0x8B889D624D44885DULL, 0xF4D14597E660F855ULL,
0xD4347F66EC8941C3ULL, 0xE699ED85B0DFB40DULL, 0x2472F6207C2D0484ULL,
0xC2A1E7B5B459AEB5ULL, 0xAB4F6451CC1D45ECULL, 0x63767572AE3D6174ULL,
0xA59E0BD101731A28ULL, 0x116D0016CB948F09ULL, 0x2CF9C8CA052F6E9FULL,
0x0B090A7560A968E3ULL, 0xABEEDDB2DDE06FF1ULL, 0x58EFC10B06A2068DULL,
0xC6E57A78FBD986E0ULL, 0x2EAB8CA63CE802D7ULL, 0x14A195640116F336ULL,
0x7C0828DD624EC390ULL, 0xD74BBE77E6116AC7ULL, 0x804456AF10F5FB53ULL,
0xEBE9EA2ADF4321C7ULL, 0x03219A39EE587A30ULL, 0x49787FEF17AF9924ULL,
0xA1E9300CD8520548ULL, 0x5B45E522E4B1B4EFULL, 0xB49C3B3995091A36ULL,
0xD4490AD526F14431ULL, 0x12A8F216AF9418C2ULL, 0x001F837CC7350524ULL,
0x1877B51E57A764D5ULL, 0xA2853B80F17F58EEULL, 0x993E1DE72D36D310ULL,
0xB3598080CE64A656ULL, 0x252F59CF0D9F04BBULL, 0xD23C8E176D113600ULL,
0x1BDA0492E7E4586EULL, 0x21E0BD5026C619BFULL, 0x3B097ADAF088F94EULL,
0x8D14DEDB30BE846EULL, 0xF95CFFA23AF5F6F4ULL, 0x3871700761B3F743ULL,
0xCA672B91E9E4FA16ULL, 0x64C8E531BFF53B55ULL, 0x241260ED4AD1E87DULL,
0x106C09B972D2E822ULL, 0x7FBA195410E5CA30ULL, 0x7884D9BC6CB569D8ULL,
0x0647DFEDCD894A29ULL, 0x63573FF03E224774ULL, 0x4FC8E9560F91B123ULL,
0x1DB956E450275779ULL, 0xB8D91274B9E9D4FBULL, 0xA2EBEE47E2FBFCE1ULL,
0xD9F1F30CCD97FB09ULL, 0xEFED53D75FD64E6BULL, 0x2E6D02C36017F67FULL,
0xA9AA4D20DB084E9BULL, 0xB64BE8D8B25396C1ULL, 0x70CB6AF7C2D5BCF0ULL,
0x98F076A4F7A2322EULL, 0xBF84470805E69B5FULL, 0x94C3251F06F90CF3ULL,
0x3E003E616A6591E9ULL, 0xB925A6CD0421AFF3ULL, 0x61BDD1307C66E300ULL,
0xBF8D5108E27E0D48ULL, 0x240AB57A8B888B20ULL, 0xFC87614BAF287E07ULL,
0xEF02CDD06FFDB432ULL, 0xA1082C0466DF6C0AULL, 0x8215E577001332C8ULL,
0xD39BB9C3A48DB6CFULL, 0x2738259634305C14ULL, 0x61CF4F94C97DF93DULL,
0x1B6BACA2AE4E125BULL, 0x758F450C88572E0BULL, 0x959F587D507A8359ULL,
0xB063E962E045F54DULL, 0x60E8ED72C0DFF5D1ULL, 0x7B64978555326F9FULL,
0xFD080D236DA814BAULL, 0x8C90FD9B083F4558ULL, 0x106F72FE81E2C590ULL,
0x7976033A39F7D952ULL, 0xA4EC0132764CA04BULL, 0x733EA705FAE4FA77ULL,
0xB4D8F77BC3E56167ULL, 0x9E21F4F903B33FD9ULL, 0x9D765E419FB69F6DULL,
0xD30C088BA61EA5EFULL, 0x5D94337FBFAF7F5BULL, 0x1A4E4822EB4D7A59ULL,
0x6FFE73E81B637FB3ULL, 0xDDF957BC36D8B9CAULL, 0x64D0E29EEA8838B3ULL,
0x08DD9BDFD96B9F63ULL, 0x087E79E5A57D1D13ULL, 0xE328E230E3E2B3FBULL,
0x1C2559E30F0946BEULL, 0x720BF5F26F4D2EAAULL, 0xB0774D261CC609DBULL,
0x443F64EC5A371195ULL, 0x4112CF68649A260EULL, 0xD813F2FAB7F5C5CAULL,
0x660D3257380841EEULL, 0x59AC2C7873F910A3ULL, 0xE846963877671A17ULL,
0x93B633ABFA3469F8ULL, 0xC0C0F5A60EF4CDCFULL, 0xCAF21ECD4377B28CULL,
0x57277707199B8175ULL, 0x506C11B9D90E8B1DULL, 0xD83CC2687A19255FULL,
0x4A29C6465A314CD1ULL, 0xED2DF21216235097ULL, 0xB5635C95FF7296E2ULL,
0x22AF003AB672E811ULL, 0x52E762596BF68235ULL, 0x9AEBA33AC6ECC6B0ULL,
0x944F6DE09134DFB6ULL, 0x6C47BEC883A7DE39ULL, 0x6AD047C430A12104ULL,
0xA5B1CFDBA0AB4067ULL, 0x7C45D833AFF07862ULL, 0x5092EF950A16DA0BULL,
0x9338E69C052B8E7BULL, 0x455A4B4CFE30E3F5ULL, 0x6B02E63195AD0CF8ULL,
0x6B17B224BAD6BF27ULL, 0xD1E0CCD25BB9C169ULL, 0xDE0C89A556B9AE70ULL,
0x50065E535A213CF6ULL, 0x9C1169FA2777B874ULL, 0x78EDEFD694AF1EEDULL,
0x6DC93D9526A50E68ULL, 0xEE97F453F06791EDULL, 0x32AB0EDB696703D3ULL,
0x3A6853C7E70757A7ULL, 0x31865CED6120F37DULL, 0x67FEF95D92607890ULL,
0x1F2B1D1F15F6DC9CULL, 0xB69E38A8965C6B65ULL, 0xAA9119FF184CCCF4ULL,
0xF43C732873F24C13ULL, 0xFB4A3D794A9A80D2ULL, 0x3550C2321FD6109CULL,
0x371F77E76BB8417EULL, 0x6BFA9AAE5EC05779ULL, 0xCD04F3FF001A4778ULL,
0xE3273522064480CAULL, 0x9F91508BFFCFC14AULL, 0x049A7F41061A9E60ULL,
0xFCB6BE43A9F2FE9BULL, 0x08DE8A1C7797DA9BULL, 0x8F9887E6078735A1ULL,
0xB5B4071DBFC73A66ULL, 0x230E343DFBA08D33ULL, 0x43ED7F5A0FAE657DULL,
0x3A88A0FBBCB05C63ULL, 0x21874B8B4D2DBC4FULL, 0x1BDEA12E35F6A8C9ULL,
0x53C065C6C8E63528ULL, 0xE34A1D250E7A8D6BULL, 0xD6B04D3B7651DD7EULL,
0x5E90277E7CB39E2DULL, 0x2C046F22062DC67DULL, 0xB10BB459132D0A26ULL,
0x3FA9DDFB67E2F199ULL, 0x0E09B88E1914F7AFULL, 0x10E8B35AF3EEAB37ULL,
0x9EEDECA8E272B933ULL, 0xD4C718BC4AE8AE5FULL, 0x81536D601170FC20ULL,
0x91B534F885818A06ULL, 0xEC8177F83F900978ULL, 0x190E714FADA5156EULL,
0xB592BF39B0364963ULL, 0x89C350C893AE7DC1ULL, 0xAC042E70F8B383F2ULL,
0xB49B52E587A1EE60ULL, 0xFB152FE3FF26DA89ULL, 0x3E666E6F69AE2C15ULL,
0x3B544EBE544C19F9ULL, 0xE805A1E290CF2456ULL, 0x24B33C9D7ED25117ULL,
0xE74733427B72F0C1ULL, 0x0A804D18B7097475ULL, 0x57E3306D881EDB4FULL,
0x4AE7D6A36EB5DBCBULL, 0x2D8D5432157064C8ULL, 0xD1E649DE1E7F268BULL,
0x8A328A1CEDFE552CULL, 0x07A3AEC79624C7DAULL, 0x84547DDC3E203C94ULL,
0x990A98FD5071D263ULL, 0x1A4FF12616EEFC89ULL, 0xF6F7FD1431714200ULL,
0x30C05B1BA332F41CULL, 0x8D2636B81555A786ULL, 0x46C9FEB55D120902ULL,
0xCCEC0A73B49C9921ULL, 0x4E9D2827355FC492ULL, 0x19EBB029435DCB0FULL,
0x4659D2B743848A2CULL, 0x963EF2C96B33BE31ULL, 0x74F85198B05A2E7DULL,
0x5A0F544DD2B1FB18ULL, 0x03727073C2E134B1ULL, 0xC7F6AA2DE59AEA61ULL,
0x352787BAA0D7C22FULL, 0x9853EAB63B5E0B35ULL, 0xABBDCDD7ED5C0860ULL,
0xCF05DAF5AC8D77B0ULL, 0x49CAD48CEBF4A71EULL, 0x7A4C10EC2158C4A6ULL,
0xD9E92AA246BF719EULL, 0x13AE978D09FE5557ULL, 0x730499AF921549FFULL,
0x4E4B705B92903BA4ULL, 0xFF577222C14F0A3AULL, 0x55B6344CF97AAFAEULL,
0xB862225B055B6960ULL, 0xCAC09AFBDDD2CDB4ULL, 0xDAF8E9829FE96B5FULL,
0xB5FDFC5D3132C498ULL, 0x310CB380DB6F7503ULL, 0xE87FBB46217A360EULL,
0x2102AE466EBB1148ULL, 0xF8549E1A3AA5E00DULL, 0x07A69AFDCC42261AULL,
0xC4C118BFE78FEAAEULL, 0xF9F4892ED96BD438ULL, 0x1AF3DBE25D8F45DAULL,
0xF5B4B0B0D2DEEEB4ULL, 0x962ACEEFA82E1C84ULL, 0x046E3ECAAF453CE9ULL,
0xF05D129681949A4CULL, 0x964781CE734B3C84ULL, 0x9C2ED44081CE5FBDULL,
0x522E23F3925E319EULL, 0x177E00F9FC32F791ULL, 0x2BC60A63A6F3B3F2ULL,
0x222BBFAE61725606ULL, 0x486289DDCC3D6780ULL, 0x7DC7785B8EFDFC80ULL,
0x8AF38731C02BA980ULL, 0x1FAB64EA29A2DDF7ULL, 0xE4D9429322CD065AULL,
0x9DA058C67844F20CULL, 0x24C0E332B70019B0ULL, 0x233003B5A6CFE6ADULL,
0xD586BD01C5C217F6ULL, 0x5E5637885F29BC2BULL, 0x7EBA726D8C94094BULL,
0x0A56A5F0BFE39272ULL, 0xD79476A84EE20D06ULL, 0x9E4C1269BAA4BF37ULL,
0x17EFEE45B0DEE640ULL, 0x1D95B0A5FCF90BC6ULL, 0x93CBE0B699C2585DULL,
0x65FA4F227A2B6D79ULL, 0xD5F9E858292504D5ULL, 0xC2B5A03F71471A6FULL,
0x59300222B4561E00ULL, 0xCE2F8642CA0712DCULL, 0x7CA9723FBB2E8988ULL,
0x2785338347F2BA08ULL, 0xC61BB3A141E50E8CULL, 0x150F361DAB9DEC26ULL,
0x9F6A419D382595F4ULL, 0x64A53DC924FE7AC9ULL, 0x142DE49FFF7A7C3DULL,
0x0C335248857FA9E7ULL, 0x0A9C32D5EAE45305ULL, 0xE6C42178C4BBB92EULL,
0x71F1CE2490D20B07ULL, 0xF1BCC3D275AFE51AULL, 0xE728E8C83C334074ULL,
0x96FBF83A12884624ULL, 0x81A1549FD6573DA5ULL, 0x5FA7867CAF35E149ULL,
0x56986E2EF3ED091BULL, 0x917F1DD5F8886C61ULL, 0xD20D8C88C8FFE65FULL,
0x31D71DCE64B2C310ULL, 0xF165B587DF898190ULL, 0xA57E6339DD2CF3A0ULL,
0x1EF6E6DBB1961EC9ULL, 0x70CC73D90BC26E24ULL, 0xE21A6B35DF0C3AD7ULL,
0x003A93D8B2806962ULL, 0x1C99DED33CB890A1ULL, 0xCF3145DE0ADD4289ULL,
0xD0E4427A5514FB72ULL, 0x77C621CC9FB3A483ULL, 0x67A34DAC4356550BULL,
0xF8D626AAAF278509ULL
};
// Indices to the Random64[] array
const int PieceIdx = 0;
const int CastleIdx = 768;
const int EnPassantIdx = 772;
const int TurnIdx = 780;
// book_key() builds up a PolyGlot hash key out of a position
uint64_t book_key(const Position& pos) {
// Piece offset is calculated as (64 * PolyPieceType + square), where
// PolyPieceType is: BP = 0, WP = 1, BN = 2, WN = 3 .... BK = 10, WK = 11
static const int PieceToPoly[] = { 0, 1, 3, 5, 7, 9, 11, 0, 0, 0, 2, 4, 6, 8, 10 };
uint64_t result = 0;
Bitboard b = pos.occupied_squares();
while (b)
{
Square s = pop_1st_bit(&b);
int p = PieceToPoly[int(pos.piece_on(s))];
result ^= Random64[PieceIdx + 64 * p + int(s)];
}
if (pos.can_castle_kingside(WHITE))
result ^= Random64[CastleIdx + 0];
if (pos.can_castle_queenside(WHITE))
result ^= Random64[CastleIdx + 1];
if (pos.can_castle_kingside(BLACK))
result ^= Random64[CastleIdx + 2];
if (pos.can_castle_queenside(BLACK))
result ^= Random64[CastleIdx + 3];
if (pos.ep_square() != SQ_NONE)
result ^= Random64[EnPassantIdx + square_file(pos.ep_square())];
if (pos.side_to_move() == WHITE)
result ^= Random64[TurnIdx];
return result;
}
}
/// Book 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>();
}
/// Book destructor. Be sure file is closed before we leave.
Book::~Book() {
close();
}
/// Book::close() closes the file only if it is open, otherwise
/// we can end up in a little mess due to how std::ifstream works.
void Book::close() {
if (bookFile.is_open())
bookFile.close();
bookName = "";
}
/// Book::open() opens a book file with a given file name
void Book::open(const string& fileName) {
// Close old file before opening the new
close();
bookFile.open(fileName.c_str(), ifstream::in | ifstream::binary);
// Silently return when asked to open a non-exsistent file
if (!bookFile.is_open())
return;
// Get the book size in number of entries
bookFile.seekg(0, ios::end);
bookSize = long(bookFile.tellg()) / sizeof(BookEntry);
if (!bookFile.good())
{
cerr << "Failed to open book file " << fileName << endl;
exit(EXIT_FAILURE);
}
// Set only if successful
bookName = fileName;
}
/// Book::get_move() gets a book move for a given position. Returns
/// MOVE_NONE if no book move is found. If findBestMove is true then
/// return always the highest rated book move.
Move Book::get_move(const Position& pos, bool findBestMove) {
if (!bookFile.is_open() || bookSize == 0)
return MOVE_NONE;
BookEntry entry;
int bookMove = MOVE_NONE;
unsigned score, scoresSum = 0, bestScore = 0;
uint64_t key = book_key(pos);
// Choose a book move among the possible moves for the given position
for (int idx = find_entry(key); idx < bookSize; idx++)
{
entry = read_entry(idx);
if (entry.key != key)
break;
score = entry.count;
if (!findBestMove)
{
// 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
// lower score. Note that first entry is always chosen.
scoresSum += score;
if (RKiss.rand<unsigned>() % scoresSum < score)
bookMove = entry.move;
}
else if (score > bestScore)
{
bestScore = score;
bookMove = entry.move;
}
}
// A PolyGlot book move is encoded as follows:
//
// bit 0- 5: destination square (from 0 to 63)
// bit 6-11: origin square (from 0 to 63)
// bit 12-13-14: promotion piece (from KNIGHT == 1 to QUEEN == 4)
//
// Castling moves follow "king captures rook" representation. So in case
// book move is a promotion we have to convert to our representation, in
// all other cases we can directly compare with a Move after having
// masked out special Move's flags that are not supported by PolyGlot.
int p = (bookMove >> 12) & 7;
if (p)
bookMove = int(make_promotion_move(move_from(Move(bookMove)),
move_to(Move(bookMove)), PieceType(p + 1)));
// Verify the book move (if any) is legal
MoveStack mlist[MAX_MOVES];
MoveStack* last = generate<MV_LEGAL>(pos, mlist);
for (MoveStack* cur = mlist; cur != last; cur++)
if ((int(cur->move) & ~(3 << 14)) == bookMove) // Mask out special flags
return cur->move;
return MOVE_NONE;
}
/// Book::find_entry() takes a book key as input, and does a binary search
/// through the book file for the given key. The index to the first book
/// entry with the same key as the input is returned. When the key is not
/// found in the book file, bookSize is returned.
int Book::find_entry(uint64_t key) {
int left, right, mid;
// Binary search (finds the leftmost entry)
left = 0;
right = bookSize - 1;
assert(left <= right);
while (left < right)
{
mid = (left + right) / 2;
assert(mid >= left && mid < right);
if (key <= read_entry(mid).key)
right = mid;
else
left = mid + 1;
}
assert(left == right);
return read_entry(left).key == key ? left : bookSize;
}
/// Book::read_entry() takes an integer index, and returns the BookEntry
/// at the given index in the book file.
BookEntry Book::read_entry(int idx) {
assert(idx >= 0 && idx < bookSize);
assert(bookFile.is_open());
BookEntry e;
bookFile.seekg(idx * sizeof(BookEntry), ios_base::beg);
*this >> e.key >> e.move >> e.count >> e.learn;
if (!bookFile.good())
{
cerr << "Failed to read book entry at index " << idx << endl;
exit(EXIT_FAILURE);
}
return e;
}

View File

@ -0,0 +1,71 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(BOOK_H_INCLUDED)
#define BOOK_H_INCLUDED
#include <fstream>
#include <string>
#include "move.h"
#include "position.h"
#include "rkiss.h"
// A Polyglot book is a series of "entries" of 16 bytes. All integers are
// stored highest byte first (regardless of size). The entries are ordered
// according to key. Lowest key first.
struct BookEntry {
uint64_t key;
uint16_t move;
uint16_t count;
uint32_t learn;
};
class Book {
public:
Book();
~Book();
void open(const std::string& fileName);
void close();
Move get_move(const Position& pos, bool findBestMove);
const std::string name() const { return bookName; }
private:
// read n chars from the file stream and converts them in an
// integer number. Integers are stored with highest byte first.
template<int n> uint64_t get_int();
template<typename T>
Book& operator>>(T& n) { n = (T)get_int<sizeof(T)>(); return *this; }
BookEntry read_entry(int idx);
int find_entry(uint64_t key);
std::ifstream bookFile;
std::string bookName;
int bookSize;
RKISS RKiss;
};
// Yes, we indulge a bit here ;-)
template<int n> inline uint64_t Book::get_int() { return 256 * get_int<n-1>() + bookFile.get(); }
template<> inline uint64_t Book::get_int<1>() { return bookFile.get(); }
#endif // !defined(BOOK_H_INCLUDED)

View File

@ -0,0 +1,939 @@
/*
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/>.
*/
#include <cassert>
#include "bitcount.h"
#include "endgame.h"
#include "pawns.h"
using std::string;
extern uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm);
namespace {
// Table used to drive the defending king towards the edge of the board
// in KX vs K and KQ vs KR endgames.
const int MateTable[64] = {
100, 90, 80, 70, 70, 80, 90, 100,
90, 70, 60, 50, 50, 60, 70, 90,
80, 60, 40, 30, 30, 40, 60, 80,
70, 50, 30, 20, 20, 30, 50, 70,
70, 50, 30, 20, 20, 30, 50, 70,
80, 60, 40, 30, 30, 40, 60, 80,
90, 70, 60, 50, 50, 60, 70, 90,
100, 90, 80, 70, 70, 80, 90, 100,
};
// Table used to drive the defending king towards a corner square of the
// right color in KBN vs K endgames.
const int KBNKMateTable[64] = {
200, 190, 180, 170, 160, 150, 140, 130,
190, 180, 170, 160, 150, 140, 130, 140,
180, 170, 155, 140, 140, 125, 140, 150,
170, 160, 140, 120, 110, 140, 150, 160,
160, 150, 140, 110, 120, 140, 160, 170,
150, 140, 125, 140, 140, 155, 170, 180,
140, 130, 140, 150, 160, 170, 180, 190,
130, 140, 150, 160, 170, 180, 190, 200
};
// The attacking side is given a descending bonus based on distance between
// the two kings in basic endgames.
const int DistanceBonus[8] = { 0, 0, 100, 80, 60, 40, 20, 10 };
// Penalty for big distance between king and knight for the defending king
// and knight in KR vs KN endgames.
const int KRKNKingKnightDistancePenalty[8] = { 0, 0, 4, 10, 20, 32, 48, 70 };
// Build corresponding key code for the opposite color: "KBPKN" -> "KNKBP"
const string swap_colors(const string& keyCode) {
size_t idx = keyCode.find('K', 1);
return keyCode.substr(idx) + keyCode.substr(0, idx);
}
// Get the material key of a position out of the given endgame key code
// like "KBPKN". The trick here is to first build up a FEN string and then
// let a Position object to do the work for us. Note that the FEN string
// could correspond to an illegal position.
Key mat_key(const string& keyCode) {
assert(keyCode.length() > 0 && keyCode.length() < 8);
assert(keyCode[0] == 'K');
string fen;
size_t i = 0;
// First add white and then black pieces
do fen += keyCode[i]; while (keyCode[++i] != 'K');
do fen += char(tolower(keyCode[i])); while (++i < keyCode.length());
// Add file padding and remaining empty ranks
fen += string(1, '0' + int(8 - keyCode.length())) + "/8/8/8/8/8/8/8 w - -";
// Build a Position out of the fen string and get its material key
return Position(fen, false, 0).get_material_key();
}
typedef EndgameBase<Value> EF;
typedef EndgameBase<ScaleFactor> SF;
} // namespace
/// Endgames member definitions
template<> const Endgames::EFMap& Endgames::get<EF>() const { return maps.first; }
template<> const Endgames::SFMap& Endgames::get<SF>() const { return maps.second; }
Endgames::Endgames() {
add<Endgame<Value, KNNK> >("KNNK");
add<Endgame<Value, KPK> >("KPK");
add<Endgame<Value, KBNK> >("KBNK");
add<Endgame<Value, KRKP> >("KRKP");
add<Endgame<Value, KRKB> >("KRKB");
add<Endgame<Value, KRKN> >("KRKN");
add<Endgame<Value, KQKR> >("KQKR");
add<Endgame<Value, KBBKN> >("KBBKN");
add<Endgame<ScaleFactor, KNPK> >("KNPK");
add<Endgame<ScaleFactor, KRPKR> >("KRPKR");
add<Endgame<ScaleFactor, KBPKB> >("KBPKB");
add<Endgame<ScaleFactor, KBPPKB> >("KBPPKB");
add<Endgame<ScaleFactor, KBPKN> >("KBPKN");
add<Endgame<ScaleFactor, KRPPKRP> >("KRPPKRP");
}
Endgames::~Endgames() {
for (EFMap::const_iterator it = get<EF>().begin(); it != get<EF>().end(); ++it)
delete it->second;
for (SFMap::const_iterator it = get<SF>().begin(); it != get<SF>().end(); ++it)
delete it->second;
}
template<class T>
void Endgames::add(const string& keyCode) {
typedef typename T::Base F;
typedef std::map<Key, F*> M;
const_cast<M&>(get<F>()).insert(std::pair<Key, F*>(mat_key(keyCode), new T(WHITE)));
const_cast<M&>(get<F>()).insert(std::pair<Key, F*>(mat_key(swap_colors(keyCode)), new T(BLACK)));
}
template<class T>
T* Endgames::get(Key key) const {
typename std::map<Key, T*>::const_iterator it = get<T>().find(key);
return it != get<T>().end() ? it->second : NULL;
}
// Explicit template instantiations
template EF* Endgames::get<EF>(Key key) const;
template SF* Endgames::get<SF>(Key key) const;
/// 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
/// 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.
template<>
Value Endgame<Value, KXK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO);
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
Value result = pos.non_pawn_material(strongerSide)
+ pos.piece_count(strongerSide, PAWN) * PawnValueEndgame
+ MateTable[loserKSq]
+ DistanceBonus[square_distance(winnerKSq, loserKSq)];
if ( pos.piece_count(strongerSide, QUEEN)
|| pos.piece_count(strongerSide, ROOK)
|| pos.piece_count(strongerSide, BISHOP) > 1)
// TODO: check for two equal-colored bishops!
result += VALUE_KNOWN_WIN;
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
/// defending king towards a corner square of the right color.
template<>
Value Endgame<Value, KBNK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO);
assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame + BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, KNIGHT) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 0);
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
Square bishopSquare = pos.piece_list(strongerSide, BISHOP, 0);
// kbnk_mate_table() tries to drive toward corners A1 or H8,
// 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 (opposite_color_squares(bishopSquare, SQ_A1))
{
winnerKSq = flop_square(winnerKSq);
loserKSq = flop_square(loserKSq);
}
Value result = VALUE_KNOWN_WIN
+ DistanceBonus[square_distance(winnerKSq, loserKSq)]
+ KBNKMateTable[loserKSq];
return strongerSide == pos.side_to_move() ? result : -result;
}
/// KP vs K. This endgame is evaluated with the help of a bitbase.
template<>
Value Endgame<Value, KPK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square wksq, bksq, wpsq;
Color stm;
if (strongerSide == WHITE)
{
wksq = pos.king_square(WHITE);
bksq = pos.king_square(BLACK);
wpsq = pos.piece_list(WHITE, PAWN, 0);
stm = pos.side_to_move();
}
else
{
wksq = flip_square(pos.king_square(BLACK));
bksq = flip_square(pos.king_square(WHITE));
wpsq = flip_square(pos.piece_list(BLACK, PAWN, 0));
stm = opposite_color(pos.side_to_move());
}
if (square_file(wpsq) >= FILE_E)
{
wksq = flop_square(wksq);
bksq = flop_square(bksq);
wpsq = flop_square(wpsq);
}
if (!probe_kpk_bitbase(wksq, wpsq, bksq, stm))
return VALUE_DRAW;
Value result = VALUE_KNOWN_WIN
+ PawnValueEndgame
+ Value(square_rank(wpsq));
return strongerSide == pos.side_to_move() ? result : -result;
}
/// KR vs KP. This is a somewhat tricky endgame to evaluate precisely without
/// a bitbase. The function below returns drawish scores when the pawn is
/// far advanced with support of the king, while the attacking king is far
/// away.
template<>
Value Endgame<Value, KRKP>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide) == 0);
assert(pos.piece_count(weakerSide, PAWN) == 1);
Square wksq, wrsq, bksq, bpsq;
int tempo = (pos.side_to_move() == strongerSide);
wksq = pos.king_square(strongerSide);
wrsq = pos.piece_list(strongerSide, ROOK, 0);
bksq = pos.king_square(weakerSide);
bpsq = pos.piece_list(weakerSide, PAWN, 0);
if (strongerSide == BLACK)
{
wksq = flip_square(wksq);
wrsq = flip_square(wrsq);
bksq = flip_square(bksq);
bpsq = flip_square(bpsq);
}
Square queeningSq = make_square(square_file(bpsq), RANK_1);
Value result;
// If the stronger side's king is in front of the pawn, it's a win
if (wksq < bpsq && square_file(wksq) == square_file(bpsq))
result = RookValueEndgame - Value(square_distance(wksq, bpsq));
// If the weaker side's king is too far from the pawn and the rook,
// it's a win
else if ( square_distance(bksq, bpsq) - (tempo ^ 1) >= 3
&& square_distance(bksq, wrsq) >= 3)
result = RookValueEndgame - Value(square_distance(wksq, bpsq));
// If the pawn is far advanced and supported by the defending king,
// the position is drawish
else if ( square_rank(bksq) <= RANK_3
&& square_distance(bksq, bpsq) == 1
&& square_rank(wksq) >= RANK_4
&& square_distance(wksq, bpsq) - tempo > 2)
result = Value(80 - square_distance(wksq, bpsq) * 8);
else
result = Value(200)
- Value(square_distance(wksq, bpsq + DELTA_S) * 8)
+ Value(square_distance(bksq, bpsq + DELTA_S) * 8)
+ Value(square_distance(bpsq, queeningSq) * 8);
return strongerSide == pos.side_to_move() ? result : -result;
}
/// 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.
template<>
Value Endgame<Value, KRKB>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame);
assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.piece_count(weakerSide, BISHOP) == 1);
Value result = Value(MateTable[pos.king_square(weakerSide)]);
return strongerSide == pos.side_to_move() ? result : -result;
}
/// 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.
template<>
Value Endgame<Value, KRKN>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
Square defendingKSq = pos.king_square(weakerSide);
Square nSq = pos.piece_list(weakerSide, KNIGHT, 0);
int d = square_distance(defendingKSq, nSq);
Value result = Value(10)
+ MateTable[defendingKSq]
+ KRKNKingKnightDistancePenalty[d];
return strongerSide == pos.side_to_move() ? result : -result;
}
/// KQ vs KR. This is almost identical to KX vs K: We give the attacking
/// king a bonus for having the kings close together, and for forcing the
/// defending king towards the edge. If we also take care to avoid null move
/// for the defending side in the search, this is usually sufficient to be
/// able to win KQ vs KR.
template<>
Value Endgame<Value, KQKR>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
Value result = QueenValueEndgame
- RookValueEndgame
+ MateTable[loserKSq]
+ DistanceBonus[square_distance(winnerKSq, loserKSq)];
return strongerSide == pos.side_to_move() ? result : -result;
}
template<>
Value Endgame<Value, KBBKN>::apply(const Position& pos) const {
assert(pos.piece_count(strongerSide, BISHOP) == 2);
assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMidgame);
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
assert(pos.pieces(PAWN) == EmptyBoardBB);
Value result = BishopValueEndgame;
Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide);
Square nsq = pos.piece_list(weakerSide, KNIGHT, 0);
// Bonus for attacking king close to defending king
result += Value(DistanceBonus[square_distance(wksq, bksq)]);
// Bonus for driving the defending king and knight apart
result += Value(square_distance(bksq, nsq) * 32);
// Bonus for restricting the knight's mobility
result += Value((8 - count_1s<CNT32_MAX15>(pos.attacks_from<KNIGHT>(nsq))) * 8);
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
/// king alone are always draw.
template<>
Value Endgame<Value, KmmKm>::apply(const Position&) const {
return VALUE_DRAW;
}
template<>
Value Endgame<Value, KNNK>::apply(const Position&) const {
return VALUE_DRAW;
}
/// 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 of the wrong color. If such a draw is detected, SCALE_FACTOR_ZERO is
/// returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
/// will be used.
template<>
ScaleFactor Endgame<ScaleFactor, KBPsK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, PAWN) >= 1);
// No assertions about the material of weakerSide, because we want draws to
// be detected even when the weaker side has some pawns.
Bitboard pawns = pos.pieces(PAWN, strongerSide);
File pawnFile = square_file(pos.piece_list(strongerSide, PAWN, 0));
// All pawns are on a single rook file ?
if ( (pawnFile == FILE_A || pawnFile == FILE_H)
&& (pawns & ~file_bb(pawnFile)) == EmptyBoardBB)
{
Square bishopSq = pos.piece_list(strongerSide, BISHOP, 0);
Square queeningSq = relative_square(strongerSide, make_square(pawnFile, RANK_8));
Square kingSq = pos.king_square(weakerSide);
if ( opposite_color_squares(queeningSq, bishopSq)
&& abs(square_file(kingSq) - pawnFile) <= 1)
{
// The bishop has the wrong color, and the defending king is on the
// file of the pawn(s) or the neighboring file. Find the rank of the
// frontmost pawn.
Rank rank;
if (strongerSide == WHITE)
{
for (rank = RANK_7; (rank_bb(rank) & pawns) == EmptyBoardBB; rank--) {}
assert(rank >= RANK_2 && rank <= RANK_7);
}
else
{
for (rank = RANK_2; (rank_bb(rank) & pawns) == EmptyBoardBB; rank++) {}
rank = Rank(rank ^ 7); // HACK to get the relative rank
assert(rank >= RANK_2 && rank <= RANK_7);
}
// If the defending king has distance 1 to the promotion square or
// is placed somewhere in front of the pawn, it's a draw.
if ( square_distance(kingSq, queeningSq) <= 1
|| relative_rank(strongerSide, kingSq) >= rank)
return SCALE_FACTOR_ZERO;
}
}
return SCALE_FACTOR_NONE;
}
/// KQKRPScalingFunction scales endgames where the stronger side has only
/// king and queen, while the weaker side has at least a rook and a pawn.
/// It tests for fortress draws with a rook on the third rank defended by
/// a pawn.
template<>
ScaleFactor Endgame<ScaleFactor, KQKRPs>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
assert(pos.piece_count(strongerSide, QUEEN) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.piece_count(weakerSide, ROOK) == 1);
assert(pos.piece_count(weakerSide, PAWN) >= 1);
Square kingSq = pos.king_square(weakerSide);
if ( relative_rank(weakerSide, kingSq) <= RANK_2
&& relative_rank(weakerSide, pos.king_square(strongerSide)) >= RANK_4
&& (pos.pieces(ROOK, weakerSide) & rank_bb(relative_rank(weakerSide, RANK_3)))
&& (pos.pieces(PAWN, weakerSide) & rank_bb(relative_rank(weakerSide, RANK_2)))
&& (pos.attacks_from<KING>(kingSq) & pos.pieces(PAWN, weakerSide)))
{
Square rsq = pos.piece_list(weakerSide, ROOK, 0);
if (pos.attacks_from<PAWN>(rsq, strongerSide) & pos.pieces(PAWN, weakerSide))
return SCALE_FACTOR_ZERO;
}
return SCALE_FACTOR_NONE;
}
/// KRPKRScalingFunction scales KRP vs KR endgames. This function knows a
/// handful of the most important classes of drawn positions, but is far
/// from perfect. It would probably be a good idea to add more knowledge
/// in the future.
///
/// 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.
template<>
ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square wksq = pos.king_square(strongerSide);
Square wrsq = pos.piece_list(strongerSide, ROOK, 0);
Square wpsq = pos.piece_list(strongerSide, PAWN, 0);
Square bksq = pos.king_square(weakerSide);
Square brsq = pos.piece_list(weakerSide, ROOK, 0);
// Orient the board in such a way that the stronger side is white, and the
// pawn is on the left half of the board.
if (strongerSide == BLACK)
{
wksq = flip_square(wksq);
wrsq = flip_square(wrsq);
wpsq = flip_square(wpsq);
bksq = flip_square(bksq);
brsq = flip_square(brsq);
}
if (square_file(wpsq) > FILE_D)
{
wksq = flop_square(wksq);
wrsq = flop_square(wrsq);
wpsq = flop_square(wpsq);
bksq = flop_square(bksq);
brsq = flop_square(brsq);
}
File f = square_file(wpsq);
Rank r = square_rank(wpsq);
Square queeningSq = make_square(f, RANK_8);
int tempo = (pos.side_to_move() == strongerSide);
// If the pawn is not too far advanced and the defending king defends the
// queening square, use the third-rank defence.
if ( r <= RANK_5
&& square_distance(bksq, queeningSq) <= 1
&& wksq <= SQ_H5
&& (square_rank(brsq) == RANK_6 || (r <= RANK_3 && square_rank(wrsq) != RANK_6)))
return SCALE_FACTOR_ZERO;
// The defending side saves a draw by checking from behind in case the pawn
// has advanced to the 6th rank with the king behind.
if ( r == RANK_6
&& square_distance(bksq, queeningSq) <= 1
&& square_rank(wksq) + tempo <= RANK_6
&& (square_rank(brsq) == RANK_1 || (!tempo && abs(square_file(brsq) - f) >= 3)))
return SCALE_FACTOR_ZERO;
if ( r >= RANK_6
&& bksq == queeningSq
&& square_rank(brsq) == RANK_1
&& (!tempo || square_distance(wksq, wpsq) >= 2))
return SCALE_FACTOR_ZERO;
// 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.
if ( wpsq == SQ_A7
&& wrsq == SQ_A8
&& (bksq == SQ_H7 || bksq == SQ_G7)
&& square_file(brsq) == FILE_A
&& (square_rank(brsq) <= RANK_3 || square_file(wksq) >= FILE_D || square_rank(wksq) <= RANK_5))
return SCALE_FACTOR_ZERO;
// If the defending king blocks the pawn and the attacking king is too far
// away, it's a draw.
if ( r <= RANK_5
&& bksq == wpsq + DELTA_N
&& square_distance(wksq, wpsq) - tempo >= 2
&& square_distance(wksq, brsq) - tempo >= 2)
return SCALE_FACTOR_ZERO;
// 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,
// and the defending king cannot gain tempi by threatening the attacking rook.
if ( r == RANK_7
&& f != FILE_A
&& square_file(wrsq) == f
&& wrsq != queeningSq
&& (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo)
&& (square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo))
return ScaleFactor(SCALE_FACTOR_MAX - 2 * square_distance(wksq, queeningSq));
// Similar to the above, but with the pawn further back
if ( f != FILE_A
&& square_file(wrsq) == f
&& wrsq < wpsq
&& (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo)
&& (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wpsq + DELTA_N) - 2 + tempo)
&& ( square_distance(bksq, wrsq) + tempo >= 3
|| ( square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo
&& (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wrsq) + tempo))))
return ScaleFactor( SCALE_FACTOR_MAX
- 8 * square_distance(wpsq, queeningSq)
- 2 * square_distance(wksq, queeningSq));
// If the pawn is not far advanced, and the defending king is somewhere in
// the pawn's path, it's probably a draw.
if (r <= RANK_4 && bksq > wpsq)
{
if (square_file(bksq) == square_file(wpsq))
return ScaleFactor(10);
if ( abs(square_file(bksq) - square_file(wpsq)) == 1
&& square_distance(wksq, bksq) > 2)
return ScaleFactor(24 - 2 * square_distance(wksq, bksq));
}
return SCALE_FACTOR_NONE;
}
/// KRPPKRPScalingFunction scales KRPP vs KRP endgames. There is only a
/// single pattern: If the stronger side has no pawns and the defending king
/// is actively placed, the position is drawish.
template<>
ScaleFactor Endgame<ScaleFactor, KRPPKRP>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 2);
assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
assert(pos.piece_count(weakerSide, PAWN) == 1);
Square wpsq1 = pos.piece_list(strongerSide, PAWN, 0);
Square wpsq2 = pos.piece_list(strongerSide, PAWN, 1);
Square bksq = pos.king_square(weakerSide);
// Does the stronger side have a passed pawn?
if ( pos.pawn_is_passed(strongerSide, wpsq1)
|| pos.pawn_is_passed(strongerSide, wpsq2))
return SCALE_FACTOR_NONE;
Rank r = Max(relative_rank(strongerSide, wpsq1), relative_rank(strongerSide, wpsq2));
if ( file_distance(bksq, wpsq1) <= 1
&& file_distance(bksq, wpsq2) <= 1
&& relative_rank(strongerSide, bksq) > r)
{
switch (r) {
case RANK_2: return ScaleFactor(10);
case RANK_3: return ScaleFactor(10);
case RANK_4: return ScaleFactor(15);
case RANK_5: return ScaleFactor(20);
case RANK_6: return ScaleFactor(40);
default: assert(false);
}
}
return SCALE_FACTOR_NONE;
}
/// KPsKScalingFunction scales endgames with king and two or more pawns
/// 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.
template<>
ScaleFactor Endgame<ScaleFactor, KPsK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.piece_count(strongerSide, PAWN) >= 2);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square ksq = pos.king_square(weakerSide);
Bitboard pawns = pos.pieces(PAWN, strongerSide);
// Are all pawns on the 'a' file?
if ((pawns & ~FileABB) == EmptyBoardBB)
{
// Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1
|| ( square_file(ksq) == FILE_A
&& (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB))
return SCALE_FACTOR_ZERO;
}
// Are all pawns on the 'h' file?
else if ((pawns & ~FileHBB) == EmptyBoardBB)
{
// Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1
|| ( square_file(ksq) == FILE_H
&& (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB))
return SCALE_FACTOR_ZERO;
}
return SCALE_FACTOR_NONE;
}
/// KBPKBScalingFunction scales KBP vs KB endgames. There are two rules:
/// If the defending king is somewhere along the path of the pawn, and the
/// square of the king is not of the same color as the stronger side's bishop,
/// it's a draw. If the two bishops have opposite color, it's almost always
/// a draw.
template<>
ScaleFactor Endgame<ScaleFactor, KBPKB>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame);
assert(pos.piece_count(weakerSide, BISHOP) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN, 0);
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP, 0);
Square weakerBishopSq = pos.piece_list(weakerSide, BISHOP, 0);
Square weakerKingSq = pos.king_square(weakerSide);
// Case 1: Defending king blocks the pawn, and cannot be driven away
if ( square_file(weakerKingSq) == square_file(pawnSq)
&& relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq)
&& ( opposite_color_squares(weakerKingSq, strongerBishopSq)
|| relative_rank(strongerSide, weakerKingSq) <= RANK_6))
return SCALE_FACTOR_ZERO;
// Case 2: Opposite colored bishops
if (opposite_color_squares(strongerBishopSq, weakerBishopSq))
{
// We assume that the position is drawn in the following three situations:
//
// a. The pawn is on rank 5 or further back.
// b. The defending king is somewhere in the pawn's path.
// c. The defending bishop attacks some square along the pawn's path,
// and is at least three squares away from the pawn.
//
// These rules are probably not perfect, but in practice they work
// reasonably well.
if (relative_rank(strongerSide, pawnSq) <= RANK_5)
return SCALE_FACTOR_ZERO;
else
{
Bitboard path = squares_in_front_of(strongerSide, pawnSq);
if (path & pos.pieces(KING, weakerSide))
return SCALE_FACTOR_ZERO;
if ( (pos.attacks_from<BISHOP>(weakerBishopSq) & path)
&& square_distance(weakerBishopSq, pawnSq) >= 3)
return SCALE_FACTOR_ZERO;
}
}
return SCALE_FACTOR_NONE;
}
/// KBPPKBScalingFunction scales KBPP vs KB endgames. It detects a few basic
/// draws with opposite-colored bishops.
template<>
ScaleFactor Endgame<ScaleFactor, KBPPKB>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 2);
assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame);
assert(pos.piece_count(weakerSide, BISHOP) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square wbsq = pos.piece_list(strongerSide, BISHOP, 0);
Square bbsq = pos.piece_list(weakerSide, BISHOP, 0);
if (!opposite_color_squares(wbsq, bbsq))
return SCALE_FACTOR_NONE;
Square ksq = pos.king_square(weakerSide);
Square psq1 = pos.piece_list(strongerSide, PAWN, 0);
Square psq2 = pos.piece_list(strongerSide, PAWN, 1);
Rank r1 = square_rank(psq1);
Rank r2 = square_rank(psq2);
Square blockSq1, blockSq2;
if (relative_rank(strongerSide, psq1) > relative_rank(strongerSide, psq2))
{
blockSq1 = psq1 + pawn_push(strongerSide);
blockSq2 = make_square(square_file(psq2), square_rank(psq1));
}
else
{
blockSq1 = psq2 + pawn_push(strongerSide);
blockSq2 = make_square(square_file(psq1), square_rank(psq2));
}
switch (file_distance(psq1, psq2))
{
case 0:
// Both pawns are on the same file. Easy draw if defender firmly controls
// some square in the frontmost pawn's path.
if ( square_file(ksq) == square_file(blockSq1)
&& relative_rank(strongerSide, ksq) >= relative_rank(strongerSide, blockSq1)
&& opposite_color_squares(ksq, wbsq))
return SCALE_FACTOR_ZERO;
else
return SCALE_FACTOR_NONE;
case 1:
// Pawns on neighboring files. Draw if defender firmly controls the square
// in front of the frontmost pawn's path, and the square diagonally behind
// this square on the file of the other pawn.
if ( ksq == blockSq1
&& opposite_color_squares(ksq, wbsq)
&& ( bbsq == blockSq2
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(BISHOP, weakerSide))
|| abs(r1 - r2) >= 2))
return SCALE_FACTOR_ZERO;
else if ( ksq == blockSq2
&& opposite_color_squares(ksq, wbsq)
&& ( bbsq == blockSq1
|| (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(BISHOP, weakerSide))))
return SCALE_FACTOR_ZERO;
else
return SCALE_FACTOR_NONE;
default:
// The pawns are not on the same file or adjacent files. No scaling.
return SCALE_FACTOR_NONE;
}
}
/// KBPKNScalingFunction scales KBP vs KN endgames. There is a single rule:
/// If the defending king is somewhere along the path of the pawn, and the
/// square of the king is not of the same color as the stronger side's bishop,
/// it's a draw.
template<>
ScaleFactor Endgame<ScaleFactor, KBPKN>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN, 0);
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP, 0);
Square weakerKingSq = pos.king_square(weakerSide);
if ( square_file(weakerKingSq) == square_file(pawnSq)
&& relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq)
&& ( opposite_color_squares(weakerKingSq, strongerBishopSq)
|| relative_rank(strongerSide, weakerKingSq) <= RANK_6))
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_NONE;
}
/// KNPKScalingFunction scales KNP vs K endgames. There is a single rule:
/// If the pawn is a rook pawn on the 7th rank and the defending king prevents
/// the pawn from advancing, the position is drawn.
template<>
ScaleFactor Endgame<ScaleFactor, KNPK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame);
assert(pos.piece_count(strongerSide, KNIGHT) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN, 0);
Square weakerKingSq = pos.king_square(weakerSide);
if ( pawnSq == relative_square(strongerSide, SQ_A7)
&& square_distance(weakerKingSq, relative_square(strongerSide, SQ_A8)) <= 1)
return SCALE_FACTOR_ZERO;
if ( pawnSq == relative_square(strongerSide, SQ_H7)
&& square_distance(weakerKingSq, relative_square(strongerSide, SQ_H8)) <= 1)
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_NONE;
}
/// KPKPScalingFunction scales KP vs KP endgames. This is done by removing
/// the weakest side's pawn and probing the KP vs K bitbase: If the weakest
/// side has a draw without the pawn, she probably has at least a draw with
/// the pawn as well. The exception is when the stronger side's pawn is far
/// 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).
template<>
ScaleFactor Endgame<ScaleFactor, KPKP>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(WHITE, PAWN) == 1);
assert(pos.piece_count(BLACK, PAWN) == 1);
Square wksq, bksq, wpsq;
Color stm;
if (strongerSide == WHITE)
{
wksq = pos.king_square(WHITE);
bksq = pos.king_square(BLACK);
wpsq = pos.piece_list(WHITE, PAWN, 0);
stm = pos.side_to_move();
}
else
{
wksq = flip_square(pos.king_square(BLACK));
bksq = flip_square(pos.king_square(WHITE));
wpsq = flip_square(pos.piece_list(BLACK, PAWN, 0));
stm = opposite_color(pos.side_to_move());
}
if (square_file(wpsq) >= FILE_E)
{
wksq = flop_square(wksq);
bksq = flop_square(bksq);
wpsq = flop_square(wpsq);
}
// If the pawn has advanced to the fifth rank or further, and is not a
// rook pawn, it's too dangerous to assume that it's at least a draw.
if ( square_rank(wpsq) >= RANK_5
&& square_file(wpsq) != FILE_A)
return SCALE_FACTOR_NONE;
// 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.
return probe_kpk_bitbase(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_ZERO;
}

View File

@ -0,0 +1,112 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(ENDGAME_H_INCLUDED)
#define ENDGAME_H_INCLUDED
#include <string>
#include <map>
#include "position.h"
#include "types.h"
/// EndgameType lists all supported endgames
enum EndgameType {
// Evaluation functions
KXK, // Generic "mate lone king" eval
KBNK, // KBN vs K
KPK, // KP vs K
KRKP, // KR vs KP
KRKB, // KR vs KB
KRKN, // KR vs KN
KQKR, // KQ vs KR
KBBKN, // KBB vs KN
KNNK, // KNN vs K
KmmKm, // K and two minors vs K and one or two minors
// Scaling functions
KBPsK, // KB+pawns vs K
KQKRPs, // KQ vs KR+pawns
KRPKR, // KRP vs KR
KRPPKRP, // KRPP vs KRP
KPsK, // King and pawns vs king
KBPKB, // KBP vs KB
KBPPKB, // KBPP vs KB
KBPKN, // KBP vs KN
KNPK, // KNP vs K
KPKP // KP vs KP
};
/// Base and derived templates for endgame evaluation and scaling functions
template<typename T>
struct EndgameBase {
typedef EndgameBase<T> Base;
virtual ~EndgameBase() {}
virtual Color color() const = 0;
virtual T apply(const Position&) const = 0;
};
template<typename T, EndgameType>
struct Endgame : public EndgameBase<T> {
explicit Endgame(Color c) : strongerSide(c), weakerSide(opposite_color(c)) {}
Color color() const { return strongerSide; }
T apply(const Position&) const;
private:
Color strongerSide, weakerSide;
};
/// Endgames class stores in two std::map the pointers to endgame evaluation
/// and scaling base objects. Then we use polymorphism to invoke the actual
/// endgame function calling its apply() method that is virtual.
class Endgames {
typedef std::map<Key, EndgameBase<Value>* > EFMap;
typedef std::map<Key, EndgameBase<ScaleFactor>* > SFMap;
public:
Endgames();
~Endgames();
template<class T> T* get(Key key) const;
private:
template<class T> void add(const std::string& keyCode);
// Here we store two maps, for evaluate and scaling functions...
std::pair<EFMap, SFMap> maps;
// ...and here is the accessing template function
template<typename T> const std::map<Key, T*>& get() const;
};
#endif // !defined(ENDGAME_H_INCLUDED)

File diff suppressed because it is too large Load Diff

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-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(EVALUATE_H_INCLUDED)
#define EVALUATE_H_INCLUDED
#include "types.h"
class Position;
extern Value evaluate(const Position& pos, Value& margin);
extern std::string trace_evaluate(const Position& pos);
extern void read_evaluation_uci_options(Color sideToMove);
#endif // !defined(EVALUATE_H_INCLUDED)

View File

@ -0,0 +1,70 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(HISTORY_H_INCLUDED)
#define HISTORY_H_INCLUDED
#include <cstring>
#include "types.h"
/// The History class stores statistics about how often different moves
/// have been successful or unsuccessful during the current search. These
/// statistics are used for reduction and move ordering decisions. History
/// entries are stored according only to moving piece and destination square,
/// in particular two moves with different origin but same destination and
/// same piece will be considered identical.
class History {
public:
void clear();
Value value(Piece p, Square to) const;
void update(Piece p, Square to, Value bonus);
Value gain(Piece p, Square to) const;
void update_gain(Piece p, Square to, Value g);
static const Value MaxValue = Value(2000);
private:
Value history[16][64]; // [piece][to_square]
Value maxGains[16][64]; // [piece][to_square]
};
inline void History::clear() {
memset(history, 0, 16 * 64 * sizeof(Value));
memset(maxGains, 0, 16 * 64 * sizeof(Value));
}
inline Value History::value(Piece p, Square to) const {
return history[p][to];
}
inline void History::update(Piece p, Square to, Value bonus) {
if (abs(history[p][to] + bonus) < MaxValue) history[p][to] += bonus;
}
inline Value History::gain(Piece p, Square to) const {
return maxGains[p][to];
}
inline void History::update_gain(Piece p, Square to, Value g) {
maxGains[p][to] = Max(g, maxGains[p][to] - 1);
}
#endif // !defined(HISTORY_H_INCLUDED)

View File

@ -0,0 +1,79 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(LOCK_H_INCLUDED)
#define LOCK_H_INCLUDED
#if !defined(_MSC_VER)
# include <pthread.h>
typedef pthread_mutex_t Lock;
typedef pthread_cond_t WaitCondition;
# define lock_init(x) pthread_mutex_init(x, NULL)
# define lock_grab(x) pthread_mutex_lock(x)
# define lock_release(x) pthread_mutex_unlock(x)
# define lock_destroy(x) pthread_mutex_destroy(x)
# define cond_destroy(x) pthread_cond_destroy(x)
# define cond_init(x) pthread_cond_init(x, NULL)
# define cond_signal(x) pthread_cond_signal(x)
# define cond_wait(x,y) pthread_cond_wait(x,y)
#else
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#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 HANDLE WaitCondition;
# define lock_init(x) InitializeCriticalSection(x)
# define lock_grab(x) EnterCriticalSection(x)
# define lock_release(x) LeaveCriticalSection(x)
# define lock_destroy(x) DeleteCriticalSection(x)
# define cond_init(x) { *x = CreateEvent(0, FALSE, FALSE, 0); }
# define cond_destroy(x) CloseHandle(*x)
# define cond_signal(x) SetEvent(*x)
# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(*x, INFINITE); lock_grab(y); }
#endif
#endif
#endif // !defined(LOCK_H_INCLUDED)

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-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/>.
*/
// To profile with callgrind uncomment following line
//#define USE_CALLGRIND
#include <cstdio>
#include <iostream>
#include <string>
#include "bitboard.h"
#include "evaluate.h"
#include "position.h"
#include "thread.h"
#include "search.h"
#include "ucioption.h"
#ifdef USE_CALLGRIND
#include <valgrind/callgrind.h>
#endif
using namespace std;
extern bool execute_uci_command(const string& cmd);
extern void benchmark(int argc, char* argv[]);
extern void init_kpk_bitbase();
int main(int argc, char* argv[]) {
// Disable IO buffering for C and C++ standard libraries
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
cout.rdbuf()->pubsetbuf(NULL, 0);
cin.rdbuf()->pubsetbuf(NULL, 0);
// Startup initializations
init_bitboards();
Position::init_zobrist();
Position::init_piece_square_tables();
init_kpk_bitbase();
init_search();
Threads.init();
#ifdef USE_CALLGRIND
CALLGRIND_START_INSTRUMENTATION;
#endif
if (argc < 2)
{
// Print copyright notice
cout << engine_name() << " by " << engine_authors() << endl;
if (CpuHasPOPCNT)
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 to
// ensure that we exit gracefully if the GUI dies unexpectedly.
string cmd;
while (getline(cin, cmd) && execute_uci_command(cmd)) {}
}
else if (string(argv[1]) == "bench" && argc < 8)
benchmark(argc, argv);
else
cout << "Usage: stockfish bench [hash size = 128] [threads = 1] "
<< "[limit = 12] [fen positions file = default] "
<< "[limited by depth, time, nodes or perft = depth]" << endl;
Threads.exit();
return 0;
}

View File

@ -0,0 +1,285 @@
/*
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/>.
*/
#include <cassert>
#include <cstring>
#include "material.h"
using namespace std;
namespace {
// Values modified by Joona Kiiski
const Value MidgameLimit = Value(15581);
const Value EndgameLimit = Value(3998);
// Scale factors used when one side has no more pawns
const int NoPawnsSF[4] = { 6, 12, 32 };
// Polynomial material balance parameters
const Value RedundantQueenPenalty = Value(320);
const Value RedundantRookPenalty = Value(554);
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
const int QuadraticCoefficientsSameColor[][8] = {
{ 7, 7, 7, 7, 7, 7 }, { 39, 2, 7, 7, 7, 7 }, { 35, 271, -4, 7, 7, 7 },
{ 7, 25, 4, 7, 7, 7 }, { -27, -2, 46, 100, 56, 7 }, { 58, 29, 83, 148, -3, -25 } };
const int QuadraticCoefficientsOppositeColor[][8] = {
{ 41, 41, 41, 41, 41, 41 }, { 37, 41, 41, 41, 41, 41 }, { 10, 62, 41, 41, 41, 41 },
{ 57, 64, 39, 41, 41, 41 }, { 50, 40, 23, -22, 41, 41 }, { 106, 101, 3, 151, 171, 41 } };
// Endgame evaluation and scaling functions accessed direcly and not through
// the function maps because correspond to more then one material hash key.
Endgame<Value, KmmKm> EvaluateKmmKm[] = { Endgame<Value, KmmKm>(WHITE), Endgame<Value, KmmKm>(BLACK) };
Endgame<Value, KXK> EvaluateKXK[] = { Endgame<Value, KXK>(WHITE), Endgame<Value, KXK>(BLACK) };
Endgame<ScaleFactor, KBPsK> ScaleKBPsK[] = { Endgame<ScaleFactor, KBPsK>(WHITE), Endgame<ScaleFactor, KBPsK>(BLACK) };
Endgame<ScaleFactor, KQKRPs> ScaleKQKRPs[] = { Endgame<ScaleFactor, KQKRPs>(WHITE), Endgame<ScaleFactor, KQKRPs>(BLACK) };
Endgame<ScaleFactor, KPsK> ScaleKPsK[] = { Endgame<ScaleFactor, KPsK>(WHITE), Endgame<ScaleFactor, KPsK>(BLACK) };
Endgame<ScaleFactor, KPKP> ScaleKPKP[] = { Endgame<ScaleFactor, KPKP>(WHITE), Endgame<ScaleFactor, 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_KBPsKs(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;
}
} // namespace
/// MaterialInfoTable c'tor and d'tor allocate and free the space for Endgames
void MaterialInfoTable::init() { Base::init(); if (!funcs) funcs = new Endgames(); }
MaterialInfoTable::~MaterialInfoTable() { delete funcs; }
/// MaterialInfoTable::get_material_info() takes a position object as input,
/// computes or looks up a MaterialInfo object, and returns a pointer to it.
/// If the material configuration is not already present in the table, it
/// is stored there, so we don't have to recompute everything when the
/// same material configuration occurs again.
MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) const {
Key key = pos.get_material_key();
MaterialInfo* mi = probe(key);
// If mi->key matches the position's material hash key, it means that we
// have analysed this material configuration before, and we can simply
// return the information we found the last time instead of recomputing it.
if (mi->key == key)
return mi;
// Initialize MaterialInfo entry
memset(mi, 0, sizeof(MaterialInfo));
mi->key = key;
mi->factor[WHITE] = mi->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
// Store game phase
mi->gamePhase = MaterialInfoTable::game_phase(pos);
// Let's look if we have a specialized evaluation function for this
// particular material configuration. First we look for a fixed
// configuration one, then a generic one if previous search failed.
if ((mi->evaluationFunction = funcs->get<EndgameBase<Value> >(key)) != NULL)
return mi;
if (is_KXK<WHITE>(pos))
{
mi->evaluationFunction = &EvaluateKXK[WHITE];
return mi;
}
if (is_KXK<BLACK>(pos))
{
mi->evaluationFunction = &EvaluateKXK[BLACK];
return mi;
}
if (!pos.pieces(PAWN) && !pos.pieces(ROOK) && !pos.pieces(QUEEN))
{
// Minor piece endgame with at least one minor piece per side and
// no pawns. Note that the case KmmK is already handled by KXK.
assert((pos.pieces(KNIGHT, WHITE) | pos.pieces(BISHOP, WHITE)));
assert((pos.pieces(KNIGHT, BLACK) | pos.pieces(BISHOP, BLACK)));
if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2
&& pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2)
{
mi->evaluationFunction = &EvaluateKmmKm[WHITE];
return mi;
}
}
// OK, we didn't find any special evaluation function for the current
// material configuration. Is there a suitable scaling function?
//
// We face problems when there are several conflicting applicable
// scaling functions and we need to decide which one to use.
EndgameBase<ScaleFactor>* sf;
if ((sf = funcs->get<EndgameBase<ScaleFactor> >(key)) != NULL)
{
mi->scalingFunction[sf->color()] = sf;
return mi;
}
// Generic scaling functions that refer to more then one material
// distribution. Should be probed after the specialized ones.
// Note that these ones don't return after setting the function.
if (is_KBPsKs<WHITE>(pos))
mi->scalingFunction[WHITE] = &ScaleKBPsK[WHITE];
if (is_KBPsKs<BLACK>(pos))
mi->scalingFunction[BLACK] = &ScaleKBPsK[BLACK];
if (is_KQKRPs<WHITE>(pos))
mi->scalingFunction[WHITE] = &ScaleKQKRPs[WHITE];
else if (is_KQKRPs<BLACK>(pos))
mi->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK];
Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK);
if (npm_w + npm_b == VALUE_ZERO)
{
if (pos.piece_count(BLACK, PAWN) == 0)
{
assert(pos.piece_count(WHITE, PAWN) >= 2);
mi->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
}
else if (pos.piece_count(WHITE, PAWN) == 0)
{
assert(pos.piece_count(BLACK, PAWN) >= 2);
mi->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
}
else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1)
{
// This is a special case because we set scaling functions
// for both colors instead of only one.
mi->scalingFunction[WHITE] = &ScaleKPKP[WHITE];
mi->scalingFunction[BLACK] = &ScaleKPKP[BLACK];
}
}
// No pawns makes it difficult to win, even with a material advantage
if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMidgame)
{
mi->factor[WHITE] = uint8_t
(npm_w == npm_b || npm_w < RookValueMidgame ? 0 : NoPawnsSF[Min(pos.piece_count(WHITE, BISHOP), 2)]);
}
if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMidgame)
{
mi->factor[BLACK] = uint8_t
(npm_w == npm_b || npm_b < RookValueMidgame ? 0 : NoPawnsSF[Min(pos.piece_count(BLACK, BISHOP), 2)]);
}
// Compute the space weight
if (npm_w + npm_b >= 2 * QueenValueMidgame + 4 * RookValueMidgame + 2 * KnightValueMidgame)
{
int minorPieceCount = pos.piece_count(WHITE, KNIGHT) + pos.piece_count(WHITE, BISHOP)
+ pos.piece_count(BLACK, KNIGHT) + pos.piece_count(BLACK, BISHOP);
mi->spaceWeight = minorPieceCount * minorPieceCount;
}
// Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
// for the bishop pair "extended piece", this allow us to be more flexible
// in defining bishop pair bonuses.
const int pieceCount[2][8] = {
{ pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT),
pos.piece_count(WHITE, BISHOP) , pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) },
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT),
pos.piece_count(BLACK, BISHOP) , pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } };
mi->value = int16_t((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16);
return mi;
}
/// MaterialInfoTable::imbalance() calculates imbalance comparing piece count of each
/// piece type for both colors.
template<Color Us>
int MaterialInfoTable::imbalance(const int pieceCount[][8]) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
int pt1, pt2, pc, v;
int value = 0;
// Redundancy of major pieces, formula based on Kaufman's paper
// "The Evaluation of Material Imbalances in Chess"
if (pieceCount[Us][ROOK] > 0)
value -= RedundantRookPenalty * (pieceCount[Us][ROOK] - 1)
+ RedundantQueenPenalty * pieceCount[Us][QUEEN];
// Second-degree polynomial material imbalance by Tord Romstad
for (pt1 = PIECE_TYPE_NONE; pt1 <= QUEEN; pt1++)
{
pc = pieceCount[Us][pt1];
if (!pc)
continue;
v = LinearCoefficients[pt1];
for (pt2 = PIECE_TYPE_NONE; pt2 <= pt1; pt2++)
v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];
value += pc * v;
}
return value;
}
/// MaterialInfoTable::game_phase() calculates the phase given the current
/// position. Because the phase is strictly a function of the material, it
/// is stored in MaterialInfo.
Phase MaterialInfoTable::game_phase(const Position& pos) {
Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK);
return npm >= MidgameLimit ? PHASE_MIDGAME
: npm <= EndgameLimit ? PHASE_ENDGAME
: Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
}

View File

@ -0,0 +1,117 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(MATERIAL_H_INCLUDED)
#define MATERIAL_H_INCLUDED
#include "endgame.h"
#include "position.h"
#include "tt.h"
#include "types.h"
const int MaterialTableSize = 8192;
/// MaterialInfo is a class which contains various information about a
/// material configuration. It contains a material balance evaluation,
/// a function pointer to a special endgame evaluation function (which in
/// most cases is NULL, meaning that the standard evaluation function will
/// be used), and "scale factors" for black and white.
///
/// The scale factors are used to scale the evaluation score up or down.
/// For instance, in KRB vs KR endgames, the score is scaled down by a factor
/// of 4, which will result in scores of absolute value less than one pawn.
class MaterialInfo {
friend class MaterialInfoTable;
public:
Score material_value() const;
ScaleFactor scale_factor(const Position& pos, Color c) const;
int space_weight() const;
Phase game_phase() const;
bool specialized_eval_exists() const;
Value evaluate(const Position& pos) const;
private:
Key key;
int16_t value;
uint8_t factor[2];
EndgameBase<Value>* evaluationFunction;
EndgameBase<ScaleFactor>* scalingFunction[2];
int spaceWeight;
Phase gamePhase;
};
/// The MaterialInfoTable class represents a pawn hash table. The most important
/// method is get_material_info, which returns a pointer to a MaterialInfo object.
class MaterialInfoTable : public SimpleHash<MaterialInfo, MaterialTableSize> {
public:
~MaterialInfoTable();
void init();
MaterialInfo* get_material_info(const Position& pos) const;
static Phase game_phase(const Position& pos);
private:
template<Color Us>
static int imbalance(const int pieceCount[][8]);
Endgames* funcs;
};
/// 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
/// position in addition to the color, because the scale factor need not
/// to be a constant: It can also be a function which should be applied to
/// the position. For instance, in KBP vs K endgames, a scaling function
/// which checks for draws with rook pawns and wrong-colored bishops.
inline ScaleFactor MaterialInfo::scale_factor(const Position& pos, Color c) const {
if (!scalingFunction[c])
return ScaleFactor(factor[c]);
ScaleFactor sf = scalingFunction[c]->apply(pos);
return sf == SCALE_FACTOR_NONE ? ScaleFactor(factor[c]) : sf;
}
inline Value MaterialInfo::evaluate(const Position& pos) const {
return evaluationFunction->apply(pos);
}
inline Score MaterialInfo::material_value() const {
return make_score(value, value);
}
inline int MaterialInfo::space_weight() const {
return spaceWeight;
}
inline Phase MaterialInfo::game_phase() const {
return gamePhase;
}
inline bool MaterialInfo::specialized_eval_exists() const {
return evaluationFunction != NULL;
}
#endif // !defined(MATERIAL_H_INCLUDED)

View File

@ -0,0 +1,270 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(_MSC_VER)
# include <sys/time.h>
# include <sys/types.h>
# include <unistd.h>
# if defined(__hpux)
# include <sys/pstat.h>
# endif
#else
#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#include <sys/timeb.h>
#endif
#if !defined(NO_PREFETCH)
# include <xmmintrin.h>
#endif
#include <cassert>
#include <cstdio>
#include <iomanip>
#include <iostream>
#include <sstream>
#include "bitcount.h"
#include "misc.h"
#include "thread.h"
using namespace std;
/// Version number. If EngineVersion is left empty, then AppTag plus
/// current date (in the format YYMMDD) is used as a version number.
static const string AppName = "Stockfish";
static const string EngineVersion = "2.1";
static const string AppTag = "";
/// engine_name() returns the full name of the current Stockfish version.
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when
/// the program was compiled) or "Stockfish <version number>", depending
/// on whether the constant EngineVersion is empty.
const string engine_name() {
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
const string cpu64(CpuIs64Bit ? " 64bit" : "");
if (!EngineVersion.empty())
return AppName + " " + EngineVersion + cpu64;
stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008"
string month, day, year;
date >> month >> day >> year;
s << setfill('0') << AppName + " " + AppTag + " "
<< year.substr(2, 2) << setw(2)
<< (1 + months.find(month) / 4) << setw(2)
<< day << cpu64;
return s.str();
}
/// Our brave developers! Required by UCI
const string engine_authors() {
return "Tord Romstad, Marco Costalba and Joona Kiiski";
}
/// Debug stuff. Helper functions used mainly for debugging purposes
static uint64_t dbg_hit_cnt0;
static uint64_t dbg_hit_cnt1;
static uint64_t dbg_mean_cnt0;
static uint64_t dbg_mean_cnt1;
void dbg_print_hit_rate() {
if (dbg_hit_cnt0)
cout << "Total " << dbg_hit_cnt0 << " Hit " << dbg_hit_cnt1
<< " hit rate (%) " << 100 * dbg_hit_cnt1 / dbg_hit_cnt0 << endl;
}
void dbg_print_mean() {
if (dbg_mean_cnt0)
cout << "Total " << dbg_mean_cnt0 << " Mean "
<< (float)dbg_mean_cnt1 / dbg_mean_cnt0 << endl;
}
void dbg_mean_of(int v) {
dbg_mean_cnt0++;
dbg_mean_cnt1 += v;
}
void dbg_hit_on(bool b) {
dbg_hit_cnt0++;
if (b)
dbg_hit_cnt1++;
}
void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); }
void dbg_before() { dbg_hit_on(false); }
void dbg_after() { dbg_hit_on(true); dbg_hit_cnt0--; }
/// get_system_time() returns the current system time, measured in milliseconds
int get_system_time() {
#if defined(_MSC_VER)
struct _timeb t;
_ftime(&t);
return int(t.time * 1000 + t.millitm);
#else
struct timeval t;
gettimeofday(&t, NULL);
return t.tv_sec * 1000 + t.tv_usec / 1000;
#endif
}
/// cpu_count() tries to detect the number of CPU cores
int cpu_count() {
#if defined(_MSC_VER)
SYSTEM_INFO s;
GetSystemInfo(&s);
return Min(s.dwNumberOfProcessors, MAX_THREADS);
#else
# if defined(_SC_NPROCESSORS_ONLN)
return Min(sysconf(_SC_NPROCESSORS_ONLN), MAX_THREADS);
# elif defined(__hpux)
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
return 1;
# endif
#endif
}
/// Check for console input. Original code from Beowulf, Olithink and Greko
#ifndef _WIN32
int input_available() {
fd_set readfds;
struct timeval timeout;
FD_ZERO(&readfds);
FD_SET(fileno(stdin), &readfds);
timeout.tv_sec = 0; // Set to timeout immediately
timeout.tv_usec = 0;
select(16, &readfds, 0, 0, &timeout);
return (FD_ISSET(fileno(stdin), &readfds));
}
#else
int input_available() {
static HANDLE inh = NULL;
static bool usePipe = false;
INPUT_RECORD rec[256];
DWORD nchars, recCnt;
if (!inh)
{
inh = GetStdHandle(STD_INPUT_HANDLE);
if (GetConsoleMode(inh, &nchars))
{
SetConsoleMode(inh, nchars & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
FlushConsoleInputBuffer(inh);
} else
usePipe = true;
}
// When using Standard C input functions, also check if there
// is anything in the buffer. After a call to such functions,
// the input waiting in the pipe will be copied to the buffer,
// and the call to PeekNamedPipe can indicate no input available.
// Setting stdin to unbuffered was not enough. [from Greko]
if (stdin->_cnt > 0)
return 1;
// When running under a GUI the input commands are sent to us
// directly over the internal pipe. If PeekNamedPipe() returns 0
// then something went wrong. Probably the parent program exited.
// Returning 1 will make the next call to the input function
// return EOF, where this should be catched then.
if (usePipe)
return PeekNamedPipe(inh, NULL, 0, NULL, &nchars, NULL) ? nchars : 1;
// Count the number of unread input records, including keyboard,
// mouse, and window-resizing input records.
GetNumberOfConsoleInputEvents(inh, &nchars);
// Read data from console without removing it from the buffer
if (nchars <= 0 || !PeekConsoleInput(inh, rec, Min(nchars, 256), &recCnt))
return 0;
// Search for at least one keyboard event
for (DWORD i = 0; i < recCnt; i++)
if (rec[i].EventType == KEY_EVENT)
return 1;
return 0;
}
#endif
/// prefetch() preloads the given address in L1/L2 cache. This is a non
/// blocking function and do not stalls the CPU waiting for data to be
/// loaded from memory, that can be quite 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

View File

@ -0,0 +1,41 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(MISC_H_INCLUDED)
#define MISC_H_INCLUDED
#include <string>
#include "types.h"
extern const std::string engine_name();
extern const std::string engine_authors();
extern int get_system_time();
extern int cpu_count();
extern int input_available();
extern void prefetch(char* addr);
extern void dbg_hit_on(bool b);
extern void dbg_hit_on_c(bool c, bool b);
extern void dbg_before();
extern void dbg_after();
extern void dbg_mean_of(int v);
extern void dbg_print_hit_rate();
extern void dbg_print_mean();
#endif // !defined(MISC_H_INCLUDED)

View File

@ -0,0 +1,258 @@
/*
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/>.
*/
#include <cassert>
#include <cstring>
#include <iomanip>
#include <string>
#include <sstream>
#include "move.h"
#include "movegen.h"
#include "search.h"
using std::string;
namespace {
const string time_string(int milliseconds);
const string score_string(Value v);
}
/// move_to_uci() converts a move to a string in coordinate notation
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we
/// print in the e1g1 notation in normal chess mode, and in e1h1 notation in
/// Chess960 mode.
const string move_to_uci(Move m, bool chess960) {
Square from = move_from(m);
Square to = move_to(m);
string promotion;
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "0000";
if (move_is_short_castle(m) && !chess960)
return from == SQ_E1 ? "e1g1" : "e8g8";
if (move_is_long_castle(m) && !chess960)
return from == SQ_E1 ? "e1c1" : "e8c8";
if (move_is_promotion(m))
promotion = char(tolower(piece_type_to_char(move_promotion_piece(m))));
return square_to_string(from) + square_to_string(to) + promotion;
}
/// move_from_uci() takes a position and a string representing a move in
/// simple coordinate notation and returns an equivalent Move if any.
/// Moves are guaranteed to be legal.
Move move_from_uci(const Position& pos, const string& str) {
MoveStack mlist[MAX_MOVES];
MoveStack* last = generate<MV_LEGAL>(pos, mlist);
for (MoveStack* cur = mlist; cur != last; cur++)
if (str == move_to_uci(cur->move, pos.is_chess960()))
return cur->move;
return MOVE_NONE;
}
/// move_to_san() takes a position and a move as input, where it is assumed
/// that the move is a legal move from the position. The return value is
/// a string containing the move in short algebraic notation.
const string move_to_san(Position& pos, Move m) {
assert(pos.is_ok());
assert(move_is_ok(m));
MoveStack mlist[MAX_MOVES];
Square from = move_from(m);
Square to = move_to(m);
PieceType pt = pos.type_of_piece_on(from);
string san;
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "(null)";
if (move_is_long_castle(m))
san = "O-O-O";
else if (move_is_short_castle(m))
san = "O-O";
else
{
if (pt != PAWN)
{
san = piece_type_to_char(pt);
// Collect all legal moves of piece type 'pt' with destination 'to'
MoveStack* last = generate<MV_LEGAL>(pos, mlist);
int f = 0, r = 0;
for (MoveStack* cur = mlist; cur != last; cur++)
if ( move_to(cur->move) == to
&& pos.type_of_piece_on(move_from(cur->move)) == pt)
{
if (square_file(move_from(cur->move)) == square_file(from))
f++;
if (square_rank(move_from(cur->move)) == square_rank(from))
r++;
}
assert(f > 0 && r > 0);
// Disambiguation if we have more then one piece with destination 'to'
if (f == 1 && r > 1)
san += file_to_char(square_file(from));
else if (f > 1 && r == 1)
san += rank_to_char(square_rank(from));
else if (f > 1 && r > 1)
san += square_to_string(from);
}
if (pos.move_is_capture(m))
{
if (pt == PAWN)
san += file_to_char(square_file(from));
san += 'x';
}
san += square_to_string(to);
if (move_is_promotion(m))
{
san += '=';
san += piece_type_to_char(move_promotion_piece(m));
}
}
// The move gives check? We don't use pos.move_gives_check() here
// because we need to test for a mate after the move is done.
StateInfo st;
pos.do_move(m, st);
if (pos.in_check())
san += pos.is_mate() ? "#" : "+";
pos.undo_move(m);
return san;
}
/// pretty_pv() creates a human-readable string from a position and a PV.
/// It is used to write search information to the log file (which is created
/// when the UCI parameter "Use Search Log" is "true").
const string pretty_pv(Position& pos, int depth, Value score, int time, Move pv[]) {
const int64_t K = 1000;
const int64_t M = 1000000;
const int startColumn = 28;
const size_t maxLength = 80 - startColumn;
const string lf = string("\n") + string(startColumn, ' ');
StateInfo state[PLY_MAX_PLUS_2], *st = state;
Move* m = pv;
string san;
std::stringstream s;
size_t length = 0;
// First print depth, score, time and searched nodes...
s << std::setw(2) << depth
<< std::setw(8) << score_string(score)
<< std::setw(8) << time_string(time);
if (pos.nodes_searched() < M)
s << std::setw(8) << pos.nodes_searched() / 1 << " ";
else if (pos.nodes_searched() < K * M)
s << std::setw(7) << pos.nodes_searched() / K << "K ";
else
s << std::setw(7) << pos.nodes_searched() / M << "M ";
// ...then print the full PV line in short algebraic notation
while (*m != MOVE_NONE)
{
san = move_to_san(pos, *m);
length += san.length() + 1;
if (length > maxLength)
{
length = san.length() + 1;
s << lf;
}
s << san << ' ';
pos.do_move(*m++, *st++);
}
// Restore original position before to leave
while (m != pv) pos.undo_move(*--m);
return s.str();
}
namespace {
const string time_string(int millisecs) {
const int MSecMinute = 1000 * 60;
const int MSecHour = 1000 * 60 * 60;
int hours = millisecs / MSecHour;
int minutes = (millisecs % MSecHour) / MSecMinute;
int seconds = ((millisecs % MSecHour) % MSecMinute) / 1000;
std::stringstream s;
if (hours)
s << hours << ':';
s << std::setfill('0') << std::setw(2) << minutes << ':' << std::setw(2) << seconds;
return s.str();
}
const string score_string(Value v) {
std::stringstream s;
if (v >= VALUE_MATE - 200)
s << "#" << (VALUE_MATE - v + 1) / 2;
else if (v <= -VALUE_MATE + 200)
s << "-#" << (VALUE_MATE + v) / 2;
else
s << std::setprecision(2) << std::fixed << std::showpos << float(v) / PawnValueMidgame;
return s.str();
}
}

View File

@ -0,0 +1,195 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(MOVE_H_INCLUDED)
#define MOVE_H_INCLUDED
#include <string>
#include "misc.h"
#include "types.h"
// Maximum number of allowed moves per position
const int MAX_MOVES = 256;
/// A move needs 16 bits to be stored
///
/// bit 0- 5: destination square (from 0 to 63)
/// bit 6-11: origin square (from 0 to 63)
/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
/// bit 14-15: special move flag: promotion (1), en passant (2), castle (3)
///
/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in
/// because in any normal move destination square is always different
/// from origin square while MOVE_NONE and MOVE_NULL have the same
/// origin and destination square, 0 and 1 respectively.
enum Move {
MOVE_NONE = 0,
MOVE_NULL = 65
};
struct MoveStack {
Move move;
int score;
};
inline bool operator<(const MoveStack& f, const MoveStack& s) { return f.score < s.score; }
// An helper insertion sort implementation, works with pointers and iterators
template<typename T, typename K>
inline void insertion_sort(K firstMove, K lastMove)
{
T value;
K cur, p, d;
if (firstMove != lastMove)
for (cur = firstMove + 1; cur != lastMove; cur++)
{
p = d = cur;
value = *p--;
if (*p < value)
{
do *d = *p;
while (--d != firstMove && *--p < value);
*d = value;
}
}
}
// Our dedicated sort in range [firstMove, lastMove), first splits
// positive scores from ramining then order seaprately the two sets.
template<typename T>
inline void sort_moves(T* firstMove, T* lastMove, T** lastPositive)
{
T tmp;
T *p, *d;
d = lastMove;
p = firstMove - 1;
d->score = -1; // right guard
// Split positives vs non-positives
do {
while ((++p)->score > 0) {}
if (p != d)
{
while (--d != p && d->score <= 0) {}
tmp = *p;
*p = *d;
*d = tmp;
}
} while (p != d);
// Sort just positive scored moves, remaining only when we get there
insertion_sort<T, T*>(firstMove, p);
*lastPositive = p;
}
// Picks up the best move in range [curMove, lastMove), one per cycle.
// It is faster then sorting all the moves in advance when moves are few,
// as normally are the possible captures. Note that is not a stable alghoritm.
template<typename T>
inline T pick_best(T* curMove, T* lastMove)
{
T bestMove, tmp;
bestMove = *curMove;
while (++curMove != lastMove)
{
if (bestMove < *curMove)
{
tmp = *curMove;
*curMove = bestMove;
bestMove = tmp;
}
}
return bestMove;
}
inline Square move_from(Move m) {
return Square((int(m) >> 6) & 0x3F);
}
inline Square move_to(Move m) {
return Square(m & 0x3F);
}
inline bool move_is_special(Move m) {
return m & (3 << 14);
}
inline bool move_is_promotion(Move m) {
return (m & (3 << 14)) == (1 << 14);
}
inline int move_is_ep(Move m) {
return (m & (3 << 14)) == (2 << 14);
}
inline int move_is_castle(Move m) {
return (m & (3 << 14)) == (3 << 14);
}
inline bool move_is_short_castle(Move m) {
return move_is_castle(m) && (move_to(m) > move_from(m));
}
inline bool move_is_long_castle(Move m) {
return move_is_castle(m) && (move_to(m) < move_from(m));
}
inline PieceType move_promotion_piece(Move m) {
return move_is_promotion(m) ? PieceType(((int(m) >> 12) & 3) + 2) : PIECE_TYPE_NONE;
}
inline Move make_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6));
}
inline Move make_promotion_move(Square from, Square to, PieceType promotion) {
return Move(int(to) | (int(from) << 6) | ((int(promotion) - 2) << 12) | (1 << 14));
}
inline Move make_ep_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6) | (2 << 14));
}
inline Move make_castle_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6) | (3 << 14));
}
inline bool move_is_ok(Move m) {
return move_from(m) != move_to(m); // Catches also MOVE_NONE
}
class Position;
extern const std::string move_to_uci(Move m, bool chess960);
extern Move move_from_uci(const Position& pos, const std::string& str);
extern const std::string move_to_san(Position& pos, Move m);
extern const std::string pretty_pv(Position& pos, int depth, Value score, int time, Move pv[]);
#endif // !defined(MOVE_H_INCLUDED)

View File

@ -0,0 +1,542 @@
/*
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/>.
*/
#include <cassert>
#include "bitcount.h"
#include "movegen.h"
// Simple macro to wrap a very common while loop, no facny, no flexibility,
// hardcoded list name 'mlist' and from square 'from'.
#define SERIALIZE_MOVES(b) while (b) (*mlist++).move = make_move(from, pop_1st_bit(&b))
// Version used for pawns, where the 'from' square is given as a delta from the 'to' square
#define SERIALIZE_MOVES_D(b, d) while (b) { to = pop_1st_bit(&b); (*mlist++).move = make_move(to + (d), to); }
namespace {
enum CastlingSide {
KING_SIDE,
QUEEN_SIDE
};
template<CastlingSide>
MoveStack* generate_castle_moves(const Position&, MoveStack*, Color us);
template<Color, MoveType>
MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard, Square);
template<PieceType Pt>
inline MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) {
assert(Pt != QUEEN);
Bitboard b = pos.attacks_from<Pt>(from) & pos.empty_squares();
if (Pt == KING)
{
Square ksq = pos.king_square(opposite_color(pos.side_to_move()));
b &= ~QueenPseudoAttacks[ksq];
}
SERIALIZE_MOVES(b);
return mlist;
}
template<PieceType Pt>
inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us,
Bitboard dc, Square ksq) {
assert(Pt != KING);
Bitboard checkSqs, b;
Square from;
const Square* ptr = pos.piece_list_begin(us, Pt);
if ((from = *ptr++) == SQ_NONE)
return mlist;
checkSqs = pos.attacks_from<Pt>(ksq) & pos.empty_squares();
do
{
if ( (Pt == QUEEN && !(QueenPseudoAttacks[from] & checkSqs))
|| (Pt == ROOK && !(RookPseudoAttacks[from] & checkSqs))
|| (Pt == BISHOP && !(BishopPseudoAttacks[from] & checkSqs)))
continue;
if (dc && bit_is_set(dc, from))
continue;
b = pos.attacks_from<Pt>(from) & checkSqs;
SERIALIZE_MOVES(b);
} while ((from = *ptr++) != SQ_NONE);
return mlist;
}
template<>
FORCE_INLINE MoveStack* generate_direct_checks<PAWN>(const Position& p, MoveStack* m, Color us, Bitboard dc, Square ksq) {
return (us == WHITE ? generate_pawn_moves<WHITE, MV_CHECK>(p, m, dc, ksq)
: generate_pawn_moves<BLACK, MV_CHECK>(p, m, dc, ksq));
}
template<PieceType Pt, MoveType Type>
FORCE_INLINE MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) {
assert(Pt == PAWN);
assert(Type == MV_CAPTURE || Type == MV_NON_CAPTURE || Type == MV_EVASION);
return (us == WHITE ? generate_pawn_moves<WHITE, Type>(p, m, t, SQ_NONE)
: generate_pawn_moves<BLACK, Type>(p, m, t, SQ_NONE));
}
template<PieceType Pt>
FORCE_INLINE MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
Bitboard b;
Square from;
const Square* ptr = pos.piece_list_begin(us, Pt);
if (*ptr != SQ_NONE)
{
do {
from = *ptr;
b = pos.attacks_from<Pt>(from) & target;
SERIALIZE_MOVES(b);
} while (*++ptr != SQ_NONE);
}
return mlist;
}
template<>
FORCE_INLINE MoveStack* generate_piece_moves<KING>(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
Bitboard b;
Square from = pos.king_square(us);
b = pos.attacks_from<KING>(from) & target;
SERIALIZE_MOVES(b);
return mlist;
}
}
/// generate<MV_CAPTURE> generates all pseudo-legal captures and queen
/// promotions. Returns a pointer to the end of the move list.
///
/// generate<MV_NON_CAPTURE> generates all pseudo-legal non-captures and
/// underpromotions. Returns a pointer to the end of the move list.
///
/// generate<MV_NON_EVASION> generates all pseudo-legal captures and
/// non-captures. Returns a pointer to the end of the move list.
template<MoveType Type>
MoveStack* generate(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.in_check());
Color us = pos.side_to_move();
Bitboard target;
if (Type == MV_CAPTURE || Type == MV_NON_EVASION)
target = pos.pieces_of_color(opposite_color(us));
else if (Type == MV_NON_CAPTURE)
target = pos.empty_squares();
else
assert(false);
if (Type == MV_NON_EVASION)
{
mlist = generate_piece_moves<PAWN, MV_CAPTURE>(pos, mlist, us, target);
mlist = generate_piece_moves<PAWN, MV_NON_CAPTURE>(pos, mlist, us, pos.empty_squares());
target |= pos.empty_squares();
}
else
mlist = generate_piece_moves<PAWN, Type>(pos, mlist, us, target);
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);
if (Type != MV_CAPTURE)
{
if (pos.can_castle_kingside(us))
mlist = generate_castle_moves<KING_SIDE>(pos, mlist, us);
if (pos.can_castle_queenside(us))
mlist = generate_castle_moves<QUEEN_SIDE>(pos, mlist, us);
}
return mlist;
}
// Explicit template instantiations
template MoveStack* generate<MV_CAPTURE>(const Position& pos, MoveStack* mlist);
template MoveStack* generate<MV_NON_CAPTURE>(const Position& pos, MoveStack* mlist);
template MoveStack* generate<MV_NON_EVASION>(const Position& pos, MoveStack* mlist);
/// 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.
template<>
MoveStack* generate<MV_NON_CAPTURE_CHECK>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.in_check());
Bitboard b, dc;
Square from;
Color us = pos.side_to_move();
Square ksq = pos.king_square(opposite_color(us));
assert(pos.piece_on(ksq) == make_piece(opposite_color(us), KING));
// Discovered non-capture checks
b = dc = pos.discovered_check_candidates(us);
while (b)
{
from = pop_1st_bit(&b);
switch (pos.type_of_piece_on(from))
{
case PAWN: /* Will be generated togheter with pawns direct checks */ break;
case KNIGHT: mlist = generate_discovered_checks<KNIGHT>(pos, mlist, from); break;
case BISHOP: mlist = generate_discovered_checks<BISHOP>(pos, mlist, from); break;
case ROOK: mlist = generate_discovered_checks<ROOK>(pos, mlist, from); break;
case KING: mlist = generate_discovered_checks<KING>(pos, mlist, from); break;
default: assert(false); break;
}
}
// Direct non-capture checks
mlist = generate_direct_checks<PAWN>(pos, mlist, us, dc, ksq);
mlist = generate_direct_checks<KNIGHT>(pos, mlist, us, dc, ksq);
mlist = generate_direct_checks<BISHOP>(pos, mlist, us, dc, ksq);
mlist = generate_direct_checks<ROOK>(pos, mlist, us, dc, ksq);
return generate_direct_checks<QUEEN>(pos, mlist, us, dc, ksq);
}
/// generate_evasions() generates all pseudo-legal check evasions when
/// the side to move is in check. Returns a pointer to the end of the move list.
template<>
MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(pos.in_check());
Bitboard b, target;
Square from, checksq;
int checkersCnt = 0;
Color us = pos.side_to_move();
Square ksq = pos.king_square(us);
Bitboard checkers = pos.checkers();
Bitboard sliderAttacks = EmptyBoardBB;
assert(pos.piece_on(ksq) == make_piece(us, KING));
assert(checkers);
// Find squares attacked by slider checkers, we will remove
// them from the king evasions set so to early skip known
// illegal moves and avoid an useless legality check later.
b = checkers;
do
{
checkersCnt++;
checksq = pop_1st_bit(&b);
assert(pos.color_of_piece_on(checksq) == opposite_color(us));
switch (pos.type_of_piece_on(checksq))
{
case BISHOP: sliderAttacks |= BishopPseudoAttacks[checksq]; break;
case ROOK: sliderAttacks |= RookPseudoAttacks[checksq]; break;
case QUEEN:
// In case of a queen remove also squares attacked in the other direction to
// avoid possible illegal moves when queen and king are on adjacent squares.
if (RookPseudoAttacks[checksq] & (1ULL << ksq))
sliderAttacks |= RookPseudoAttacks[checksq] | pos.attacks_from<BISHOP>(checksq);
else
sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq);
default:
break;
}
} while (b);
// Generate evasions for king, capture and non capture moves
b = pos.attacks_from<KING>(ksq) & ~pos.pieces_of_color(us) & ~sliderAttacks;
from = ksq;
SERIALIZE_MOVES(b);
// Generate evasions for other pieces only if not double check
if (checkersCnt > 1)
return mlist;
// Find squares where a blocking evasion or a capture of the
// checker piece is possible.
target = squares_between(checksq, ksq) | checkers;
mlist = generate_piece_moves<PAWN, MV_EVASION>(pos, mlist, us, target);
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);
return generate_piece_moves<QUEEN>(pos, mlist, us, target);
}
/// generate<MV_LEGAL / MV_PSEUDO_LEGAL> computes a complete list of legal
/// or pseudo-legal moves in the current position.
template<>
MoveStack* generate<MV_PSEUDO_LEGAL>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
return pos.in_check() ? generate<MV_EVASION>(pos, mlist)
: generate<MV_NON_EVASION>(pos, mlist);
}
template<>
MoveStack* generate<MV_LEGAL>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
MoveStack *last, *cur = mlist;
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
last = generate<MV_PSEUDO_LEGAL>(pos, mlist);
// Remove illegal moves from the list
while (cur != last)
if (!pos.pl_move_is_legal(cur->move, pinned))
cur->move = (--last)->move;
else
cur++;
return last;
}
namespace {
template<Square Delta>
inline Bitboard move_pawns(Bitboard p) {
return Delta == DELTA_N ? p << 8 : Delta == DELTA_S ? p >> 8 :
Delta == DELTA_NE ? p << 9 : Delta == DELTA_SE ? p >> 7 :
Delta == DELTA_NW ? p << 7 : Delta == DELTA_SW ? p >> 9 : p;
}
template<MoveType Type, Square Delta>
inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard target) {
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB);
Bitboard b;
Square to;
// 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;
SERIALIZE_MOVES_D(b, -Delta);
return mlist;
}
template<Color Us, MoveType Type, Square Delta>
inline MoveStack* generate_promotions(const Position& pos, MoveStack* mlist, Bitboard pawnsOn7, Bitboard target) {
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB);
Bitboard b;
Square to;
// Promotions and under-promotions, both captures and non-captures
b = move_pawns<Delta>(pawnsOn7) & target;
if (Delta != DELTA_N && Delta != DELTA_S)
b &= ~TFileABB;
while (b)
{
to = pop_1st_bit(&b);
if (Type == MV_CAPTURE || Type == MV_EVASION)
(*mlist++).move = make_promotion_move(to - Delta, to, QUEEN);
if (Type == MV_NON_CAPTURE || Type == MV_EVASION)
{
(*mlist++).move = make_promotion_move(to - Delta, to, ROOK);
(*mlist++).move = make_promotion_move(to - Delta, to, BISHOP);
(*mlist++).move = make_promotion_move(to - Delta, to, KNIGHT);
}
// This is the only possible under promotion that can give a check
// not already included in the queen-promotion.
if ( Type == MV_CHECK
&& bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(opposite_color(Us))))
(*mlist++).move = make_promotion_move(to - Delta, to, KNIGHT);
else (void)pos; // Silence a warning under MSVC
}
return mlist;
}
template<Color Us, MoveType Type>
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) {
// Calculate our parametrized parameters at compile time, named
// according to the point of view of white side.
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const Square TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S);
const Square TDELTA_NE = (Us == WHITE ? DELTA_NE : DELTA_SE);
const Square TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW);
Square to;
Bitboard b1, b2, dc1, dc2, pawnPushes, emptySquares;
Bitboard pawns = pos.pieces(PAWN, Us);
Bitboard pawnsOn7 = pawns & TRank7BB;
Bitboard enemyPieces = (Type == MV_CAPTURE ? target : pos.pieces_of_color(Them));
// Pre-calculate pawn pushes before changing emptySquares definition
if (Type != MV_CAPTURE)
{
emptySquares = (Type == MV_NON_CAPTURE ? target : pos.empty_squares());
pawnPushes = move_pawns<TDELTA_N>(pawns & ~TRank7BB) & emptySquares;
}
if (Type == MV_EVASION)
{
emptySquares &= target; // Only blocking squares
enemyPieces &= target; // Capture only the checker piece
}
// Promotions and underpromotions
if (pawnsOn7)
{
if (Type == MV_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 == MV_CAPTURE || Type == MV_EVASION)
{
mlist = generate_pawn_captures<Type, TDELTA_NE>(mlist, pawns, enemyPieces);
mlist = generate_pawn_captures<Type, TDELTA_NW>(mlist, pawns, enemyPieces);
}
// Single and double pawn pushes
if (Type != MV_CAPTURE)
{
b1 = pawnPushes & emptySquares;
b2 = move_pawns<TDELTA_N>(pawnPushes & TRank3BB) & emptySquares;
if (Type == MV_CHECK)
{
// Consider only pawn moves which give direct checks
b1 &= pos.attacks_from<PAWN>(ksq, Them);
b2 &= pos.attacks_from<PAWN>(ksq, Them);
// Add pawn moves which gives discovered check. This is possible only
// if the pawn is not on the same file as the enemy king, because we
// don't generate captures.
if (pawns & target) // For CHECK type target is dc bitboard
{
dc1 = move_pawns<TDELTA_N>(pawns & target & ~file_bb(ksq)) & emptySquares;
dc2 = move_pawns<TDELTA_N>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1;
b2 |= dc2;
}
}
SERIALIZE_MOVES_D(b1, -TDELTA_N);
SERIALIZE_MOVES_D(b2, -TDELTA_N -TDELTA_N);
}
// En passant captures
if ((Type == MV_CAPTURE || Type == MV_EVASION) && pos.ep_square() != SQ_NONE)
{
assert(Us != WHITE || square_rank(pos.ep_square()) == RANK_6);
assert(Us != BLACK || square_rank(pos.ep_square()) == RANK_3);
// An en passant capture can be an evasion only if the checking piece
// is the double pushed pawn and so is in the target. Otherwise this
// is a discovery check and we are forced to do otherwise.
if (Type == MV_EVASION && !bit_is_set(target, pos.ep_square() - TDELTA_N))
return mlist;
b1 = pawns & pos.attacks_from<PAWN>(pos.ep_square(), Them);
assert(b1 != EmptyBoardBB);
while (b1)
{
to = pop_1st_bit(&b1);
(*mlist++).move = make_ep_move(to, pos.ep_square());
}
}
return mlist;
}
template<CastlingSide Side>
MoveStack* generate_castle_moves(const Position& pos, MoveStack* mlist, Color us) {
Color them = opposite_color(us);
Square ksq = pos.king_square(us);
assert(pos.piece_on(ksq) == make_piece(us, KING));
Square rsq = (Side == KING_SIDE ? pos.initial_kr_square(us) : pos.initial_qr_square(us));
Square s1 = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
Square s2 = relative_square(us, Side == KING_SIDE ? SQ_F1 : SQ_D1);
Square s;
bool illegal = false;
assert(pos.piece_on(rsq) == make_piece(us, ROOK));
// It is a bit complicated to correctly handle Chess960
for (s = Min(ksq, s1); s <= Max(ksq, s1); s++)
if ( (s != ksq && s != rsq && pos.square_is_occupied(s))
||(pos.attackers_to(s) & pos.pieces_of_color(them)))
illegal = true;
for (s = Min(rsq, s2); s <= Max(rsq, s2); s++)
if (s != ksq && s != rsq && pos.square_is_occupied(s))
illegal = true;
if ( Side == QUEEN_SIDE
&& square_file(rsq) == FILE_B
&& ( pos.piece_on(relative_square(us, SQ_A1)) == make_piece(them, ROOK)
|| pos.piece_on(relative_square(us, SQ_A1)) == make_piece(them, QUEEN)))
illegal = true;
if (!illegal)
(*mlist++).move = make_castle_move(ksq, rsq);
return mlist;
}
} // namespace

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-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(MOVEGEN_H_INCLUDED)
#define MOVEGEN_H_INCLUDED
#include "move.h"
#include "position.h"
enum MoveType {
MV_CAPTURE,
MV_NON_CAPTURE,
MV_CHECK,
MV_NON_CAPTURE_CHECK,
MV_EVASION,
MV_NON_EVASION,
MV_LEGAL,
MV_PSEUDO_LEGAL
};
template<MoveType>
MoveStack* generate(const Position& pos, MoveStack* mlist);
#endif // !defined(MOVEGEN_H_INCLUDED)

View File

@ -0,0 +1,350 @@
/*
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/>.
*/
#include <cassert>
#include "movegen.h"
#include "movepick.h"
#include "search.h"
#include "types.h"
namespace {
enum MovegenPhase {
PH_TT_MOVES, // Transposition table move and mate killer
PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= 0
PH_KILLERS, // Killer moves from the current ply
PH_NONCAPTURES, // Non-captures and underpromotions
PH_BAD_CAPTURES, // Queen promotions and captures with SEE values < 0
PH_EVASIONS, // Check evasions
PH_QCAPTURES, // Captures in quiescence search
PH_QCHECKS, // Non-capture checks in quiescence search
PH_STOP
};
CACHE_LINE_ALIGNMENT
const uint8_t MainSearchTable[] = { PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP };
const uint8_t EvasionTable[] = { PH_TT_MOVES, PH_EVASIONS, PH_STOP };
const uint8_t QsearchWithChecksTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP };
const uint8_t QsearchWithoutChecksTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_STOP };
}
/// Constructor for the MovePicker class. Apart from the position for which
/// it is asked to pick legal moves, MovePicker also wants some information
/// to help it to return the presumably good moves first, to decide which
/// moves to return (in the quiescence search, for instance, we only want to
/// search captures, promotions and some checks) and about how important good
/// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
SearchStack* ss, Value beta) : pos(p), H(h) {
int searchTT = ttm;
ttMoves[0].move = ttm;
badCaptureThreshold = 0;
badCaptures = moves + MAX_MOVES;
assert(d > DEPTH_ZERO);
pinned = p.pinned_pieces(pos.side_to_move());
if (p.in_check())
{
ttMoves[1].move = killers[0].move = killers[1].move = MOVE_NONE;
phasePtr = EvasionTable;
}
else
{
ttMoves[1].move = (ss->mateKiller == ttm) ? MOVE_NONE : ss->mateKiller;
searchTT |= ttMoves[1].move;
killers[0].move = ss->killers[0];
killers[1].move = ss->killers[1];
// 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 = MainSearchTable;
}
phasePtr += int(!searchTT) - 1;
go_next_phase();
}
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h)
: pos(p), H(h) {
int searchTT = ttm;
ttMoves[0].move = ttm;
ttMoves[1].move = MOVE_NONE;
assert(d <= DEPTH_ZERO);
pinned = p.pinned_pieces(pos.side_to_move());
if (p.in_check())
phasePtr = EvasionTable;
else if (d >= DEPTH_QS_CHECKS)
phasePtr = QsearchWithChecksTable;
else
{
phasePtr = QsearchWithoutChecksTable;
// Skip TT move if is not a capture or a promotion, this avoids
// 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();
}
/// MovePicker::go_next_phase() generates, scores and sorts the next bunch
/// of moves when there are no more moves to try for the current phase.
void MovePicker::go_next_phase() {
curMove = moves;
phase = *(++phasePtr);
switch (phase) {
case PH_TT_MOVES:
curMove = ttMoves;
lastMove = curMove + 2;
return;
case PH_GOOD_CAPTURES:
lastMove = generate<MV_CAPTURE>(pos, moves);
score_captures();
return;
case PH_KILLERS:
curMove = killers;
lastMove = curMove + 2;
return;
case PH_NONCAPTURES:
lastMove = generate<MV_NON_CAPTURE>(pos, moves);
score_noncaptures();
sort_moves(moves, lastMove, &lastGoodNonCapture);
return;
case PH_BAD_CAPTURES:
// Bad captures SEE value is already calculated so just pick
// them in order to get SEE move ordering.
curMove = badCaptures;
lastMove = moves + MAX_MOVES;
return;
case PH_EVASIONS:
assert(pos.in_check());
lastMove = generate<MV_EVASION>(pos, moves);
score_evasions();
return;
case PH_QCAPTURES:
lastMove = generate<MV_CAPTURE>(pos, moves);
score_captures();
return;
case PH_QCHECKS:
lastMove = generate<MV_NON_CAPTURE_CHECK>(pos, moves);
return;
case PH_STOP:
lastMove = curMove + 1; // Avoid another go_next_phase() call
return;
default:
assert(false);
return;
}
}
/// MovePicker::score_captures(), MovePicker::score_noncaptures() and
/// MovePicker::score_evasions() assign a numerical move ordering score
/// to each move in a move list. The moves with highest scores will be
/// picked first by get_next_move().
void MovePicker::score_captures() {
// Winning and equal captures in the main search are ordered by MVV/LVA.
// Suprisingly, this appears to perform slightly better than SEE based
// move ordering. The reason is probably that in a position with a winning
// capture, capturing a more valuable (but sufficiently defended) piece
// first usually doesn't hurt. The opponent will have to recapture, and
// the hanging piece will still be hanging (except in the unusual cases
// where it is possible to recapture with the hanging piece). Exchanging
// big pieces before capturing a hanging piece probably helps to reduce
// the subtree size.
// In main search we want to push captures with negative SEE values to
// badCaptures[] array, but instead of doing it now we delay till when
// the move has been picked up in pick_move_from_list(), this way we save
// some SEE calls in case we get a cutoff (idea from Pablo Vazquez).
Move m;
// Use MVV/LVA ordering
for (MoveStack* cur = moves; cur != lastMove; cur++)
{
m = cur->move;
if (move_is_promotion(m))
cur->score = QueenValueMidgame;
else
cur->score = pos.midgame_value_of_piece_on(move_to(m))
- pos.type_of_piece_on(move_from(m));
}
}
void MovePicker::score_noncaptures() {
Move m;
Square from;
for (MoveStack* cur = moves; cur != lastMove; cur++)
{
m = cur->move;
from = move_from(m);
cur->score = H.value(pos.piece_on(from), move_to(m));
}
}
void MovePicker::score_evasions() {
// Try good captures ordered by MVV/LVA, then non-captures if
// destination square is not under attack, ordered by history
// value, and at the end bad-captures and non-captures with a
// negative SEE. This last group is ordered by the SEE score.
Move m;
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++)
{
m = cur->move;
if ((seeScore = pos.see_sign(m)) < 0)
cur->score = seeScore - History::MaxValue; // Be sure we are at the bottom
else if (pos.move_is_capture(m))
cur->score = pos.midgame_value_of_piece_on(move_to(m))
- pos.type_of_piece_on(move_from(m)) + History::MaxValue;
else
cur->score = H.value(pos.piece_on(move_from(m)), move_to(m));
}
}
/// MovePicker::get_next_move() is the most important method of the MovePicker
/// class. It returns a new legal move every time it is called, until there
/// are no more moves left. 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. 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 move;
while (true)
{
while (curMove == lastMove)
go_next_phase();
switch (phase) {
case PH_TT_MOVES:
move = (curMove++)->move;
if ( move != MOVE_NONE
&& pos.move_is_legal(move, pinned))
return move;
break;
case PH_GOOD_CAPTURES:
move = pick_best(curMove++, lastMove).move;
if ( move != ttMoves[0].move
&& move != ttMoves[1].move
&& pos.pl_move_is_legal(move, pinned))
{
// Check for a non negative SEE now
int seeValue = pos.see_sign(move);
if (seeValue >= badCaptureThreshold)
return move;
// Losing capture, move it to the tail of the array, note
// that move has now been already checked for legality.
(--badCaptures)->move = move;
badCaptures->score = seeValue;
}
break;
case PH_KILLERS:
move = (curMove++)->move;
if ( move != MOVE_NONE
&& pos.move_is_legal(move, pinned)
&& move != ttMoves[0].move
&& move != ttMoves[1].move
&& !pos.move_is_capture(move))
return move;
break;
case PH_NONCAPTURES:
// Sort negative scored moves only when we get there
if (curMove == lastGoodNonCapture)
insertion_sort<MoveStack>(lastGoodNonCapture, lastMove);
move = (curMove++)->move;
if ( move != ttMoves[0].move
&& move != ttMoves[1].move
&& move != killers[0].move
&& move != killers[1].move
&& pos.pl_move_is_legal(move, pinned))
return move;
break;
case PH_BAD_CAPTURES:
move = pick_best(curMove++, lastMove).move;
return move;
case PH_EVASIONS:
case PH_QCAPTURES:
move = pick_best(curMove++, lastMove).move;
if ( move != ttMoves[0].move
&& pos.pl_move_is_legal(move, pinned))
return move;
break;
case PH_QCHECKS:
move = (curMove++)->move;
if ( move != ttMoves[0].move
&& pos.pl_move_is_legal(move, pinned))
return move;
break;
case PH_STOP:
return MOVE_NONE;
default:
assert(false);
break;
}
}
}

View File

@ -0,0 +1,63 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined MOVEPICK_H_INCLUDED
#define MOVEPICK_H_INCLUDED
#include "history.h"
#include "move.h"
#include "position.h"
#include "types.h"
struct SearchStack;
/// MovePicker is a class which is used to pick one legal move at a time from
/// the current position. It is initialized with a Position object and a few
/// moves we have reason to believe are good. The most important method is
/// MovePicker::get_next_move(), which returns a new legal move each time it
/// is called, until there are no legal moves left, when MOVE_NONE is returned.
/// In order to improve the efficiency of the alpha beta algorithm, MovePicker
/// attempts to return the moves which are most likely to get a cut-off first.
class MovePicker {
MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC
public:
MovePicker(const Position&, Move, Depth, const History&, SearchStack*, Value);
MovePicker(const Position&, Move, Depth, const History&);
Move get_next_move();
private:
void score_captures();
void score_noncaptures();
void score_evasions();
void go_next_phase();
const Position& pos;
const History& H;
Bitboard pinned;
MoveStack ttMoves[2], killers[2];
int badCaptureThreshold, phase;
const uint8_t* phasePtr;
MoveStack *curMove, *lastMove, *lastGoodNonCapture, *badCaptures;
MoveStack moves[MAX_MOVES];
};
#endif // !defined(MOVEPICK_H_INCLUDED)

View File

@ -0,0 +1,243 @@
/*
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/>.
*/
#include <cassert>
#include "bitboard.h"
#include "bitcount.h"
#include "pawns.h"
#include "position.h"
namespace {
#define S(mg, eg) make_score(mg, eg)
// Doubled pawn penalty by opposed flag and file
const Score DoubledPawnPenalty[2][8] = {
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) },
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }};
// Isolated pawn penalty by opposed flag and file
const Score IsolatedPawnPenalty[2][8] = {
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52),
S(60, 52), S(60, 52), S(54, 52), S(37, 45) },
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35),
S(40, 35), S(40, 35), S(36, 35), S(25, 30) }};
// Backward pawn penalty by opposed flag and file
const Score BackwardPawnPenalty[2][8] = {
{ S(30, 42), S(43, 46), S(49, 46), S(49, 46),
S(49, 46), S(49, 46), S(43, 46), S(30, 42) },
{ S(20, 28), S(29, 31), S(33, 31), S(33, 31),
S(33, 31), S(33, 31), S(29, 31), S(20, 28) }};
// Pawn chain membership bonus by file
const Score ChainBonus[8] = {
S(11,-1), S(13,-1), S(13,-1), S(14,-1),
S(14,-1), S(13,-1), S(13,-1), S(11,-1)
};
// Candidate passed pawn bonus by rank
const Score CandidateBonus[8] = {
S( 0, 0), S( 6, 13), S(6,13), S(14,29),
S(34,68), S(83,166), S(0, 0), S( 0, 0)
};
const Score PawnStructureWeight = S(233, 201);
#undef S
inline Score apply_weight(Score v, Score w) {
return make_score((int(mg_value(v)) * mg_value(w)) / 0x100,
(int(eg_value(v)) * eg_value(w)) / 0x100);
}
}
/// 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 an 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 {
assert(pos.is_ok());
Key key = pos.get_pawn_key();
PawnInfo* pi = probe(key);
// If pi->key matches the position's pawn hash key, it means that we
// have analysed this pawn structure before, and we can simply return
// the information we found the last time instead of recomputing it.
if (pi->key == key)
return pi;
// Initialize PawnInfo entry
pi->key = key;
pi->passedPawns[WHITE] = pi->passedPawns[BLACK] = 0;
pi->kingSquares[WHITE] = pi->kingSquares[BLACK] = SQ_NONE;
pi->halfOpenFiles[WHITE] = pi->halfOpenFiles[BLACK] = 0xFF;
// Calculate pawn attacks
Bitboard wPawns = pos.pieces(PAWN, WHITE);
Bitboard bPawns = pos.pieces(PAWN, BLACK);
pi->pawnAttacks[WHITE] = ((wPawns << 9) & ~FileABB) | ((wPawns << 7) & ~FileHBB);
pi->pawnAttacks[BLACK] = ((bPawns >> 7) & ~FileABB) | ((bPawns >> 9) & ~FileHBB);
// Evaluate pawns for both colors and weight the result
pi->value = evaluate_pawns<WHITE>(pos, wPawns, bPawns, pi)
- evaluate_pawns<BLACK>(pos, bPawns, wPawns, pi);
pi->value = apply_weight(pi->value, PawnStructureWeight);
return pi;
}
/// PawnInfoTable::evaluate_pawns() evaluates each pawn of the given color
template<Color Us>
Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnInfo* pi) {
const BitCountType Max15 = CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b;
Square s;
File f;
Rank r;
bool passed, isolated, doubled, opposed, chain, backward, candidate;
Score value = SCORE_ZERO;
const Square* ptr = pos.piece_list_begin(Us, PAWN);
// Loop through all pawns of the current color and score each pawn
while ((s = *ptr++) != SQ_NONE)
{
assert(pos.piece_on(s) == make_piece(Us, PAWN));
f = square_file(s);
r = square_rank(s);
// This file cannot be half open
pi->halfOpenFiles[Us] &= ~(1 << f);
// Our rank plus previous one. Used for chain detection
b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1));
// Flag the pawn as passed, isolated, doubled or member of a pawn
// chain (but not the backward one).
passed = !(theirPawns & passed_pawn_mask(Us, s));
doubled = ourPawns & squares_in_front_of(Us, s);
opposed = theirPawns & squares_in_front_of(Us, s);
isolated = !(ourPawns & neighboring_files_bb(f));
chain = ourPawns & neighboring_files_bb(f) & b;
// Test for backward pawn
backward = false;
// If the pawn is passed, isolated, or member of a pawn chain it cannot
// be backward. If there are friendly pawns behind on neighboring files
// or if can capture an enemy pawn it cannot be backward either.
if ( !(passed | isolated | chain)
&& !(ourPawns & attack_span_mask(Them, s))
&& !(pos.attacks_from<PAWN>(s, Us) & theirPawns))
{
// We now know that there are no friendly pawns beside or behind this
// pawn on neighboring files. We now check whether the pawn is
// backward by looking in the forward direction on the neighboring
// files, and seeing whether we meet a friendly or an enemy pawn first.
b = pos.attacks_from<PAWN>(s, Us);
// Note that we are sure to find something because pawn is not passed
// nor isolated, so loop is potentially infinite, but it isn't.
while (!(b & (ourPawns | theirPawns)))
Us == WHITE ? b <<= 8 : b >>= 8;
// The friendly pawn needs to be at least two ranks closer than the
// enemy pawn in order to help the potentially backward pawn advance.
backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns;
}
assert(opposed | passed | (attack_span_mask(Us, s) & theirPawns));
// A not passed pawn is a candidate to become passed if it is free to
// advance and if the number of friendly pawns beside or behind this
// pawn on neighboring files is higher or equal than the number of
// enemy pawns in the forward direction on the neighboring files.
candidate = !(opposed | passed | backward | isolated)
&& (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != EmptyBoardBB
&& count_1s<Max15>(b) >= count_1s<Max15>(attack_span_mask(Us, s) & theirPawns);
// Passed pawns will be properly scored in evaluation because we need
// full attack info to evaluate passed pawns. Only the frontmost passed
// pawn on each file is considered a true passed pawn.
if (passed && !doubled)
set_bit(&(pi->passedPawns[Us]), s);
// Score this pawn
if (isolated)
value -= IsolatedPawnPenalty[opposed][f];
if (doubled)
value -= DoubledPawnPenalty[opposed][f];
if (backward)
value -= BackwardPawnPenalty[opposed][f];
if (chain)
value += ChainBonus[f];
if (candidate)
value += CandidateBonus[relative_rank(Us, s)];
}
return value;
}
/// PawnInfo::updateShelter() calculates and caches king shelter. It is called
/// only when king square changes, about 20% of total king_shelter() calls.
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 = ksq & (7 << 3);
for (int i = 0; i < 3; i++)
{
r += Shift;
shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (64 >> i);
}
}
kingSquares[Us] = ksq;
kingShelters[Us] = make_score(shelter, 0);
return kingShelters[Us];
}
// Explicit template instantiation
template Score PawnInfo::updateShelter<WHITE>(const Position& pos, Square ksq);
template Score PawnInfo::updateShelter<BLACK>(const Position& pos, Square ksq);

View File

@ -0,0 +1,107 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(PAWNS_H_INCLUDED)
#define PAWNS_H_INCLUDED
#include "position.h"
#include "tt.h"
#include "types.h"
const int PawnTableSize = 16384;
/// PawnInfo is a class which contains various information about a pawn
/// structure. Currently, it only includes a middle game and an end game
/// 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
/// (performed by calling the get_pawn_info method in a PawnInfoTable object)
/// returns a pointer to a PawnInfo object.
class PawnInfo {
friend class PawnInfoTable;
public:
Score pawns_value() const;
Bitboard pawn_attacks(Color c) const;
Bitboard passed_pawns(Color c) 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_right(Color c, File f) const;
template<Color Us>
Score king_shelter(const Position& pos, Square ksq);
private:
template<Color Us>
Score updateShelter(const Position& pos, Square ksq);
Key key;
Bitboard passedPawns[2];
Bitboard pawnAttacks[2];
Square kingSquares[2];
Score value;
int halfOpenFiles[2];
Score kingShelters[2];
};
/// The PawnInfoTable class represents a pawn hash table. The most important
/// method is get_pawn_info, which returns a pointer to a PawnInfo object.
class PawnInfoTable : public SimpleHash<PawnInfo, PawnTableSize> {
public:
PawnInfo* get_pawn_info(const Position& pos) const;
private:
template<Color Us>
static Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Bitboard theirPawns, PawnInfo* pi);
};
inline Score PawnInfo::pawns_value() const {
return value;
}
inline Bitboard PawnInfo::pawn_attacks(Color c) const {
return pawnAttacks[c];
}
inline Bitboard PawnInfo::passed_pawns(Color c) const {
return passedPawns[c];
}
inline int PawnInfo::file_is_half_open(Color c, File f) const {
return halfOpenFiles[c] & (1 << int(f));
}
inline int PawnInfo::has_open_file_to_left(Color c, File f) const {
return halfOpenFiles[c] & ((1 << int(f)) - 1);
}
inline int PawnInfo::has_open_file_to_right(Color c, File f) const {
return halfOpenFiles[c] & ~((1 << int(f+1)) - 1);
}
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)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,554 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(POSITION_H_INCLUDED)
#define POSITION_H_INCLUDED
#include "bitboard.h"
#include "move.h"
#include "types.h"
/// Maximum number of plies per game (220 should be enough, because the
/// maximum search depth is 100, and during position setup we reset the
/// move counter for every non-reversible move).
const int MaxGameLength = 220;
class Position;
/// struct checkInfo is initialized at c'tor time and keeps
/// info used to detect if a move gives check.
struct CheckInfo {
explicit CheckInfo(const Position&);
Bitboard dcCandidates;
Bitboard checkSq[8];
Square ksq;
};
/// Castle rights, encoded as bit fields
enum CastleRights {
CASTLES_NONE = 0,
WHITE_OO = 1,
BLACK_OO = 2,
WHITE_OOO = 4,
BLACK_OOO = 8,
ALL_CASTLES = 15
};
/// Game phase
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128
};
/// The StateInfo struct stores information we need to restore a Position
/// object to its previous state when we retract a move. Whenever a move
/// is made on the board (by calling Position::do_move), an StateInfo object
/// must be passed as a parameter.
struct StateInfo {
Key pawnKey, materialKey;
int castleRights, rule50, gamePly, pliesFromNull;
Square epSquare;
Score value;
Value npMaterial[2];
PieceType capturedType;
Key key;
Bitboard checkersBB;
StateInfo* previous;
};
/// The position data structure. A position consists of the following data:
///
/// * For each piece type, a bitboard representing the squares occupied
/// by pieces of that type.
/// * For each color, a bitboard representing the squares occupied by
/// pieces of that color.
/// * A bitboard of all occupied squares.
/// * A bitboard of all checking pieces.
/// * A 64-entry array of pieces, indexed by the squares of the board.
/// * The current side to move.
/// * Information about the castling rights for both sides.
/// * The initial files of the kings and both pairs of rooks. This is
/// used to implement the Chess960 castling rules.
/// * The en passant square (which is SQ_NONE if no en passant capture is
/// possible).
/// * The squares of the kings for both sides.
/// * Hash keys for the position itself, the current pawn structure, and
/// the current material situation.
/// * Hash keys for all previous positions in the game for detecting
/// repetition draws.
/// * A counter for detecting 50 move rule draws.
class Position {
Position(); // No default or copy c'tor allowed
Position(const Position& pos);
public:
enum GamePhase {
MidGame,
EndGame
};
// Constructors
Position(const Position& pos, int threadID);
Position(const std::string& fen, bool isChess960, int threadID);
// Text input/output
void from_fen(const std::string& fen, bool isChess960);
const std::string to_fen() const;
void print(Move m = MOVE_NONE) const;
// Copying
void flip();
// The piece on a given square
Piece piece_on(Square s) const;
PieceType type_of_piece_on(Square s) const;
Color color_of_piece_on(Square s) const;
bool square_is_empty(Square s) const;
bool square_is_occupied(Square s) const;
Value midgame_value_of_piece_on(Square s) const;
Value endgame_value_of_piece_on(Square s) const;
// Side to move
Color side_to_move() const;
// Bitboard representation of the position
Bitboard empty_squares() const;
Bitboard occupied_squares() const;
Bitboard pieces_of_color(Color c) const;
Bitboard pieces(PieceType pt) const;
Bitboard pieces(PieceType pt, Color c) const;
Bitboard pieces(PieceType pt1, PieceType pt2) const;
Bitboard pieces(PieceType pt1, PieceType pt2, Color c) const;
// Number of pieces of each color and type
int piece_count(Color c, PieceType pt) const;
// The en passant square
Square ep_square() const;
// Current king position for each color
Square king_square(Color c) const;
// Castling rights
bool can_castle_kingside(Color c) const;
bool can_castle_queenside(Color c) const;
bool can_castle(Color c) const;
Square initial_kr_square(Color c) const;
Square initial_qr_square(Color c) const;
// Bitboards for pinned pieces and discovered check candidates
Bitboard discovered_check_candidates(Color c) const;
Bitboard pinned_pieces(Color c) const;
// Checking pieces and under check information
Bitboard checkers() const;
bool in_check() const;
// Piece lists
Square piece_list(Color c, PieceType pt, int index) const;
const Square* piece_list_begin(Color c, PieceType pt) const;
// Information about attacks to or from a given square
Bitboard attackers_to(Square s) const;
Bitboard attacks_from(Piece p, Square s) const;
static Bitboard attacks_from(Piece p, Square s, Bitboard occ);
template<PieceType> Bitboard attacks_from(Square s) const;
template<PieceType> Bitboard attacks_from(Square s, Color c) const;
// Properties of moves
bool pl_move_is_legal(Move m, Bitboard pinned) const;
bool pl_move_is_evasion(Move m, Bitboard pinned) const;
bool move_is_legal(const Move m) const;
bool move_is_legal(const Move m, Bitboard pinned) const;
bool move_gives_check(Move m) const;
bool move_gives_check(Move m, const CheckInfo& ci) const;
bool move_is_capture(Move m) const;
bool move_is_capture_or_promotion(Move m) const;
bool move_is_passed_pawn_push(Move m) const;
bool move_attacks_square(Move m, Square s) const;
// Piece captured with previous moves
PieceType captured_piece_type() const;
// Information about pawns
bool pawn_is_passed(Color c, Square s) const;
// Weak squares
bool square_is_weak(Square s, Color c) const;
// Doing and undoing moves
void do_setup_move(Move m);
void do_move(Move m, StateInfo& st);
void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck);
void undo_move(Move m);
void do_null_move(StateInfo& st);
void undo_null_move();
// Static exchange evaluation
int see(Square from, Square to) const;
int see(Move m) const;
int see_sign(Move m) const;
// Accessing hash keys
Key get_key() const;
Key get_exclusion_key() const;
Key get_pawn_key() const;
Key get_material_key() const;
// Incremental evaluation
Score value() const;
Value non_pawn_material(Color c) const;
static Score pst_delta(Piece piece, Square from, Square to);
// Game termination checks
bool is_mate() const;
bool is_draw() const;
// Number of plies from starting position
int startpos_ply_counter() const;
// Other properties of the position
bool opposite_colored_bishops() const;
bool has_pawn_on_7th(Color c) const;
bool is_chess960() const;
// Current thread ID searching on the position
int thread() const;
int64_t nodes_searched() const;
void set_nodes_searched(int64_t n);
// Position consistency check, for debugging
bool is_ok(int* failedStep = NULL) const;
// Static member functions
static void init_zobrist();
static void init_piece_square_tables();
private:
// Initialization helper functions (used while setting up a position)
void clear();
void detach();
void put_piece(Piece p, Square s);
void do_allow_oo(Color c);
void do_allow_ooo(Color c);
bool set_castling_rights(char token);
// Helper functions for doing and undoing moves
void do_capture_move(Key& key, PieceType capture, Color them, Square to, bool ep);
void do_castle_move(Move m);
void undo_castle_move(Move m);
void find_checkers();
template<bool FindPinned>
Bitboard hidden_checkers(Color c) const;
// Computing hash keys from scratch (for initialization and debugging)
Key compute_key() const;
Key compute_pawn_key() const;
Key compute_material_key() const;
// Computing incremental evaluation scores and material counts
static Score pst(Color c, PieceType pt, Square s);
Score compute_value() const;
Value compute_non_pawn_material(Color c) const;
// Board
Piece board[64];
// Bitboards
Bitboard byTypeBB[8], byColorBB[2];
// Piece counts
int pieceCount[2][8]; // [color][pieceType]
// Piece lists
Square pieceList[2][8][16]; // [color][pieceType][index]
int index[64]; // [square]
// Other info
Color sideToMove;
Key history[MaxGameLength];
int castleRightsMask[64];
StateInfo startState;
File initialKFile, initialKRFile, initialQRFile;
bool chess960;
int startPosPlyCounter;
int threadID;
int64_t nodes;
StateInfo* st;
// Static variables
static Key zobrist[2][8][64];
static Key zobEp[64];
static Key zobCastle[16];
static Key zobSideToMove;
static Score PieceSquareTable[16][64];
static Key zobExclusion;
static const Value seeValues[8];
static const Value PieceValueMidgame[17];
static const Value PieceValueEndgame[17];
};
inline int64_t Position::nodes_searched() const {
return nodes;
}
inline void Position::set_nodes_searched(int64_t n) {
nodes = n;
}
inline Piece Position::piece_on(Square s) const {
return board[s];
}
inline Color Position::color_of_piece_on(Square s) const {
return color_of_piece(piece_on(s));
}
inline PieceType Position::type_of_piece_on(Square s) const {
return type_of_piece(piece_on(s));
}
inline bool Position::square_is_empty(Square s) const {
return piece_on(s) == PIECE_NONE;
}
inline bool Position::square_is_occupied(Square s) const {
return !square_is_empty(s);
}
inline Value Position::midgame_value_of_piece_on(Square s) const {
return PieceValueMidgame[piece_on(s)];
}
inline Value Position::endgame_value_of_piece_on(Square s) const {
return PieceValueEndgame[piece_on(s)];
}
inline Color Position::side_to_move() const {
return sideToMove;
}
inline Bitboard Position::occupied_squares() const {
return byTypeBB[0];
}
inline Bitboard Position::empty_squares() const {
return ~occupied_squares();
}
inline Bitboard Position::pieces_of_color(Color c) const {
return byColorBB[c];
}
inline Bitboard Position::pieces(PieceType pt) const {
return byTypeBB[pt];
}
inline Bitboard Position::pieces(PieceType pt, Color c) const {
return byTypeBB[pt] & byColorBB[c];
}
inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const {
return byTypeBB[pt1] | byTypeBB[pt2];
}
inline Bitboard Position::pieces(PieceType pt1, PieceType pt2, Color c) const {
return (byTypeBB[pt1] | byTypeBB[pt2]) & byColorBB[c];
}
inline int Position::piece_count(Color c, PieceType pt) const {
return pieceCount[c][pt];
}
inline Square Position::piece_list(Color c, PieceType pt, int idx) const {
return pieceList[c][pt][idx];
}
inline const Square* Position::piece_list_begin(Color c, PieceType pt) const {
return pieceList[c][pt];
}
inline Square Position::ep_square() const {
return st->epSquare;
}
inline Square Position::king_square(Color c) const {
return pieceList[c][KING][0];
}
inline bool Position::can_castle_kingside(Color side) const {
return st->castleRights & (1+int(side));
}
inline bool Position::can_castle_queenside(Color side) const {
return st->castleRights & (4+4*int(side));
}
inline bool Position::can_castle(Color side) const {
return can_castle_kingside(side) || can_castle_queenside(side);
}
inline Square Position::initial_kr_square(Color c) const {
return relative_square(c, make_square(initialKRFile, RANK_1));
}
inline Square Position::initial_qr_square(Color c) const {
return relative_square(c, make_square(initialQRFile, RANK_1));
}
template<>
inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const {
return StepAttacksBB[make_piece(c, PAWN)][s];
}
template<PieceType Piece> // Knight and King and white pawns
inline Bitboard Position::attacks_from(Square s) const {
return StepAttacksBB[Piece][s];
}
template<>
inline Bitboard Position::attacks_from<BISHOP>(Square s) const {
return bishop_attacks_bb(s, occupied_squares());
}
template<>
inline Bitboard Position::attacks_from<ROOK>(Square s) const {
return rook_attacks_bb(s, occupied_squares());
}
template<>
inline Bitboard Position::attacks_from<QUEEN>(Square s) const {
return attacks_from<ROOK>(s) | attacks_from<BISHOP>(s);
}
inline Bitboard Position::checkers() const {
return st->checkersBB;
}
inline bool Position::in_check() const {
return st->checkersBB != EmptyBoardBB;
}
inline bool Position::pawn_is_passed(Color c, Square s) const {
return !(pieces(PAWN, opposite_color(c)) & passed_pawn_mask(c, s));
}
inline bool Position::square_is_weak(Square s, Color c) const {
return !(pieces(PAWN, opposite_color(c)) & attack_span_mask(c, s));
}
inline Key Position::get_key() const {
return st->key;
}
inline Key Position::get_exclusion_key() const {
return st->key ^ zobExclusion;
}
inline Key Position::get_pawn_key() const {
return st->pawnKey;
}
inline Key Position::get_material_key() const {
return st->materialKey;
}
inline Score Position::pst(Color c, PieceType pt, Square s) {
return PieceSquareTable[make_piece(c, pt)][s];
}
inline Score Position::pst_delta(Piece piece, Square from, Square to) {
return PieceSquareTable[piece][to] - PieceSquareTable[piece][from];
}
inline Score Position::value() const {
return st->value;
}
inline Value Position::non_pawn_material(Color c) const {
return st->npMaterial[c];
}
inline bool Position::move_is_passed_pawn_push(Move m) const {
Color c = side_to_move();
return piece_on(move_from(m)) == make_piece(c, PAWN)
&& pawn_is_passed(c, move_to(m));
}
inline int Position::startpos_ply_counter() const {
return startPosPlyCounter;
}
inline bool Position::opposite_colored_bishops() const {
return piece_count(WHITE, BISHOP) == 1 && piece_count(BLACK, BISHOP) == 1
&& opposite_color_squares(piece_list(WHITE, BISHOP, 0), piece_list(BLACK, BISHOP, 0));
}
inline bool Position::has_pawn_on_7th(Color c) const {
return pieces(PAWN, c) & rank_bb(relative_rank(c, RANK_7));
}
inline bool Position::is_chess960() const {
return chess960;
}
inline bool Position::move_is_capture(Move m) const {
// Move must not be MOVE_NONE !
return (m & (3 << 15)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
}
inline bool Position::move_is_capture_or_promotion(Move m) const {
// Move must not be MOVE_NONE !
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)

View File

@ -0,0 +1,185 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(PSQTAB_H_INCLUDED)
#define PSQTAB_H_INCLUDED
#include "types.h"
namespace {
////
//// Constants modified by Joona Kiiski
////
const Value MP = PawnValueMidgame;
const Value MK = KnightValueMidgame;
const Value MB = BishopValueMidgame;
const Value MR = RookValueMidgame;
const Value MQ = QueenValueMidgame;
const Value EP = PawnValueEndgame;
const Value EK = KnightValueEndgame;
const Value EB = BishopValueEndgame;
const Value ER = RookValueEndgame;
const Value EQ = QueenValueEndgame;
const int MgPST[][64] = {
{ },
{// Pawn
// A B C D E F G H
0, 0, 0, 0, 0, 0, 0, 0,
MP-28, MP-6, MP+ 4, MP+14, MP+14, MP+ 4, MP-6, MP-28,
MP-28, MP-6, MP+ 9, MP+36, MP+36, MP+ 9, MP-6, MP-28,
MP-28, MP-6, MP+17, MP+58, MP+58, MP+17, MP-6, MP-28,
MP-28, MP-6, MP+17, MP+36, MP+36, MP+17, MP-6, MP-28,
MP-28, MP-6, MP+ 9, MP+14, MP+14, MP+ 9, MP-6, MP-28,
MP-28, MP-6, MP+ 4, MP+14, MP+14, MP+ 4, MP-6, MP-28,
0, 0, 0, 0, 0, 0, 0, 0
},
{// Knight
// A B C D E F G H
MK-135, MK-107, MK-80, MK-67, MK-67, MK-80, MK-107, MK-135,
MK- 93, MK- 67, MK-39, MK-25, MK-25, MK-39, MK- 67, MK- 93,
MK- 53, MK- 25, MK+ 1, MK+13, MK+13, MK+ 1, MK- 25, MK- 53,
MK- 25, MK+ 1, MK+27, MK+41, MK+41, MK+27, MK+ 1, MK- 25,
MK- 11, MK+ 13, MK+41, MK+55, MK+55, MK+41, MK+ 13, MK- 11,
MK- 11, MK+ 13, MK+41, MK+55, MK+55, MK+41, MK+ 13, MK- 11,
MK- 53, MK- 25, MK+ 1, MK+13, MK+13, MK+ 1, MK- 25, MK- 53,
MK-193, MK- 67, MK-39, MK-25, MK-25, MK-39, MK- 67, MK-193
},
{// Bishop
// A B C D E F G H
MB-40, MB-40, MB-35, MB-30, MB-30, MB-35, MB-40, MB-40,
MB-17, MB+ 0, MB- 4, MB+ 0, MB+ 0, MB- 4, MB+ 0, MB-17,
MB-13, MB- 4, MB+ 8, MB+ 4, MB+ 4, MB+ 8, MB- 4, MB-13,
MB- 8, MB+ 0, MB+ 4, MB+17, MB+17, MB+ 4, MB+ 0, MB- 8,
MB- 8, MB+ 0, MB+ 4, MB+17, MB+17, MB+ 4, MB+ 0, MB- 8,
MB-13, MB- 4, MB+ 8, MB+ 4, MB+ 4, MB+ 8, MB- 4, MB-13,
MB-17, MB+ 0, MB- 4, MB+ 0, MB+ 0, MB- 4, MB+ 0, MB-17,
MB-17, MB-17, MB-13, MB- 8, MB- 8, MB-13, MB-17, MB-17
},
{// Rook
// A B C D E F G H
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12
},
{// Queen
// A B C D E F G H
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8
},
{// King
//A B C D E F G H
287, 311, 262, 214, 214, 262, 311, 287,
262, 287, 238, 190, 190, 238, 287, 262,
214, 238, 190, 142, 142, 190, 238, 214,
190, 214, 167, 119, 119, 167, 214, 190,
167, 190, 142, 94, 94, 142, 190, 167,
142, 167, 119, 69, 69, 119, 167, 142,
119, 142, 94, 46, 46, 94, 142, 119,
94, 119, 69, 21, 21, 69, 119, 94
}
};
const int EgPST[][64] = {
{ },
{// Pawn
// A B C D E F G H
0, 0, 0, 0, 0, 0, 0, 0,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
0, 0, 0, 0, 0, 0, 0, 0
},
{// Knight
// A B C D E F G H
EK-104, EK-79, EK-55, EK-42, EK-42, EK-55, EK-79, EK-104,
EK- 79, EK-55, EK-30, EK-17, EK-17, EK-30, EK-55, EK- 79,
EK- 55, EK-30, EK- 6, EK+ 5, EK+ 5, EK- 6, EK-30, EK- 55,
EK- 42, EK-17, EK+ 5, EK+18, EK+18, EK+ 5, EK-17, EK- 42,
EK- 42, EK-17, EK+ 5, EK+18, EK+18, EK+ 5, EK-17, EK- 42,
EK- 55, EK-30, EK- 6, EK+ 5, EK+ 5, EK- 6, EK-30, EK- 55,
EK- 79, EK-55, EK-30, EK-17, EK-17, EK-30, EK-55, EK- 79,
EK-104, EK-79, EK-55, EK-42, EK-42, EK-55, EK-79, EK-104
},
{// Bishop
// A B C D E F G H
EB-59, EB-42, EB-35, EB-26, EB-26, EB-35, EB-42, EB-59,
EB-42, EB-26, EB-18, EB-11, EB-11, EB-18, EB-26, EB-42,
EB-35, EB-18, EB-11, EB- 4, EB- 4, EB-11, EB-18, EB-35,
EB-26, EB-11, EB- 4, EB+ 4, EB+ 4, EB- 4, EB-11, EB-26,
EB-26, EB-11, EB- 4, EB+ 4, EB+ 4, EB- 4, EB-11, EB-26,
EB-35, EB-18, EB-11, EB- 4, EB- 4, EB-11, EB-18, EB-35,
EB-42, EB-26, EB-18, EB-11, EB-11, EB-18, EB-26, EB-42,
EB-59, EB-42, EB-35, EB-26, EB-26, EB-35, EB-42, EB-59
},
{// Rook
// A B C D E F G H
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3
},
{// Queen
// A B C D E F G H
EQ-80, EQ-54, EQ-42, EQ-30, EQ-30, EQ-42, EQ-54, EQ-80,
EQ-54, EQ-30, EQ-18, EQ- 6, EQ- 6, EQ-18, EQ-30, EQ-54,
EQ-42, EQ-18, EQ- 6, EQ+ 6, EQ+ 6, EQ- 6, EQ-18, EQ-42,
EQ-30, EQ- 6, EQ+ 6, EQ+18, EQ+18, EQ+ 6, EQ- 6, EQ-30,
EQ-30, EQ- 6, EQ+ 6, EQ+18, EQ+18, EQ+ 6, EQ- 6, EQ-30,
EQ-42, EQ-18, EQ- 6, EQ+ 6, EQ+ 6, EQ- 6, EQ-18, EQ-42,
EQ-54, EQ-30, EQ-18, EQ- 6, EQ- 6, EQ-18, EQ-30, EQ-54,
EQ-80, EQ-54, EQ-42, EQ-30, EQ-30, EQ-42, EQ-54, EQ-80
},
{// King
//A B C D E F G H
18, 77, 105, 135, 135, 105, 77, 18,
77, 135, 165, 193, 193, 165, 135, 77,
105, 165, 193, 222, 222, 193, 165, 105,
135, 193, 222, 251, 251, 222, 193, 135,
135, 193, 222, 251, 251, 222, 193, 135,
105, 165, 193, 222, 222, 193, 165, 105,
77, 135, 165, 193, 193, 165, 135, 77,
18, 77, 105, 135, 135, 105, 77, 18
}
};
} // namespace
#endif // !defined(PSQTAB_H_INCLUDED)

View File

@ -0,0 +1,77 @@
/*
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.
** George Marsaglia invented the RNG-Kiss-family in the early 90's.
** This is a specific version that Heinz van Saanen derived and
** tested from some public domain code by Bob Jenkins:
**
** 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
*/
#if !defined(RKISS_H_INCLUDED)
#define RKISS_H_INCLUDED
#include "types.h"
class RKISS {
// Keep variables always together
struct S { uint64_t a, b, c, d; } s;
uint64_t rotate(uint64_t x, uint64_t k) const {
return (x << k) | (x >> (64 - k));
}
// Return 64 bit unsigned integer in between [0, 2^64 - 1]
uint64_t rand64() {
const uint64_t
e = s.a - rotate(s.b, 7);
s.a = s.b ^ rotate(s.c, 13);
s.b = s.c + rotate(s.d, 37);
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)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,73 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(SEARCH_H_INCLUDED)
#define SEARCH_H_INCLUDED
#include <cstring>
#include "move.h"
#include "types.h"
class Position;
struct SplitPoint;
/// The SearchStack struct keeps track of the information we need to remember
/// from nodes shallower and deeper in the tree during the search. Each
/// search thread has its own array of SearchStack objects, indexed by the
/// current ply.
struct SearchStack {
int ply;
Move currentMove;
Move mateKiller;
Move excludedMove;
Move bestMove;
Move killers[2];
Depth reduction;
Value eval;
Value evalMargin;
bool skipNullMove;
SplitPoint* sp;
};
/// The SearchLimits struct stores information sent by GUI about available time
/// to search the current move, maximum depth/time, if we are in analysis mode
/// or if we have to ponder while is our opponent's side to move.
struct SearchLimits {
SearchLimits() { memset(this, 0, sizeof(SearchLimits)); }
SearchLimits(int t, int i, int mtg, int mt, int md, int mn, bool inf, bool pon)
: time(t), increment(i), movesToGo(mtg), maxTime(mt), maxDepth(md),
maxNodes(mn), infinite(inf), ponder(pon) {}
bool useTimeManagement() const { return !(maxTime | maxDepth | maxNodes | int(infinite)); }
int time, increment, movesToGo, maxTime, maxDepth, maxNodes;
bool infinite, ponder;
};
extern void init_search();
extern int64_t perft(Position& pos, Depth depth);
extern bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]);
#endif // !defined(SEARCH_H_INCLUDED)

View File

@ -0,0 +1,345 @@
/*
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/>.
*/
#include <iostream>
#include "thread.h"
#include "ucioption.h"
ThreadsManager Threads; // Global object definition
namespace { extern "C" {
// start_routine() is the C function which is called when a new thread
// is launched. It simply calls idle_loop() with the supplied threadID.
// There are two versions of this function; one for POSIX threads and
// one for Windows threads.
#if defined(_MSC_VER)
DWORD WINAPI start_routine(LPVOID threadID) {
Threads.idle_loop(*(int*)threadID, NULL);
return 0;
}
#else
void* start_routine(void* threadID) {
Threads.idle_loop(*(int*)threadID, NULL);
return NULL;
}
#endif
} }
// wake_up() wakes up the thread, normally at the beginning of the search or,
// if "sleeping threads" is used, when there is some work to do.
void Thread::wake_up() {
lock_grab(&sleepLock);
cond_signal(&sleepCond);
lock_release(&sleepLock);
}
// cutoff_occurred() checks whether a beta cutoff has occurred in
// the thread's currently active split point, or in some ancestor of
// the current split point.
bool Thread::cutoff_occurred() const {
for (SplitPoint* sp = splitPoint; sp; sp = sp->parent)
if (sp->is_betaCutoff)
return true;
return false;
}
// is_available_to() checks whether the thread is available to help the thread with
// threadID "master" at a split point. An obvious requirement is that thread must be
// idle. With more than two threads, this is not by itself sufficient: If the thread
// is the master of some active split point, it is only available as a slave to the
// threads which are busy searching the split point at the top of "slave"'s split
// point stack (the "helpful master concept" in YBWC terminology).
bool Thread::is_available_to(int master) const {
if (state != AVAILABLE)
return false;
// Make a local copy to be sure doesn't become zero under our feet while
// testing next condition and so leading to an out of bound access.
int localActiveSplitPoints = activeSplitPoints;
// No active split points means that the thread is available as a slave for any
// other thread otherwise apply the "helpful master" concept if possible.
if ( !localActiveSplitPoints
|| splitPoints[localActiveSplitPoints - 1].is_slave[master])
return true;
return false;
}
// read_uci_options() updates number of active threads and other internal
// parameters according to the UCI options values. It is called before
// to start a new search.
void ThreadsManager::read_uci_options() {
maxThreadsPerSplitPoint = Options["Maximum Number of Threads per Split Point"].value<int>();
minimumSplitDepth = Options["Minimum Split Depth"].value<int>() * ONE_PLY;
useSleepingThreads = Options["Use Sleeping Threads"].value<bool>();
activeThreads = Options["Threads"].value<int>();
}
// init() is called during startup. Initializes locks and condition variables
// and launches all threads sending them immediately to sleep.
void ThreadsManager::init() {
int threadID[MAX_THREADS];
// This flag is needed to properly end the threads when program exits
allThreadsShouldExit = false;
// Threads will sent to sleep as soon as created, only main thread is kept alive
activeThreads = 1;
threads[0].state = Thread::SEARCHING;
// Allocate pawn and material hash tables for main thread
init_hash_tables();
lock_init(&mpLock);
// Initialize thread and split point locks
for (int i = 0; i < MAX_THREADS; i++)
{
lock_init(&threads[i].sleepLock);
cond_init(&threads[i].sleepCond);
for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
lock_init(&(threads[i].splitPoints[j].lock));
}
// Create and startup all the threads but the main that is already running
for (int i = 1; i < MAX_THREADS; i++)
{
threads[i].state = Thread::INITIALIZING;
threadID[i] = i;
#if defined(_MSC_VER)
bool ok = (CreateThread(NULL, 0, start_routine, (LPVOID)&threadID[i], 0, NULL) != NULL);
#else
pthread_t pthreadID;
bool ok = (pthread_create(&pthreadID, NULL, start_routine, (void*)&threadID[i]) == 0);
pthread_detach(pthreadID);
#endif
if (!ok)
{
std::cout << "Failed to create thread number " << i << std::endl;
::exit(EXIT_FAILURE);
}
// Wait until the thread has finished launching and is gone to sleep
while (threads[i].state == Thread::INITIALIZING) {}
}
}
// exit() is called to cleanly exit the threads when the program finishes
void ThreadsManager::exit() {
// Force the woken up threads to exit idle_loop() and hence terminate
allThreadsShouldExit = true;
for (int i = 0; i < MAX_THREADS; i++)
{
// Wake up all the threads and waits for termination
if (i != 0)
{
threads[i].wake_up();
while (threads[i].state != Thread::TERMINATED) {}
}
// Now we can safely destroy the locks and wait conditions
lock_destroy(&threads[i].sleepLock);
cond_destroy(&threads[i].sleepCond);
for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
lock_destroy(&(threads[i].splitPoints[j].lock));
}
lock_destroy(&mpLock);
}
// init_hash_tables() dynamically allocates pawn and material hash tables
// according to the number of active threads. This avoids preallocating
// memory for all possible threads if only few are used as, for instance,
// on mobile devices where memory is scarce and allocating for MAX_THREADS
// threads could even result in a crash.
void ThreadsManager::init_hash_tables() {
for (int i = 0; i < activeThreads; i++)
{
threads[i].pawnTable.init();
threads[i].materialTable.init();
}
}
// available_slave_exists() tries to find an idle thread which is available as
// a slave for the thread with threadID "master".
bool ThreadsManager::available_slave_exists(int master) const {
assert(master >= 0 && master < activeThreads);
for (int i = 0; i < activeThreads; i++)
if (i != master && threads[i].is_available_to(master))
return true;
return false;
}
// split() does the actual work of distributing the work at a node between
// several available threads. If it does not succeed in splitting the
// node (because no idle threads are available, or because we have no unused
// split point objects), the function immediately returns. If splitting is
// possible, a SplitPoint object is initialized with all the data that must be
// copied to the helper threads and we tell our helper threads that they have
// been assigned work. This will cause them to instantly leave their idle loops and
// call search().When all threads have returned from search() then split() returns.
template <bool Fake>
void ThreadsManager::split(Position& pos, SearchStack* ss, Value* alpha, const Value beta,
Value* bestValue, Depth depth, Move threatMove,
int moveCount, MovePicker* mp, bool pvNode) {
assert(pos.is_ok());
assert(*bestValue >= -VALUE_INFINITE);
assert(*bestValue <= *alpha);
assert(*alpha < beta);
assert(beta <= VALUE_INFINITE);
assert(depth > DEPTH_ZERO);
assert(pos.thread() >= 0 && pos.thread() < activeThreads);
assert(activeThreads > 1);
int i, master = pos.thread();
Thread& masterThread = threads[master];
lock_grab(&mpLock);
// If no other thread is available to help us, or if we have too many
// active split points, don't split.
if ( !available_slave_exists(master)
|| masterThread.activeSplitPoints >= MAX_ACTIVE_SPLIT_POINTS)
{
lock_release(&mpLock);
return;
}
// Pick the next available split point object from the split point stack
SplitPoint& splitPoint = masterThread.splitPoints[masterThread.activeSplitPoints++];
// Initialize the split point object
splitPoint.parent = masterThread.splitPoint;
splitPoint.master = master;
splitPoint.is_betaCutoff = false;
splitPoint.depth = depth;
splitPoint.threatMove = threatMove;
splitPoint.alpha = *alpha;
splitPoint.beta = beta;
splitPoint.pvNode = pvNode;
splitPoint.bestValue = *bestValue;
splitPoint.mp = mp;
splitPoint.moveCount = moveCount;
splitPoint.pos = &pos;
splitPoint.nodes = 0;
splitPoint.ss = ss;
for (i = 0; i < activeThreads; i++)
splitPoint.is_slave[i] = false;
masterThread.splitPoint = &splitPoint;
// If we are here it means we are not available
assert(masterThread.state != Thread::AVAILABLE);
int workersCnt = 1; // At least the master is included
// Allocate available threads setting state to THREAD_BOOKED
for (i = 0; !Fake && i < activeThreads && workersCnt < maxThreadsPerSplitPoint; i++)
if (i != master && threads[i].is_available_to(master))
{
threads[i].state = Thread::BOOKED;
threads[i].splitPoint = &splitPoint;
splitPoint.is_slave[i] = true;
workersCnt++;
}
assert(Fake || workersCnt > 1);
// We can release the lock because slave threads are already booked and master is not available
lock_release(&mpLock);
// Tell the threads that they have work to do. This will make them leave
// their idle loop.
for (i = 0; i < activeThreads; i++)
if (i == master || splitPoint.is_slave[i])
{
assert(i == master || threads[i].state == Thread::BOOKED);
threads[i].state = Thread::WORKISWAITING; // This makes the slave to exit from idle_loop()
if (useSleepingThreads && i != master)
threads[i].wake_up();
}
// Everything is set up. The master thread enters the idle loop, from
// which it will instantly launch a search, because its state is
// THREAD_WORKISWAITING. We send the split point as a second parameter to the
// idle loop, which means that the main thread will return from the idle
// loop when all threads have finished their work at this split point.
idle_loop(master, &splitPoint);
// We have returned from the idle loop, which means that all threads are
// finished. Update alpha and bestValue, and return.
lock_grab(&mpLock);
*alpha = splitPoint.alpha;
*bestValue = splitPoint.bestValue;
masterThread.activeSplitPoints--;
masterThread.splitPoint = splitPoint.parent;
pos.set_nodes_searched(pos.nodes_searched() + splitPoint.nodes);
lock_release(&mpLock);
}
// Explicit template instantiations
template void ThreadsManager::split<false>(Position&, SearchStack*, Value*, const Value, Value*, Depth, Move, int, MovePicker*, bool);
template void ThreadsManager::split<true>(Position&, SearchStack*, Value*, const Value, Value*, Depth, Move, int, MovePicker*, bool);

View File

@ -0,0 +1,132 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(THREAD_H_INCLUDED)
#define THREAD_H_INCLUDED
#include <cstring>
#include "lock.h"
#include "material.h"
#include "movepick.h"
#include "pawns.h"
#include "position.h"
const int MAX_THREADS = 32;
const int MAX_ACTIVE_SPLIT_POINTS = 8;
struct SplitPoint {
// Const data after splitPoint has been setup
SplitPoint* parent;
const Position* pos;
Depth depth;
bool pvNode;
Value beta;
int ply;
int master;
Move threatMove;
// Const pointers to shared data
MovePicker* mp;
SearchStack* ss;
// Shared data
Lock lock;
volatile int64_t nodes;
volatile Value alpha;
volatile Value bestValue;
volatile int moveCount;
volatile bool is_betaCutoff;
volatile bool is_slave[MAX_THREADS];
};
/// Thread struct is used to keep together all the thread related stuff like locks,
/// state and especially split points. We also use per-thread pawn and material hash
/// tables so that once we get a pointer to an entry its life time is unlimited and
/// we don't have to care about someone changing the entry under our feet.
struct Thread {
enum ThreadState
{
INITIALIZING, // Thread is initializing itself
SEARCHING, // Thread is performing work
AVAILABLE, // Thread is waiting for work
BOOKED, // Other thread (master) has booked us as a slave
WORKISWAITING, // Master has ordered us to start
TERMINATED // We are quitting and thread is terminated
};
void wake_up();
bool cutoff_occurred() const;
bool is_available_to(int master) const;
MaterialInfoTable materialTable;
PawnInfoTable pawnTable;
int maxPly;
Lock sleepLock;
WaitCondition sleepCond;
volatile ThreadState state;
SplitPoint* volatile splitPoint;
volatile int activeSplitPoints;
SplitPoint splitPoints[MAX_ACTIVE_SPLIT_POINTS];
};
/// ThreadsManager class is used to handle all the threads related stuff like init,
/// starting, parking and, the most important, launching a slave thread at a split
/// point. All the access to shared thread data is done through this class.
class ThreadsManager {
/* As long as the single ThreadsManager object is defined as a global we don't
need to explicitly initialize to zero its data members because variables with
static storage duration are automatically set to zero before enter main()
*/
public:
Thread& operator[](int threadID) { return threads[threadID]; }
void init();
void exit();
void init_hash_tables();
int min_split_depth() const { return minimumSplitDepth; }
int size() const { return activeThreads; }
void set_size(int cnt) { activeThreads = cnt; }
void read_uci_options();
bool available_slave_exists(int master) const;
void idle_loop(int threadID, SplitPoint* sp);
template <bool Fake>
void split(Position& pos, SearchStack* ss, Value* alpha, const Value beta, Value* bestValue,
Depth depth, Move threatMove, int moveCount, MovePicker* mp, bool pvNode);
private:
Lock mpLock;
Depth minimumSplitDepth;
int maxThreadsPerSplitPoint;
bool useSleepingThreads;
int activeThreads;
volatile bool allThreadsShouldExit;
Thread threads[MAX_THREADS];
};
extern ThreadsManager Threads;
#endif // !defined(THREAD_H_INCLUDED)

View File

@ -0,0 +1,161 @@
/*
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/>.
*/
#include <cmath>
#include "misc.h"
#include "search.h"
#include "timeman.h"
#include "ucioption.h"
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);
}
void TimeManager::pv_instability(int curChanges, int prevChanges) {
unstablePVExtraTime = curChanges * (optimumSearchTime / 2)
+ prevChanges * (optimumSearchTime / 3);
}
void TimeManager::init(const SearchLimits& limits, int currentPly)
{
/* We support four different kind of time controls:
increment == 0 && movesToGo == 0 means: x basetime [sudden death!]
increment == 0 && movesToGo != 0 means: x moves in y minutes
increment > 0 && movesToGo == 0 means: x basetime + z increment
increment > 0 && movesToGo != 0 means: x moves in y minutes + z increment
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 = limits.time;
// 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 <= (limits.movesToGo ? Min(limits.movesToGo, MoveHorizon) : MoveHorizon); hypMTG++)
{
// Calculate thinking time for hypothetic "moves to go"-value
hypMyTime = limits.time
+ limits.increment * (hypMTG - 1)
- emergencyBaseTime
- emergencyMoveTime * Min(hypMTG, emergencyMoveHorizon);
hypMyTime = Max(hypMyTime, 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);
}
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)));
}
}

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-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(TIMEMAN_H_INCLUDED)
#define TIMEMAN_H_INCLUDED
struct SearchLimits;
class TimeManager {
public:
void init(const SearchLimits& limits, int currentPly);
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)

View File

@ -0,0 +1,144 @@
/*
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/>.
*/
#include <cassert>
#include <cstring>
#include <iostream>
#include "tt.h"
TranspositionTable TT; // Our global transposition table
TranspositionTable::TranspositionTable() {
size = generation = 0;
entries = NULL;
}
TranspositionTable::~TranspositionTable() {
delete [] entries;
}
/// TranspositionTable::set_size() sets the size of the transposition table,
/// measured in megabytes.
void TranspositionTable::set_size(size_t mbSize) {
size_t newSize = 1024;
// Transposition table consists of clusters and each cluster consists
// of ClusterSize number of TTEntries. Each non-empty entry contains
// information of exactly one position and newSize is the number of
// clusters we are going to allocate.
while (2ULL * newSize * sizeof(TTCluster) <= (mbSize << 20))
newSize *= 2;
if (newSize == size)
return;
size = newSize;
delete [] entries;
entries = new (std::nothrow) TTCluster[size];
if (!entries)
{
std::cerr << "Failed to allocate " << mbSize
<< " MB for transposition table." << std::endl;
exit(EXIT_FAILURE);
}
clear();
}
/// TranspositionTable::clear() overwrites the entire transposition table
/// 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).
void TranspositionTable::clear() {
memset(entries, 0, size * sizeof(TTCluster));
}
/// TranspositionTable::store() writes a new entry containing position key and
/// valuable information of current position. The lowest order bits of position
/// key are used to decide on which cluster the position will be placed.
/// When a new entry is written and there are no empty entries available in cluster,
/// 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 current search and t2 is from
/// a previous search, or if the depth of t1 is bigger than the depth of t2.
void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d, Move m, Value statV, Value kingD) {
int c1, c2, c3;
TTEntry *tte, *replace;
uint32_t posKey32 = posKey >> 32; // Use the high 32 bits as key inside the cluster
tte = replace = first_entry(posKey);
for (int i = 0; i < ClusterSize; i++, tte++)
{
if (!tte->key() || tte->key() == posKey32) // Empty or overwrite old
{
// Preserve any existing ttMove
if (m == MOVE_NONE)
m = tte->move();
tte->save(posKey32, v, t, d, m, generation, statV, kingD);
return;
}
// Implement replace strategy
c1 = (replace->generation() == generation ? 2 : 0);
c2 = (tte->generation() == generation || tte->type() == VALUE_TYPE_EXACT ? -2 : 0);
c3 = (tte->depth() < replace->depth() ? 1 : 0);
if (c1 + c2 + c3 > 0)
replace = tte;
}
replace->save(posKey32, v, t, d, m, generation, statV, kingD);
}
/// TranspositionTable::probe() looks up the current position in the
/// transposition table. Returns a pointer to the TTEntry or NULL if
/// position is not found.
TTEntry* TranspositionTable::probe(const Key posKey) const {
uint32_t posKey32 = posKey >> 32;
TTEntry* tte = first_entry(posKey);
for (int i = 0; i < ClusterSize; i++, tte++)
if (tte->key() == posKey32)
return tte;
return NULL;
}
/// TranspositionTable::new_search() is called at the beginning of every new
/// search. It increments the "generation" variable, which is used to
/// distinguish transposition table entries from previous searches from
/// entries from the current search.
void TranspositionTable::new_search() {
generation++;
}

View File

@ -0,0 +1,173 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(TT_H_INCLUDED)
#define TT_H_INCLUDED
#include <iostream>
#include "move.h"
#include "types.h"
/// The TTEntry is the class of transposition table entries
///
/// A TTEntry needs 128 bits to be stored
///
/// bit 0-31: key
/// bit 32-63: data
/// bit 64-79: value
/// 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
///
/// bit 0-15: move
/// bit 16-20: not used
/// bit 21-22: value type
/// bit 23-31: generation
class TTEntry {
public:
void save(uint32_t k, Value v, ValueType t, Depth d, Move m, int g, Value statV, Value statM) {
key32 = (uint32_t)k;
move16 = (uint16_t)m;
valueType = (uint8_t)t;
generation8 = (uint8_t)g;
value16 = (int16_t)v;
depth16 = (int16_t)d;
staticValue = (int16_t)statV;
staticMargin = (int16_t)statM;
}
void set_generation(int g) { generation8 = (uint8_t)g; }
uint32_t key() const { return key32; }
Depth depth() const { return (Depth)depth16; }
Move move() const { return (Move)move16; }
Value value() const { return (Value)value16; }
ValueType type() const { return (ValueType)valueType; }
int generation() const { return (int)generation8; }
Value static_value() const { return (Value)staticValue; }
Value static_value_margin() const { return (Value)staticMargin; }
private:
uint32_t key32;
uint16_t move16;
uint8_t valueType, generation8;
int16_t value16, depth16, staticValue, staticMargin;
};
/// This is the number of TTEntry slots for each cluster
const int ClusterSize = 4;
/// TTCluster consists of ClusterSize number of TTEntries. Size of TTCluster
/// must not be bigger than a cache line size. In case it is less, it should
/// be padded to guarantee always aligned accesses.
struct TTCluster {
TTEntry data[ClusterSize];
};
/// The transposition table class. This is basically just a huge array containing
/// TTCluster objects, and a few methods for writing and reading entries.
class TranspositionTable {
TranspositionTable(const TranspositionTable&);
TranspositionTable& operator=(const TranspositionTable&);
public:
TranspositionTable();
~TranspositionTable();
void set_size(size_t mbSize);
void clear();
void store(const Key posKey, Value v, ValueType type, Depth d, Move m, Value statV, Value kingD);
TTEntry* probe(const Key posKey) const;
void new_search();
TTEntry* first_entry(const Key posKey) const;
void refresh(const TTEntry* tte) const;
private:
size_t size;
TTCluster* entries;
uint8_t generation; // Size must be not bigger then TTEntry::generation8
};
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.
inline void TranspositionTable::refresh(const TTEntry* tte) const {
const_cast<TTEntry*>(tte)->set_generation(generation);
}
/// A simple fixed size hash table used to store pawns and material
/// configurations. It is basically just an array of Entry objects.
/// Without cluster concept or overwrite policy.
template<class Entry, int HashSize>
struct SimpleHash {
typedef SimpleHash<Entry, HashSize> Base;
void init() {
if (entries)
return;
entries = new (std::nothrow) Entry[HashSize];
if (!entries)
{
std::cerr << "Failed to allocate " << HashSize * sizeof(Entry)
<< " bytes for hash table." << std::endl;
exit(EXIT_FAILURE);
}
memset(entries, 0, HashSize * sizeof(Entry));
}
virtual ~SimpleHash() { delete [] entries; }
Entry* probe(Key key) const { return entries + ((uint32_t)key & (HashSize - 1)); }
void prefetch(Key key) const { ::prefetch((char*)probe(key)); }
protected:
Entry* entries;
};
#endif // !defined(TT_H_INCLUDED)

View File

@ -0,0 +1,471 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(TYPES_H_INCLUDED)
#define TYPES_H_INCLUDED
#include <climits>
#include <cstdlib>
#include <ctype.h>
#if defined(_MSC_VER)
// 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
#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type
// MSVC does not support <inttypes.h>
typedef signed __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef signed __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef signed __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <inttypes.h>
#endif
#define Min(x, y) (((x) < (y)) ? (x) : (y))
#define Max(x, y) (((x) < (y)) ? (y) : (x))
////
//// Configuration
////
//// For Linux and OSX configuration is done automatically using Makefile.
//// To get started type "make help".
////
//// For windows part of the configuration is detected automatically, but
//// 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
#if defined(_WIN64)
#define IS_64BIT
#endif
// Automatic detection for use of bsfq asm-instruction under Windows
#if defined(_WIN64)
#define USE_BSFQ
#endif
// Intel header for _mm_popcnt_u64() intrinsic
#if defined(USE_POPCNT) && defined(_MSC_VER) && defined(__INTEL_COMPILER)
#include <nmmintrin.h>
#endif
// Cache line alignment specification
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
#define CACHE_LINE_ALIGNMENT __declspec(align(64))
#else
#define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64)))
#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
// Define FORCE_INLINE macro to force inlining overriding compiler choice
#if defined(_MSC_VER)
#define FORCE_INLINE __forceinline
#elif defined(__GNUC__)
#define FORCE_INLINE inline __attribute__((always_inline))
#else
#define FORCE_INLINE inline
#endif
/// cpu_has_popcnt() detects support for popcnt instruction at runtime
inline bool cpu_has_popcnt() {
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();
#else
const bool CpuHasPOPCNT = false;
#endif
/// CpuIs64Bit is a global constant initialized at compile time that
/// is set to true if CPU on which application runs is a 64 bits.
#if defined(IS_64BIT)
const bool CpuIs64Bit = true;
#else
const bool CpuIs64Bit = false;
#endif
#include <string>
typedef uint64_t Key;
typedef uint64_t Bitboard;
const int PLY_MAX = 100;
const int PLY_MAX_PLUS_2 = PLY_MAX + 2;
enum ValueType {
VALUE_TYPE_NONE = 0,
VALUE_TYPE_UPPER = 1,
VALUE_TYPE_LOWER = 2,
VALUE_TYPE_EXACT = VALUE_TYPE_UPPER | VALUE_TYPE_LOWER
};
enum Value {
VALUE_ZERO = 0,
VALUE_DRAW = 0,
VALUE_KNOWN_WIN = 15000,
VALUE_MATE = 30000,
VALUE_INFINITE = 30001,
VALUE_NONE = 30002,
VALUE_MATE_IN_PLY_MAX = VALUE_MATE - PLY_MAX,
VALUE_MATED_IN_PLY_MAX = -VALUE_MATE + PLY_MAX,
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN
};
enum PieceType {
PIECE_TYPE_NONE = 0,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
};
enum Piece {
PIECE_NONE_DARK_SQ = 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
};
enum Color {
WHITE, BLACK, COLOR_NONE
};
enum Depth {
ONE_PLY = 2,
DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_NONE = -127 * ONE_PLY
};
enum Square {
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3,
SQ_A4, SQ_B4, SQ_C4, SQ_D4, SQ_E4, SQ_F4, SQ_G4, SQ_H4,
SQ_A5, SQ_B5, SQ_C5, SQ_D5, SQ_E5, SQ_F5, SQ_G5, SQ_H5,
SQ_A6, SQ_B6, SQ_C6, SQ_D6, SQ_E6, SQ_F6, SQ_G6, SQ_H6,
SQ_A7, SQ_B7, SQ_C7, SQ_D7, SQ_E7, SQ_F7, SQ_G7, SQ_H7,
SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8,
SQ_NONE,
DELTA_N = 8,
DELTA_E = 1,
DELTA_S = -8,
DELTA_W = -1,
DELTA_NN = DELTA_N + DELTA_N,
DELTA_NE = DELTA_N + DELTA_E,
DELTA_SE = DELTA_S + DELTA_E,
DELTA_SS = DELTA_S + DELTA_S,
DELTA_SW = DELTA_S + DELTA_W,
DELTA_NW = DELTA_N + DELTA_W
};
enum File {
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H
};
enum Rank {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8
};
enum SquareColor {
DARK, LIGHT
};
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
/// integer (enum), first LSB 16 bits are used to store endgame
/// value, while upper bits are used for midgame value. Compiler
/// is free to choose the enum type as long as can keep its data,
/// so ensure Score to be an integer type.
enum Score {
SCORE_ZERO = 0,
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
};
#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); }
ENABLE_OPERATORS_ON(Value)
ENABLE_OPERATORS_ON(PieceType)
ENABLE_OPERATORS_ON(Piece)
ENABLE_OPERATORS_ON(Color)
ENABLE_OPERATORS_ON(Depth)
ENABLE_OPERATORS_ON(Square)
ENABLE_OPERATORS_ON(File)
ENABLE_OPERATORS_ON(Rank)
#undef ENABLE_OPERATORS_ON
// Extra operators for adding integers to a Value
inline Value operator+ (Value v, int i) { return Value(int(v) + i); }
inline Value operator- (Value v, int i) { return Value(int(v) - i); }
// Extracting the _signed_ lower and upper 16 bits it not so trivial
// because according to the standard a simple cast to short is
// implementation defined and so is a right shift of a signed integer.
inline Value mg_value(Score s) { return Value(((int(s) + 32768) & ~0xffff) / 0x10000); }
// Unfortunatly on Intel 64 bit we have a small speed regression, so use a faster code in
// this case, although not 100% standard compliant it seems to work for Intel and MSVC.
#if defined(IS_64BIT) && (!defined(__GNUC__) || defined(__INTEL_COMPILER))
inline Value eg_value(Score s) { return Value(int16_t(s & 0xffff)); }
#else
inline Value eg_value(Score s) { return Value((int)(unsigned(s) & 0x7fffu) - (int)(unsigned(s) & 0x8000u)); }
#endif
inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
// 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); }
// Only declared but not defined. We don't want to multiply two scores due to
// a very high risk of overflow. So user should explicitly convert to integer.
inline Score operator*(Score s1, Score s2);
// Remaining Score 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)); }
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)); }
inline void operator+= (Score& d1, const Score d2) { d1 = d1 + d2; }
inline void operator-= (Score& d1, const Score d2) { d1 = d1 - d2; }
inline void operator*= (Score& d, int i) { d = Score(int(d) * i); }
inline void operator/= (Score& d, int i) { d = Score(int(d) / i); }
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 Value value_mate_in(int ply) {
return VALUE_MATE - ply;
}
inline Value value_mated_in(int ply) {
return -VALUE_MATE + ply;
}
inline Piece make_piece(Color c, PieceType pt) {
return Piece((int(c) << 3) | int(pt));
}
inline PieceType type_of_piece(Piece p) {
return PieceType(int(p) & 7);
}
inline Color color_of_piece(Piece p) {
return Color(int(p) >> 3);
}
inline Color opposite_color(Color c) {
return Color(int(c) ^ 1);
}
inline bool color_is_ok(Color c) {
return c == WHITE || c == BLACK;
}
inline bool piece_type_is_ok(PieceType pt) {
return pt >= PAWN && pt <= KING;
}
inline bool piece_is_ok(Piece p) {
return piece_type_is_ok(type_of_piece(p)) && color_is_ok(color_of_piece(p));
}
inline char piece_type_to_char(PieceType pt) {
static const char ch[] = " PNBRQK";
return ch[pt];
}
inline Square make_square(File f, Rank r) {
return Square((int(r) << 3) | int(f));
}
inline File square_file(Square s) {
return File(int(s) & 7);
}
inline Rank square_rank(Square s) {
return Rank(int(s) >> 3);
}
inline Square flip_square(Square s) {
return Square(int(s) ^ 56);
}
inline Square flop_square(Square s) {
return Square(int(s) ^ 7);
}
inline Square relative_square(Color c, Square s) {
return Square(int(s) ^ (int(c) * 56));
}
inline Rank relative_rank(Color c, Rank r) {
return Rank(int(r) ^ (int(c) * 7));
}
inline Rank relative_rank(Color c, Square s) {
return relative_rank(c, square_rank(s));
}
inline SquareColor square_color(Square s) {
return SquareColor(int(square_rank(s) + s) & 1);
}
inline bool opposite_color_squares(Square s1, Square s2) {
int s = int(s1) ^ int(s2);
return ((s >> 3) ^ s) & 1;
}
inline int file_distance(Square s1, Square s2) {
return abs(square_file(s1) - square_file(s2));
}
inline int rank_distance(Square s1, Square s2) {
return abs(square_rank(s1) - square_rank(s2));
}
inline int square_distance(Square s1, Square s2) {
return Max(file_distance(s1, s2), rank_distance(s1, s2));
}
inline File file_from_char(char c) {
return File(c - 'a') + FILE_A;
}
inline char file_to_char(File f) {
return char(f - FILE_A + int('a'));
}
inline Rank rank_from_char(char c) {
return Rank(c - '1') + RANK_1;
}
inline char rank_to_char(Rank r) {
return char(r - RANK_1 + int('1'));
}
inline const std::string square_to_string(Square s) {
char ch[] = { file_to_char(square_file(s)), rank_to_char(square_rank(s)), 0 };
return std::string(ch);
}
inline bool file_is_ok(File f) {
return f >= FILE_A && f <= FILE_H;
}
inline bool rank_is_ok(Rank r) {
return r >= RANK_1 && r <= RANK_8;
}
inline bool square_is_ok(Square s) {
return s >= SQ_A1 && s <= SQ_H8;
}
inline Square pawn_push(Color c) {
return c == WHITE ? DELTA_N : DELTA_S;
}
#endif // !defined(TYPES_H_INCLUDED)

View File

@ -0,0 +1,247 @@
/*
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/>.
*/
#include <cassert>
#include <iostream>
#include <sstream>
#include <string>
#include "evaluate.h"
#include "misc.h"
#include "move.h"
#include "position.h"
#include "search.h"
#include "ucioption.h"
using namespace std;
namespace {
// FEN string for the initial position
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.
typedef istringstream UCIParser;
void set_option(UCIParser& up);
void set_position(Position& pos, UCIParser& up);
bool go(Position& pos, UCIParser& up);
void perft(Position& pos, UCIParser& up);
}
/// execute_uci_command() takes a string as input, uses a UCIParser
/// object to parse this text string as a UCI command, and calls
/// the appropriate functions. In addition to the UCI commands,
/// the function also supports a few debug commands.
bool execute_uci_command(const string& cmd) {
static Position pos(StartPositionFEN, false, 0); // The root position
UCIParser up(cmd);
string token;
up >> token; // operator>>() skips any whitespace
if (token == "quit")
return false;
if (token == "go")
return go(pos, up);
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);
else if (token == "perft")
perft(pos, up);
else if (token == "d")
pos.print();
else if (token == "flip")
pos.flip();
else if (token == "eval")
{
read_evaluation_uci_options(pos.side_to_move());
cout << trace_evaluate(pos) << 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 == "uci")
cout << "id name " << engine_name()
<< "\nid author " << engine_authors()
<< "\n" << Options.print_all()
<< "\nuciok" << endl;
else
cout << "Unknown command: " << cmd << endl;
return true;
}
namespace {
// set_position() is called when engine receives the "position" UCI
// command. The function sets up the position described in the given
// fen string ("fen") or the starting position ("startpos") and then
// makes the moves given in the following move list ("moves").
void set_position(Position& pos, UCIParser& up) {
string token, fen;
up >> token; // operator>>() skips any whitespace
if (token == "startpos")
{
pos.from_fen(StartPositionFEN, false);
up >> token; // Consume "moves" token if any
}
else if (token == "fen")
{
while (up >> token && token != "moves")
fen += token + " ";
pos.from_fen(fen, Options["UCI_Chess960"].value<bool>());
}
else return;
// Parse move list (if any)
while (up >> token)
pos.do_setup_move(move_from_uci(pos, token));
}
// set_option() is called when engine receives the "setoption" UCI
// command. The function updates the corresponding UCI option ("name")
// to the given value ("value").
void set_option(UCIParser& up) {
string token, name;
string value = "true"; // UCI buttons don't have a "value" field
up >> token; // Consume "name" token
up >> name; // Read option name
// Handle names with included spaces
while (up >> token && token != "value")
name += " " + token;
up >> value; // Read option value
// Handle values with included spaces
while (up >> token)
value += " " + token;
if (Options.find(name) != Options.end())
Options[name].set_value(value);
else
cout << "No such option: " << name << endl;
}
// go() is called when engine receives the "go" UCI command. The
// function sets the thinking time and other parameters from the input
// string, and then calls think(). Returns false if a quit command
// is received while thinking, true otherwise.
bool go(Position& pos, UCIParser& up) {
string token;
SearchLimits limits;
Move searchMoves[MAX_MOVES], *cur = searchMoves;
int time[] = { 0, 0 }, inc[] = { 0, 0 };
while (up >> token)
{
if (token == "infinite")
limits.infinite = true;
else if (token == "ponder")
limits.ponder = true;
else if (token == "wtime")
up >> time[WHITE];
else if (token == "btime")
up >> time[BLACK];
else if (token == "winc")
up >> inc[WHITE];
else if (token == "binc")
up >> inc[BLACK];
else if (token == "movestogo")
up >> limits.movesToGo;
else if (token == "depth")
up >> limits.maxDepth;
else if (token == "nodes")
up >> limits.maxNodes;
else if (token == "movetime")
up >> limits.maxTime;
else if (token == "searchmoves")
while (up >> token)
*cur++ = move_from_uci(pos, token);
}
*cur = MOVE_NONE;
limits.time = time[pos.side_to_move()];
limits.increment = inc[pos.side_to_move()];
assert(pos.is_ok());
return think(pos, limits, searchMoves);
}
// perft() is called when engine receives the "perft" command.
// The function calls perft() passing the required search depth
// then prints counted leaf nodes and elapsed time.
void perft(Position& pos, UCIParser& up) {
int depth, time;
int64_t n;
if (!(up >> depth))
return;
time = get_system_time();
n = perft(pos, depth * ONE_PLY);
time = get_system_time() - time;
std::cout << "\nNodes " << n
<< "\nTime (ms) " << time
<< "\nNodes/second " << int(n / (time / 1000.0)) << std::endl;
}
}

View File

@ -0,0 +1,170 @@
/*
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/>.
*/
#include <cctype>
#include <iostream>
#include <sstream>
#include "misc.h"
#include "thread.h"
#include "ucioption.h"
using std::string;
using std::cout;
using std::endl;
OptionsMap Options; // Global object
// Our case insensitive less() function as required by UCI protocol
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
int c1, c2;
size_t i = 0;
while (i < s1.size() && i < s2.size())
{
c1 = tolower(s1[i]);
c2 = tolower(s2[i++]);
if (c1 != c2)
return c1 < c2;
}
return s1.size() < s2.size();
}
// stringify() converts a numeric value of type T to a std::string
template<typename T>
static string stringify(const T& v) {
std::ostringstream ss;
ss << v;
return ss.str();
}
/// OptionsMap c'tor initializes the UCI options to their hard coded default
/// values and initializes the default value of "Threads" and "Minimum Split Depth"
/// parameters according to the number of CPU cores.
OptionsMap::OptionsMap() {
OptionsMap& o = *this;
o["Use Search Log"] = UCIOption(false);
o["Search Log Filename"] = UCIOption("SearchLog.txt");
o["Book File"] = UCIOption("book.bin");
o["Best Book Move"] = UCIOption(false);
o["Mobility (Middle Game)"] = UCIOption(100, 0, 200);
o["Mobility (Endgame)"] = UCIOption(100, 0, 200);
o["Passed Pawns (Middle Game)"] = UCIOption(100, 0, 200);
o["Passed Pawns (Endgame)"] = UCIOption(100, 0, 200);
o["Space"] = UCIOption(100, 0, 200);
o["Aggressiveness"] = UCIOption(100, 0, 200);
o["Cowardice"] = UCIOption(100, 0, 200);
o["Minimum Split Depth"] = UCIOption(4, 4, 7);
o["Maximum Number of Threads per Split Point"] = UCIOption(5, 4, 8);
o["Threads"] = UCIOption(1, 1, MAX_THREADS);
o["Use Sleeping Threads"] = UCIOption(false);
o["Hash"] = UCIOption(32, 4, 8192);
o["Clear Hash"] = UCIOption(false, "button");
o["Ponder"] = UCIOption(true);
o["OwnBook"] = UCIOption(true);
o["MultiPV"] = UCIOption(1, 1, 500);
o["Skill Level"] = UCIOption(20, 0, 20);
o["Emergency Move Horizon"] = UCIOption(40, 0, 50);
o["Emergency Base Time"] = UCIOption(200, 0, 30000);
o["Emergency Move Time"] = UCIOption(70, 0, 5000);
o["Minimum Thinking Time"] = UCIOption(20, 0, 5000);
o["UCI_Chess960"] = UCIOption(false);
o["UCI_AnalyseMode"] = UCIOption(false);
// Set some SMP parameters accordingly to the detected CPU count
UCIOption& thr = o["Threads"];
UCIOption& msd = o["Minimum Split Depth"];
thr.defaultValue = thr.currentValue = stringify(cpu_count());
if (cpu_count() >= 8)
msd.defaultValue = msd.currentValue = stringify(7);
}
/// OptionsMap::print_all() returns a string with all the UCI options in chronological
/// insertion order (the idx field) and in the format defined by the UCI protocol.
string OptionsMap::print_all() const {
std::stringstream s;
for (size_t i = 0; i <= size(); i++)
for (OptionsMap::const_iterator it = begin(); it != end(); ++it)
if (it->second.idx == i)
{
const UCIOption& o = it->second;
s << "\noption name " << it->first << " type " << o.type;
if (o.type != "button")
s << " default " << o.defaultValue;
if (o.type == "spin")
s << " min " << o.minValue << " max " << o.maxValue;
break;
}
return s.str();
}
/// Option class c'tors
UCIOption::UCIOption(const char* def) : type("string"), minValue(0), maxValue(0), idx(Options.size())
{ defaultValue = currentValue = def; }
UCIOption::UCIOption(bool def, string t) : type(t), minValue(0), maxValue(0), idx(Options.size())
{ defaultValue = currentValue = (def ? "true" : "false"); }
UCIOption::UCIOption(int def, int minv, int maxv) : type("spin"), minValue(minv), maxValue(maxv), idx(Options.size())
{ defaultValue = currentValue = stringify(def); }
/// set_value() updates currentValue of the Option object. Normally it's up to
/// the GUI to check for option's limits, but we could receive the new value
/// directly from the user by teminal window. So let's check the bounds anyway.
void UCIOption::set_value(const string& v) {
assert(!type.empty());
if (v.empty())
return;
if ((type == "check" || type == "button") != (v == "true" || v == "false"))
return;
if (type == "spin")
{
int val = atoi(v.c_str());
if (val < minValue || val > maxValue)
return;
}
currentValue = v;
}

View File

@ -0,0 +1,85 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(UCIOPTION_H_INCLUDED)
#define UCIOPTION_H_INCLUDED
#include <cassert>
#include <cstdlib>
#include <map>
#include <string>
class UCIOption {
public:
UCIOption() {} // To be used in a std::map
UCIOption(const char* defaultValue);
UCIOption(bool defaultValue, std::string type = "check");
UCIOption(int defaultValue, int minValue, int maxValue);
void set_value(const std::string& v);
template<typename T> T value() const;
private:
friend class OptionsMap;
std::string defaultValue, currentValue, type;
int minValue, maxValue;
size_t idx;
};
/// Custom comparator because UCI options should not be case sensitive
struct CaseInsensitiveLess {
bool operator() (const std::string&, const std::string&) const;
};
/// Our options container is actually a map with a customized c'tor
class OptionsMap : public std::map<std::string, UCIOption, CaseInsensitiveLess> {
public:
OptionsMap();
std::string print_all() const;
};
extern OptionsMap Options;
/// Option::value() definition and specializations
template<typename T>
T UCIOption::value() const {
assert(type == "spin");
return T(atoi(currentValue.c_str()));
}
template<>
inline std::string UCIOption::value<std::string>() const {
assert(type == "string");
return currentValue;
}
template<>
inline bool UCIOption::value<bool>() const {
assert(type == "check" || type == "button");
return currentValue == "true";
}
#endif // !defined(UCIOPTION_H_INCLUDED)

View File

@ -0,0 +1,13 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "ant.properties", and override values to adapt the script to your
# project structure.
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=android-10

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<view
class="org.petero.droidfish.activities.ChessBoardEdit"
android:id="@+id/eb_chessboard"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:text=""
android:id="@+id/eb_status"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:includeFontPadding="true"
android:typeface="monospace"
android:textSize="10dip"/>
<LinearLayout
android:orientation="horizontal"
android:id="@+id/LinearLayout02"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:text="Cancel"
android:id="@+id/eb_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1">
</Button>
<Button
android:text="Ok"
android:id="@+id/eb_ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1">
</Button>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<view
class="org.petero.droidfish.ChessBoard"
android:id="@+id/chessboard"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageButton
android:id="@+id/modeButton"
android:layout_width="36dp"
android:layout_height="32dp"
android:src="@drawable/mode">
</ImageButton>
<ImageButton
android:id="@+id/undoButton"
android:layout_width="36dp"
android:layout_height="32dp"
android:src="@drawable/left">
</ImageButton>
<ImageButton
android:id="@+id/redoButton"
android:layout_width="36dp"
android:layout_height="32dp"
android:src="@drawable/right">
</ImageButton>
</LinearLayout>
<TextView
android:id="@+id/status"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:includeFontPadding="true"
android:typeface="monospace"
android:textSize="10dip"/>
<RelativeLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<view
class="org.petero.droidfish.MyScrollView"
android:id="@+id/scrollViewBot"
android:layout_alignParentBottom="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/thinking"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:includeFontPadding="true"
android:typeface="monospace"
android:textSize="10dip"/>
</view>
<ScrollView
android:id="@+id/scrollView"
android:layout_above="@+id/scrollViewBot"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="@+id/moveList"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:includeFontPadding="true"
android:typeface="monospace"
android:textSize="10dip"/>
</ScrollView>
</RelativeLayout>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</LinearLayout>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_weight="1"
android:text="Filename:">
</TextView>
<EditText
android:id="@+id/create_pgn_filename"
android:background="@android:drawable/editbox_background"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:minEms="8"
android:inputType="text">
</EditText>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/create_pgn_cancel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Cancel">
</Button>
<Button
android:id="@+id/create_pgn_ok"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Ok">
</Button>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:stretchColumns="1">
<TableRow>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Before">
</TextView>
<EditText
android:id="@+id/ed_comments_pre"
android:background="@android:drawable/editbox_background"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:lines="2"
android:inputType="textMultiLine">
</EditText>
</TableRow>
<TableRow>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Move">
</TextView>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
android:layout_weight="1"
<TextView
android:id="@+id/ed_comments_move"
android:background="@android:drawable/editbox_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lines="1"
android:inputType="text">
</TextView>
<EditText
android:id="@+id/ed_comments_nag"
android:background="@android:drawable/editbox_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lines="1"
android:minEms="3"
android:inputType="text">
</EditText>
</LinearLayout>
</TableRow>
<TableRow>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="After">
</TextView>
<EditText
android:id="@+id/ed_comments_post"
android:background="@android:drawable/editbox_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:lines="5"
android:inputType="textMultiLine">
</EditText>
</TableRow>
</TableLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/ed_comments_cancel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Cancel">
</Button>
<Button
android:id="@+id/ed_comments_ok"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Ok">
</Button>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:stretchColumns="1">
<TableRow>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Event">
</TextView>
<EditText
android:id="@+id/ed_header_event"
android:background="@android:drawable/editbox_background"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="text">
</EditText>
</TableRow>
<TableRow>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Site">
</TextView>
<EditText
android:id="@+id/ed_header_site"
android:background="@android:drawable/editbox_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="text">
</EditText>
</TableRow>
<TableRow>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Date">
</TextView>
<EditText
android:id="@+id/ed_header_date"
android:background="@android:drawable/editbox_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="text">
</EditText>
</TableRow>
<TableRow>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Round">
</TextView>
<EditText
android:id="@+id/ed_header_round"
android:background="@android:drawable/editbox_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="text">
</EditText>
</TableRow>
<TableRow>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="White">
</TextView>
<EditText
android:id="@+id/ed_header_white"
android:background="@android:drawable/editbox_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="text">
</EditText>
</TableRow>
<TableRow>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Black">
</TextView>
<EditText
android:id="@+id/ed_header_black"
android:background="@android:drawable/editbox_background"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_weight="1"
android:inputType="text">
</EditText>
</TableRow>
</TableLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/ed_header_cancel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Cancel">
</Button>
<Button
android:id="@+id/ed_header_ok"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Ok">
</Button>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_weight="1"
android:text="Halfmove clock">
</TextView>
<EditText
android:id="@+id/ed_cnt_halfmove"
android:background="@android:drawable/editbox_background"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:inputType="number">
</EditText>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_width="fill_parent"
android:text="Fullmove counter">
</TextView>
<EditText
android:id="@+id/ed_cnt_fullmove"
android:background="@android:drawable/editbox_background"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:inputType="number">
</EditText>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/ed_cnt_cancel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Cancel">
</Button>
<Button
android:id="@+id/ed_cnt_ok"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Ok">
</Button>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<view
class="org.petero.droidfish.activities.ChessBoardEdit"
android:id="@+id/eb_chessboard"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<TextView
android:text=""
android:id="@+id/eb_status"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:includeFontPadding="true"
android:typeface="monospace"
android:textSize="10dip"/>
<LinearLayout
android:orientation="horizontal"
android:id="@+id/LinearLayout02"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:text="Cancel"
android:id="@+id/eb_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1">
</Button>
<Button
android:text="Ok"
android:id="@+id/eb_ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1">
</Button>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<view
class="org.petero.droidfish.ChessBoard"
android:id="@+id/chessboard"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/status"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:includeFontPadding="true"
android:typeface="monospace"
android:textSize="10dip"/>
<ImageButton
android:id="@+id/modeButton"
android:layout_width="36dp"
android:layout_height="32dp"
android:src="@drawable/mode">
</ImageButton>
<ImageButton
android:id="@+id/undoButton"
android:layout_width="36dp"
android:layout_height="32dp"
android:src="@drawable/left">
</ImageButton>
<ImageButton
android:id="@+id/redoButton"
android:layout_width="36dp"
android:layout_height="32dp"
android:src="@drawable/right">
</ImageButton>
</LinearLayout>
<RelativeLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<view
class="org.petero.droidfish.MyScrollView"
android:id="@+id/scrollViewBot"
android:layout_alignParentBottom="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/thinking"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:includeFontPadding="true"
android:typeface="monospace"
android:textSize="10dip"/>
</view>
<ScrollView
android:id="@+id/scrollView"
android:layout_above="@+id/scrollViewBot"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="@+id/moveList"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:includeFontPadding="true"
android:typeface="monospace"
android:textSize="10dip"/>
</ScrollView>
</RelativeLayout>
</LinearLayout>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<EditText
android:text=""
android:id="@+id/select_game_filter"
android:hint="Filter text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:inputType="text">
</EditText>
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:focusable="false">
</ListView>
</LinearLayout>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp"
android:textSize="14sp">
</TextView>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_weight="1"
android:text="Move number">
</TextView>
<EditText
android:id="@+id/selmove_number"
android:background="@android:drawable/editbox_background"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:inputType="number">
</EditText>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/selmove_cancel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Cancel">
</Button>
<Button
android:id="@+id/selmove_ok"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Ok">
</Button>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_weight="1"
android:text="Value (%)">
</TextView>
<EditText
android:id="@+id/selpercentage_number"
android:background="@android:drawable/editbox_background"
android:layout_width="100dip"
android:layout_height="wrap_content"
android:inputType="numberDecimal">
</EditText>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/selpercentage_cancel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Cancel">
</Button>
<Button
android:id="@+id/selpercentage_ok"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Ok">
</Button>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/white_clock"
android:text="White: 0:00"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true">
</TextView>
<TextView
android:id="@+id/title_text"
android:text="@string/app_name"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true">
</TextView>
<TextView
android:id="@+id/black_clock"
android:text="Black: 0:00"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true">
</TextView>
</RelativeLayout>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/item_delete_file"
android:title="Delete File">
</item>
</menu>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/item_new_game"
android:title="New Game"
android:icon="@drawable/start">
</item>
<item
android:id="@+id/item_file_menu"
android:title="File"
android:icon="@android:drawable/ic_menu_save">
</item>
<item
android:id="@+id/item_resign"
android:title="Resign game"
android:icon="@android:drawable/ic_menu_delete">
</item>
<item
android:id="@+id/item_editboard"
android:title="Edit Board"
android:icon="@drawable/edit">
</item>
<item
android:id="@+id/item_settings"
android:title="Settings"
android:icon="@android:drawable/ic_menu_preferences">
</item>
<item
android:id="@+id/item_goto_move"
android:title="Goto Move"
android:icon="@android:drawable/ic_menu_mylocation">
</item>
<item
android:id="@+id/item_force_move"
android:title="Force Computer Move">
</item>
<item
android:id="@+id/item_draw"
android:title="Claim/Offer/Accept Draw">
</item>
<item
android:id="@+id/select_book"
android:title="Select Opening Book">
</item>
<item
android:id="@+id/set_color_theme"
android:title="Set Color Theme">
</item>
<item
android:id="@+id/item_about"
android:title="About / Help">
</item>
</menu>

Binary file not shown.

View File

@ -0,0 +1,321 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">DroidFish</string>
<string-array name="engine_texts">
<item>Stockfish</item>
<item>CuckooChess</item>
</string-array>
<string-array name="engine_values">
<item>stockfish</item>
<item>cuckoochess</item>
</string-array>
<string-array name="engine_threads_texts">
<item>Automatic</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
</string-array>
<string-array name="engine_threads_values">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
</string-array>
<string-array name="moves_per_session_texts">
<item>Whole Game</item>
<item>1 move</item>
<item>10 moves</item>
<item>20 moves</item>
<item>30 moves</item>
<item>40 moves</item>
<item>50 moves</item>
<item>60 moves</item>
</string-array>
<string-array name="moves_per_session_values">
<item>0</item>
<item>1</item>
<item>10</item>
<item>20</item>
<item>30</item>
<item>40</item>
<item>50</item>
<item>60</item>
</string-array>
<string-array name="time_control_texts">
<item>15 seconds</item>
<item>30 seconds</item>
<item>1 minute</item>
<item>2 minutes</item>
<item>3 minutes</item>
<item>5 minutes</item>
<item>10 minutes</item>
<item>15 minutes</item>
<item>30 minutes</item>
<item>45 minutes</item>
<item>60 minutes</item>
<item>90 minutes</item>
<item>120 minutes</item>
</string-array>
<string-array name="time_control_values">
<item>15000</item>
<item>30000</item>
<item>60000</item>
<item>120000</item>
<item>180000</item>
<item>300000</item>
<item>600000</item>
<item>900000</item>
<item>1800000</item>
<item>2700000</item>
<item>3600000</item>
<item>5400000</item>
<item>7200000</item>
</string-array>
<string-array name="time_increment_texts">
<item>0s</item>
<item>1s</item>
<item>2s</item>
<item>3s</item>
<item>4s</item>
<item>5s</item>
<item>10s</item>
<item>12s</item>
<item>15s</item>
<item>30s</item>
<item>60s</item>
</string-array>
<string-array name="time_increment_values">
<item>0</item>
<item>1000</item>
<item>2000</item>
<item>3000</item>
<item>4000</item>
<item>5000</item>
<item>10000</item>
<item>12000</item>
<item>15000</item>
<item>30000</item>
<item>60000</item>
</string-array>
<string-array name="font_size_texts">
<item>Small</item>
<item>Medium</item>
<item>Large</item>
<item>Larger</item>
</string-array>
<string-array name="font_size_values">
<item>10</item>
<item>12</item>
<item>16</item>
<item>21</item>
</string-array>
<string-array name="thinking_arrows_texts">
<item>No arrows</item>
<item>Max 1 Arrow</item>
<item>Max 2 Arrows</item>
<item>Max 3 Arrows</item>
<item>Max 4 Arrows</item>
<item>Max 5 Arrows</item>
<item>Max 6 Arrows</item>
</string-array>
<string-array name="thinking_arrows_values">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
</string-array>
<string-array name="scroll_sensitivity_texts">
<item>Off</item>
<item>Slowest</item>
<item>Slow</item>
<item>Normal</item>
<item>Fast</item>
<item>Fastest</item>
</string-array>
<string-array name="scroll_sensitivity_values">
<item>0</item>
<item>6</item>
<item>3</item>
<item>2</item>
<item>1</item>
<item>0.5</item>
</string-array>
<string-array name="book_line_length_values">
<item>5</item>
<item>10</item>
<item>15</item>
<item>20</item>
<item>30</item>
<item>50</item>
<item>1000000</item>
</string-array>
<string-array name="book_line_length_texts">
<item>5 moves</item>
<item>10 moves</item>
<item>15 moves</item>
<item>20 moves</item>
<item>30 moves</item>
<item>50 moves</item>
<item>Unlimited</item>
</string-array>
<string name="about_info">\
<b>About</b>\n\
<i>DroidFish</i> is an Android port of the famous <i>stockfish 2.1</i> chess engine. \
<i>Stockfish</i> is one of the strongest chess engines in the world. \
The Android version can often search 15 ply or deeper on a 1GHz Snapdragon CPU.\n\
\n\
<b>Features</b>\n\
* Opening book\n\
* Unlimited undo/redo\n\
* Copy/paste to/from clipboard\n\
* Analysis mode\n\
* Two player mode\n\
* Computer vs computer mode\n\
* Setup positions\n\
* Extensive PGN support\n\
* Adjustable playing strength\n\
\n\
<b>Hints</b>\n\
* In game mode, long press chess board to activate copy/paste menu.\n\
* In edit board mode, long press chess board to activate edit menu.\n\
* Scroll left/right on the chess board to undo/redo moves.\n\
* If the game contains multiple variations, scroll up/down on the chess board to \
go to the previous/next variation.\n\
* Long press left/right buttons for additional game navigation commands.\n\
\n\
<b>Opening books</b>\n\
DroidFish contains a small internal opening book. If you want to use \
a larger book, you can configure DroidFish to use external <i>polyglot</i> \
or <i>CTG</i> book files:\n\
1. Create a directory called <i>DroidFish</i> on the SD card.\n\
2a. Copy one or more polyglot book files to the <i>DroidFish</i> directory. \
Polyglot books must have the file extension .bin.\n\
2b. Copy one more more CTG book files to the <i>DroidFish</i> directory. A CTG \
book consists of three files with file extensions .ctg, .ctb and .cto. \
You must copy all three files.\n\
3. From the program menu, activate the <i>Select opening book</i> function.\n\
4. Select the opening book file you want to use.\n\
\n\
<b>PGN files</b>\n\
DroidFish can load games from PGN files. To use this feature:\n\
1. Create a directory called <i>DroidFish/pgn</i> on the SD card.\n\
2. Copy one or more PGN files to the <i>DroidFish/pgn</i> directory.\n\
3. Long press the chess board and select <i>Load game from PGN file</i>.\n\
4. Select the file, then the game within the file you want to load.\n\
\n\
To edit game headers such as player names, long press the move list and select \
<i>Edit Headers</i>. To edit comments for a move, long press the move list and \
select <i>Edit Comments</i>. To save your edited game, long press the chess board \
and select <i>Save game to PGN file</i>.\n\
\n\
To add computer analysis to the game tree, long press the analysis information \
when the computer is thinking and select <i>Add Analysis</i>. The current PV is \
added as a variation in the game tree.\n\
\n\
In analysis mode, it is possible to add null moves to the game tree. Be aware though \
that null moves are not included in the PGN standard and may not work in other PGN \
reading chess programs.\n\
\n\
<b>Scid files</b>\n\
If you have <i>Scid on the go</i> installed, you can read Scid database files \
directly from DroidFish. Long press the chess board and select <i>Load game from \
Scid file</i>.\n\
\n\
<b>License</b>\n\
GNU GPL v3\n\
\n\
<b>Developers</b>\n\
* <i>Stockfish</i> is developed by Tord Romstad, Marco Costalba and Joona Kiiski.\n\
* <i>Droidfish</i> is developed by Peter Österlund.\n\
</string>
<string name="cpu_warning">\
<b>CPU Usage</b>\n\
If you leave DroidFish running in the background and <i>GameMode</i> is set to \
<i>Analysis</i> or <i>Computer vs Computer</i>, or if <i>Thinking Time</i> \
is set to a large value, or if <i>Pondering</i> is enabled, DroidFish can use \
a lot of CPU power.\n\
\n\
If you are running on battery power, it is recommended that you change settings to \
conserve battery power, or that you exit <i>DroidFish</i> using the back button when \
you are not actively using the program.\
</string>
<string name="offer_draw">Offer Draw</string>
<string name="queen">Queen</string>
<string name="rook">Rook</string>
<string name="bishop">Bishop</string>
<string name="knight">Knight</string>
<string name="promote_pawn_to">Promote pawn to?</string>
<string name="copy_game">Copy Game to Clipboard</string>
<string name="copy_position">Copy Position to Clipboard</string>
<string name="paste">Paste from Clipboard</string>
<string name="load_game">Load game from PGN file</string>
<string name="load_scid_game">Load game from Scid file</string>
<string name="save_game">Save game to PGN file</string>
<string name="truncate_gametree">Truncate Game Tree</string>
<string name="move_var_up">Move Variation Up</string>
<string name="move_var_down">Move Variation Down</string>
<string name="add_null_move">Add Null Move</string>
<string name="tools_menu">Tools</string>
<string name="load_save_menu">Load/Save Game</string>
<string name="goto_move">Goto move</string>
<string name="edit_strength">Edit Strength</string>
<string name="edit_randomization">Edit Randomization</string>
<string name="invalid_number_format">Invalid number format</string>
<string name="side_to_move">Side to Move</string>
<string name="clear_board">Clear Board</string>
<string name="initial_position">Initial Position</string>
<string name="castling_flags">Castling Flags</string>
<string name="en_passant_file">En Passant File</string>
<string name="move_counters">Move Counters</string>
<string name="paste_position">Paste Position</string>
<string name="edit_board">Edit Board</string>
<string name="select_side_to_move_first">Select side to move first</string>
<string name="white">White</string>
<string name="black">Black</string>
<string name="white_king_castle">White king castle</string>
<string name="white_queen_castle">White queen castle</string>
<string name="black_king_castle">Black king castle</string>
<string name="black_queen_castle">Black queen castle</string>
<string name="none">None</string>
<string name="select_en_passant_file">Select En Passant File</string>
<string name="edit_move_counters">Edit Move Counters</string>
<string name="internal_book">&lt;Internal Book&gt;</string>
<string name="select_opening_book_file">Select opening book file</string>
<string name="select_pgn_file">Select PGN file to open</string>
<string name="select_pgn_file_save">Save to PGN file</string>
<string name="select_scid_file">Select Scid file to open</string>
<string name="select_pgn_game">Select game to open</string>
<string name="reading_pgn_file">Reading PGN file</string>
<string name="reading_scid_file">Reading Scid file</string>
<string name="new_file">&lt;New File&gt;</string>
<string name="please_wait">Please wait...</string>
<string name="no_pgn_files">No files found in directory DroidFish/pgn on the SD card</string>
<string name="no_scid_files">No files found in directory scid on the SD card</string>
<string name="select_color_theme">Select Color Theme</string>
<string name="edit_replay_game">Edit/re-play Game</string>
<string name="analysis_mode">Analysis Mode</string>
<string name="play_white">Play White</string>
<string name="play_black">Play Black</string>
<string name="two_players">Two Players</string>
<string name="comp_vs_comp">Computer vs Computer</string>
<string name="analysis">Analysis</string>
<string name="edit_game">Edit Game</string>
<string name="edit_headers">Edit Headers</string>
<string name="edit_comments">Edit Comments</string>
<string name="go_back">Go Back</string>
<string name="go_forward">Go Forward</string>
<string name="goto_start_game">Goto Start of Game</string>
<string name="goto_start_variation">Goto Start of Variation</string>
<string name="goto_prev_variation">Goto Previous Variation</string>
<string name="load_prev_game">Load Previous Game</string>
<string name="goto_end_variation">Goto End of Variation</string>
<string name="goto_next_variation">Goto Next Variation</string>
<string name="load_next_game">Load Next Game</string>
<string name="no_prev_game">No previous game</string>
<string name="no_next_game">No next game</string>
</resources>

View File

@ -0,0 +1,377 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="Playing Options">
<CheckBoxPreference
android:key="boardFlipped"
android:title="Flip Board"
android:summary="View board from black side"
android:defaultValue="false">
</CheckBoxPreference>
<CheckBoxPreference
android:key="autoSwapSides"
android:title="Auto Swap Sides"
android:summary="Automatically swap sides when new game started. Also overrides Flip Board setting"
android:defaultValue="false">
</CheckBoxPreference>
</PreferenceCategory>
<PreferenceCategory
android:title="Engine Settings">
<ListPreference
android:key="engine"
android:title="Engine"
android:summary="Chess Engine"
android:entryValues="@array/engine_values"
android:entries="@array/engine_texts"
android:defaultValue="stockfish">
</ListPreference>
<org.petero.droidfish.SeekBarPreference
android:key="strength"
android:defaultValue="1000"
android:title="Strength">
</org.petero.droidfish.SeekBarPreference>
<CheckBoxPreference
android:key="ponderMode"
android:title="Pondering"
android:summary="Let engine think while waiting for opponent's move"
android:defaultValue="false">
</CheckBoxPreference>
<ListPreference
android:key="threads"
android:title="Threads"
android:summary="Number of engine threads (CPU cores) to use"
android:entryValues="@array/engine_threads_values"
android:entries="@array/engine_threads_texts"
android:defaultValue="0">
</ListPreference>
</PreferenceCategory>
<PreferenceCategory
android:title="Time Control">
<ListPreference
android:key="movesPerSession"
android:title="Moves"
android:summary="Number of moves between time controls"
android:entryValues="@array/moves_per_session_values"
android:entries="@array/moves_per_session_texts"
android:defaultValue="60">
</ListPreference>
<ListPreference
android:key="timeControl"
android:title="Time"
android:summary="Maximum thinking time between time controls"
android:entryValues="@array/time_control_values"
android:entries="@array/time_control_texts"
android:defaultValue="300000">
</ListPreference>
<ListPreference
android:key="timeIncrement"
android:title="Increment"
android:summary="Extra thinking time for each completed move"
android:entryValues="@array/time_increment_values"
android:entries="@array/time_increment_texts"
android:defaultValue="0">
</ListPreference>
</PreferenceCategory>
<PreferenceCategory
android:title="Hints">
<CheckBoxPreference
android:key="showThinking"
android:title="Show Computer Thinking"
android:summary="Display computer algorithm information during search"
android:defaultValue="false">
</CheckBoxPreference>
<CheckBoxPreference
android:key="whiteBasedScores"
android:title="White-based scores"
android:summary="Positive analysis scores are good for white"
android:defaultValue="false">
</CheckBoxPreference>
<CheckBoxPreference
android:key="bookHints"
android:title="Show Book Hints"
android:summary="Display opening book move hints for human player"
android:defaultValue="false">
</CheckBoxPreference>
<ListPreference
android:key="thinkingArrows"
android:title="Use Arrows"
android:summary="Use arrows to display moves on chess board"
android:entryValues="@array/thinking_arrows_values"
android:entries="@array/thinking_arrows_texts"
android:defaultValue="2">
</ListPreference>
</PreferenceCategory>
<PreferenceCategory
android:title="User Interface">
<CheckBoxPreference
android:key="animateMoves"
android:title="Animate Moves"
android:summary="Animate piece movements"
android:defaultValue="true">
</CheckBoxPreference>
<CheckBoxPreference
android:key="oneTouchMoves"
android:title="One Touch Moves"
android:summary="Touching a square that corresponds to one unique move makes that move immediately"
android:defaultValue="false">
</CheckBoxPreference>
<CheckBoxPreference
android:key="soundEnabled"
android:title="Enable Sounds"
android:summary="Play sound when computer makes a move"
android:defaultValue="false">
</CheckBoxPreference>
<CheckBoxPreference
android:key="fullScreenMode"
android:title="Fullscreen Mode"
android:summary="Fullscreen mode hides the status bar"
android:defaultValue="false">
</CheckBoxPreference>
<CheckBoxPreference
android:key="wakeLock"
android:title="Disable Screen Timeout"
android:defaultValue="false">
</CheckBoxPreference>
<CheckBoxPreference
android:key="drawSquareLabels"
android:title="Square Labels"
android:summary="Display square labels: a-h and 1-8"
android:defaultValue="false">
</CheckBoxPreference>
<ListPreference
android:key="scrollSensitivity"
android:title="Scrolling Speed"
android:summary="Scrolling speed for game navigation"
android:entryValues="@array/scroll_sensitivity_values"
android:entries="@array/scroll_sensitivity_texts"
android:defaultValue="2">
</ListPreference>
<CheckBoxPreference
android:key="invertScrollDirection"
android:title="Invert Scroll Direction"
android:summary="Enable this if you think scrolling moves in the wrong direction"
android:defaultValue="false">
</CheckBoxPreference>
<ListPreference
android:key="fontSize"
android:title="Text Size"
android:summary="Font size for move list and game information"
android:entryValues="@array/font_size_values"
android:entries="@array/font_size_texts"
android:defaultValue="12">
</ListPreference>
<CheckBoxPreference
android:key="largeButtons"
android:title="Large Buttons"
android:summary="Use large buttons for game navigation and mode setting"
android:defaultValue="false">
</CheckBoxPreference>
<PreferenceScreen
android:key="colors"
android:title="Color Settings"
android:summary="Change individual colors">
<PreferenceCategory
android:title="Chess Board">
<EditTextPreference
android:key="color_brightSquare"
android:title="Bright Squares"
android:defaultValue="#FFFFFFFA">
</EditTextPreference>
<EditTextPreference
android:key="color_darkSquare"
android:title="Dark Squares"
android:defaultValue="#FF83A5D2">
</EditTextPreference>
<EditTextPreference
android:key="color_selectedSquare"
android:title="Selected Square"
android:defaultValue="#FF3232D1">
</EditTextPreference>
<EditTextPreference
android:key="color_cursorSquare"
android:title="Trackball Cursor"
android:defaultValue="#FF5F5FFD">
</EditTextPreference>
<EditTextPreference
android:key="color_brightPiece"
android:title="White Pieces"
android:defaultValue="#FFF0F0F0">
</EditTextPreference>
<EditTextPreference
android:key="color_darkPiece"
android:title="Black Pieces"
android:defaultValue="#FF282828">
</EditTextPreference>
<EditTextPreference
android:key="color_arrow0"
android:title="Arrow 1"
android:defaultValue="#A01F1FFF">
</EditTextPreference>
<EditTextPreference
android:key="color_arrow1"
android:title="Arrow 2"
android:defaultValue="#A01FFF1F">
</EditTextPreference>
<EditTextPreference
android:key="color_arrow2"
android:title="Arrow 3"
android:defaultValue="#501F1FFF">
</EditTextPreference>
<EditTextPreference
android:key="color_arrow3"
android:title="Arrow 4"
android:defaultValue="#501FFF1F">
</EditTextPreference>
<EditTextPreference
android:key="color_arrow4"
android:title="Arrow 5"
android:defaultValue="#1E1F1FFF">
</EditTextPreference>
<EditTextPreference
android:key="color_arrow5"
android:title="Arrow 6"
android:defaultValue="#281FFF1F">
</EditTextPreference>
<EditTextPreference
android:key="color_squareLabel"
android:title="Square Labels"
android:defaultValue="#FFFF0000">
</EditTextPreference>
</PreferenceCategory>
<PreferenceCategory
android:title="Move List">
<EditTextPreference
android:key="color_currentMove"
android:title="Current Move"
android:defaultValue="#FF3333FF">
</EditTextPreference>
</PreferenceCategory>
</PreferenceScreen>
</PreferenceCategory>
<PreferenceCategory
android:title="Other">
<PreferenceScreen
android:key="bookSettings"
android:title="Opening Book Settings"
android:summary="Settings for opening book usage">
<ListPreference
android:key="bookMaxLength"
android:title="Book Length"
android:summary="Maximum book line length"
android:entryValues="@array/book_line_length_values"
android:entries="@array/book_line_length_texts"
android:defaultValue="1000000">
</ListPreference>
<CheckBoxPreference
android:key="bookPreferMainLines"
android:title="Prefer main lines"
android:summary="Prefer book moves marked as main moves"
android:defaultValue="false">
</CheckBoxPreference>
<CheckBoxPreference
android:key="bookTournamentMode"
android:title="Tournament mode"
android:summary="Ignore moves marked as not for tournament play"
android:defaultValue="false">
</CheckBoxPreference>
<org.petero.droidfish.SeekBarPreference
android:key="bookRandom"
android:defaultValue="500"
android:title="Book Randomization">
</org.petero.droidfish.SeekBarPreference>
<EditTextPreference
android:key="bookFile"
android:title="Book Filename"
android:summary="Polyglot or CTG book file in DroidFish directory on SD Card"
android:defaultValue="">
</EditTextPreference>
</PreferenceScreen>
<PreferenceScreen
android:key="pgnSettings"
android:title="PGN Settings"
android:summary="Settings for import and export of portable game notation (PGN) data">
<PreferenceCategory
android:title="PGN viewer">
<CheckBoxPreference
android:key="viewVariations"
android:title="Variations"
android:summary="Include non-mainline moves"
android:defaultValue="true">
</CheckBoxPreference>
<CheckBoxPreference
android:key="viewComments"
android:title="Comments"
android:summary="Include user written comments"
android:defaultValue="true">
</CheckBoxPreference>
<CheckBoxPreference
android:key="viewNAG"
android:title="Annotations"
android:summary="Include numeric annotation glyphs (NAGs), such as ! and ?"
android:defaultValue="true">
</CheckBoxPreference>
<CheckBoxPreference
android:key="viewHeaders"
android:title="Headers"
android:summary="Show PGN header lines"
android:defaultValue="false">
</CheckBoxPreference>
</PreferenceCategory>
<PreferenceCategory
android:title="PGN import">
<CheckBoxPreference
android:key="importVariations"
android:title="Variations"
android:summary="Include non-mainline moves"
android:defaultValue="true">
</CheckBoxPreference>
<CheckBoxPreference
android:key="importComments"
android:title="Comments"
android:summary="Include user written comments"
android:defaultValue="true">
</CheckBoxPreference>
<CheckBoxPreference
android:key="importNAG"
android:title="Annotations"
android:summary="Include numeric annotation glyphs (NAGs), such as ! and ?"
android:defaultValue="true">
</CheckBoxPreference>
</PreferenceCategory>
<PreferenceCategory
android:title="PGN export">
<CheckBoxPreference
android:key="exportVariations"
android:title="Variations"
android:summary="Include non-mainline moves"
android:defaultValue="true">
</CheckBoxPreference>
<CheckBoxPreference
android:key="exportComments"
android:title="Comments"
android:summary="Include user written comments"
android:defaultValue="true">
</CheckBoxPreference>
<CheckBoxPreference
android:key="exportNAG"
android:title="Annotations"
android:summary="Include numeric annotation glyphs (NAGs), such as ! and ?"
android:defaultValue="true">
</CheckBoxPreference>
<CheckBoxPreference
android:key="exportPlayerAction"
android:title="Player Actions"
android:summary="Include information about draw and resign actions"
android:defaultValue="false">
</CheckBoxPreference>
<CheckBoxPreference
android:key="exportTime"
android:title="Clock"
android:summary="Include time information for each move"
android:defaultValue="false">
</CheckBoxPreference>
</PreferenceCategory>
</PreferenceScreen>
</PreferenceCategory>
</PreferenceScreen>

View File

@ -0,0 +1,57 @@
/*
DroidFish - An Android chess program.
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.petero.droidfish;
/** Settings controlling opening book usage */
public class BookOptions {
public String filename = "";
public int maxLength = 1000000;
public boolean preferMainLines = false;
public boolean tournamentMode = false;
public double random = 0; // Scale probabilities according to p^(exp(-random))
public BookOptions() { }
public BookOptions(BookOptions other) {
filename = other.filename;
maxLength = other.maxLength;
preferMainLines = other.preferMainLines;
tournamentMode = other.tournamentMode;
random = other.random;
}
@Override
public boolean equals(Object o) {
if ((o == null) || (o.getClass() != this.getClass()))
return false;
BookOptions other = (BookOptions)o;
return ((filename.equals(other.filename)) &&
(maxLength == other.maxLength) &&
(preferMainLines == other.preferMainLines) &&
(tournamentMode == other.tournamentMode) &&
(random == other.random));
}
@Override
public int hashCode() {
return 0;
}
}

View File

@ -0,0 +1,665 @@
/*
DroidFish - An Android chess program.
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.petero.droidfish;
import java.util.ArrayList;
import java.util.List;
import org.petero.droidfish.gamelogic.Move;
import org.petero.droidfish.gamelogic.MoveGen;
import org.petero.droidfish.gamelogic.Piece;
import org.petero.droidfish.gamelogic.Position;
import org.petero.droidfish.gamelogic.UndoInfo;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class ChessBoard extends View {
public Position pos;
public int selectedSquare;
public float cursorX, cursorY;
public boolean cursorVisible;
protected int x0, y0, sqSize;
int pieceXDelta, pieceYDelta; // top/left pixel draw position relative to square
public boolean flipped;
public boolean drawSquareLabels;
boolean oneTouchMoves;
List<Move> moveHints;
protected Paint darkPaint;
protected Paint brightPaint;
private Paint selectedSquarePaint;
private Paint cursorSquarePaint;
private Paint whitePiecePaint;
private Paint blackPiecePaint;
private Paint labelPaint;
private ArrayList<Paint> moveMarkPaint;
public ChessBoard(Context context, AttributeSet attrs) {
super(context, attrs);
pos = new Position();
selectedSquare = -1;
cursorX = cursorY = 0;
cursorVisible = false;
x0 = y0 = sqSize = 0;
pieceXDelta = pieceYDelta = -1;
flipped = false;
drawSquareLabels = false;
oneTouchMoves = false;
darkPaint = new Paint();
brightPaint = new Paint();
selectedSquarePaint = new Paint();
selectedSquarePaint.setStyle(Paint.Style.STROKE);
selectedSquarePaint.setAntiAlias(true);
cursorSquarePaint = new Paint();
cursorSquarePaint.setStyle(Paint.Style.STROKE);
cursorSquarePaint.setAntiAlias(true);
whitePiecePaint = new Paint();
whitePiecePaint.setAntiAlias(true);
blackPiecePaint = new Paint();
blackPiecePaint.setAntiAlias(true);
labelPaint = new Paint();
labelPaint.setAntiAlias(true);
moveMarkPaint = new ArrayList<Paint>();
for (int i = 0; i < 6; i++) {
Paint p = new Paint();
p.setStyle(Paint.Style.FILL);
p.setAntiAlias(true);
moveMarkPaint.add(p);
}
Typeface chessFont = Typeface.createFromAsset(getContext().getAssets(), "ChessCases.ttf");
whitePiecePaint.setTypeface(chessFont);
blackPiecePaint.setTypeface(chessFont);
setColors();
}
/** Must be called for new color theme to take effect. */
final void setColors() {
ColorTheme ct = ColorTheme.instance();
darkPaint.setColor(ct.getColor(ColorTheme.DARK_SQUARE));
brightPaint.setColor(ct.getColor(ColorTheme.BRIGHT_SQUARE));
selectedSquarePaint.setColor(ct.getColor(ColorTheme.SELECTED_SQUARE));
cursorSquarePaint.setColor(ct.getColor(ColorTheme.CURSOR_SQUARE));
whitePiecePaint.setColor(ct.getColor(ColorTheme.BRIGHT_PIECE));
blackPiecePaint.setColor(ct.getColor(ColorTheme.DARK_PIECE));
labelPaint.setColor(ct.getColor(ColorTheme.SQUARE_LABEL));
for (int i = 0; i < 6; i++)
moveMarkPaint.get(i).setColor(ct.getColor(ColorTheme.ARROW_0 + i));
invalidate();
}
private Handler handlerTimer = new Handler();
final class AnimInfo {
AnimInfo() { startTime = -1; }
boolean paused;
long posHash; // Position the animation is valid for
long startTime; // Time in milliseconds when animation was started
long stopTime; // Time in milliseconds when animation should stop
long now; // Current time in milliseconds
int piece1, from1, to1, hide1;
int piece2, from2, to2, hide2;
public final boolean updateState() {
now = System.currentTimeMillis();
return animActive();
}
private final boolean animActive() {
if (paused || (startTime < 0) || (now >= stopTime) || (posHash != pos.zobristHash()))
return false;
return true;
}
public final boolean squareHidden(int sq) {
if (!animActive())
return false;
return (sq == hide1) || (sq == hide2);
}
public final void draw(Canvas canvas) {
if (!animActive())
return;
double animState = (now - startTime) / (double)(stopTime - startTime);
drawAnimPiece(canvas, piece2, from2, to2, animState);
drawAnimPiece(canvas, piece1, from1, to1, animState);
long now2 = System.currentTimeMillis();
long delay = 20 - (now2 - now);
// System.out.printf("delay:%d\n", delay);
if (delay < 1) delay = 1;
handlerTimer.postDelayed(new Runnable() {
@Override
public void run() {
invalidate();
}
}, delay);
}
private void drawAnimPiece(Canvas canvas, int piece, int from, int to, double animState) {
if (piece == Piece.EMPTY)
return;
final int xCrd1 = getXCrd(Position.getX(from));
final int yCrd1 = getYCrd(Position.getY(from));
final int xCrd2 = getXCrd(Position.getX(to));
final int yCrd2 = getYCrd(Position.getY(to));
final int xCrd = xCrd1 + (int)Math.round((xCrd2 - xCrd1) * animState);
final int yCrd = yCrd1 + (int)Math.round((yCrd2 - yCrd1) * animState);
drawPiece(canvas, xCrd, yCrd, piece);
}
}
AnimInfo anim = new AnimInfo();
/**
* Set up move animation. The animation will start the next time setPosition is called.
* @param sourcePos The source position for the animation.
* @param move The move leading to the target position.
* @param forward True if forward direction, false for undo move.
*/
public void setAnimMove(Position sourcePos, Move move, boolean forward) {
anim.startTime = -1;
anim.paused = true; // Animation starts at next position update
if (forward) {
// The animation will be played when pos == targetPos
Position targetPos = new Position(sourcePos);
UndoInfo ui = new UndoInfo();
targetPos.makeMove(move, ui);
anim.posHash = targetPos.zobristHash();
} else {
anim.posHash = sourcePos.zobristHash();
}
int animTime; // Animation duration in milliseconds.
{
int dx = Position.getX(move.to) - Position.getX(move.from);
int dy = Position.getY(move.to) - Position.getY(move.from);
double dist = Math.sqrt(dx * dx + dy * dy);
double t = Math.sqrt(dist) * 100;
animTime = (int)Math.round(t);
}
if (animTime > 0) {
anim.startTime = System.currentTimeMillis();
anim.stopTime = anim.startTime + animTime;
anim.piece2 = Piece.EMPTY;
anim.from2 = -1;
anim.to2 = -1;
anim.hide2 = -1;
if (forward) {
int p = sourcePos.getPiece(move.from);
anim.piece1 = p;
anim.from1 = move.from;
anim.to1 = move.to;
anim.hide1 = anim.to1;
int p2 = sourcePos.getPiece(move.to);
if (p2 != Piece.EMPTY) { // capture
anim.piece2 = p2;
anim.from2 = move.to;
anim.to2 = move.to;
} else if ((p == Piece.WKING) || (p == Piece.BKING)) {
boolean wtm = Piece.isWhite(p);
if (move.to == move.from + 2) { // O-O
anim.piece2 = wtm ? Piece.WROOK : Piece.BROOK;
anim.from2 = move.to + 1;
anim.to2 = move.to - 1;
anim.hide2 = anim.to2;
} else if (move.to == move.from - 2) { // O-O-O
anim.piece2 = wtm ? Piece.WROOK : Piece.BROOK;
anim.from2 = move.to - 2;
anim.to2 = move.to + 1;
anim.hide2 = anim.to2;
}
}
} else {
int p = sourcePos.getPiece(move.from);
anim.piece1 = p;
if (move.promoteTo != Piece.EMPTY)
anim.piece1 = Piece.isWhite(anim.piece1) ? Piece.WPAWN : Piece.BPAWN;
anim.from1 = move.to;
anim.to1 = move.from;
anim.hide1 = anim.to1;
if ((p == Piece.WKING) || (p == Piece.BKING)) {
boolean wtm = Piece.isWhite(p);
if (move.to == move.from + 2) { // O-O
anim.piece2 = wtm ? Piece.WROOK : Piece.BROOK;
anim.from2 = move.to - 1;
anim.to2 = move.to + 1;
anim.hide2 = anim.to2;
} else if (move.to == move.from - 2) { // O-O-O
anim.piece2 = wtm ? Piece.WROOK : Piece.BROOK;
anim.from2 = move.to + 1;
anim.to2 = move.to - 2;
anim.hide2 = anim.to2;
}
}
}
}
}
/**
* Set the board to a given state.
* @param pos
*/
final public void setPosition(Position pos) {
boolean doInvalidate = false;
if (anim.paused = true) {
anim.paused = false;
doInvalidate = true;
}
if (!this.pos.equals(pos)) {
this.pos = new Position(pos);
doInvalidate = true;
}
if (doInvalidate)
invalidate();
}
/**
* Set/clear the board flipped status.
* @param flipped
*/
final public void setFlipped(boolean flipped) {
if (this.flipped != flipped) {
this.flipped = flipped;
invalidate();
}
}
/**
* Set/clear the board flipped status.
* @param flipped
*/
final public void setDrawSquareLabels(boolean drawSquareLabels) {
if (this.drawSquareLabels != drawSquareLabels) {
this.drawSquareLabels = drawSquareLabels;
invalidate();
}
}
/**
* Set/clear the selected square.
* @param square The square to select, or -1 to clear selection.
*/
final public void setSelection(int square) {
if (square != selectedSquare) {
selectedSquare = square;
invalidate();
}
}
protected int getWidth(int sqSize) { return sqSize * 8 + 4; }
protected int getHeight(int sqSize) { return sqSize * 8 + 4; }
protected int getSqSizeW(int width) { return (width - 4) / 8; }
protected int getSqSizeH(int height) { return (height - 4) / 8; }
protected int getMaxHeightPercentage() { return 75; }
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
int sqSizeW = getSqSizeW(width);
int sqSizeH = getSqSizeH(height);
int sqSize = Math.min(sqSizeW, sqSizeH);
pieceXDelta = pieceYDelta = -1;
labelBounds = null;
if (height > width) {
int p = getMaxHeightPercentage();
height = Math.min(getHeight(sqSize), height * p / 100);
} else {
width = Math.min(getWidth(sqSize), width * 65 / 100);
}
setMeasuredDimension(width, height);
}
protected void computeOrigin(int width, int height) {
x0 = (width - sqSize * 8) / 2;
y0 = (height - sqSize * 8) / 2;
}
protected int getXFromSq(int sq) { return Position.getX(sq); }
protected int getYFromSq(int sq) { return Position.getY(sq); }
@Override
protected void onDraw(Canvas canvas) {
// long t0 = System.currentTimeMillis();
boolean animActive = anim.updateState();
final int width = getWidth();
final int height = getHeight();
sqSize = Math.min(getSqSizeW(width), getSqSizeH(height));
blackPiecePaint.setTextSize(sqSize);
whitePiecePaint.setTextSize(sqSize);
labelPaint.setTextSize(sqSize/4.0f);
computeOrigin(width, height);
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
final int xCrd = getXCrd(x);
final int yCrd = getYCrd(y);
Paint paint = Position.darkSquare(x, y) ? darkPaint : brightPaint;
canvas.drawRect(xCrd, yCrd, xCrd+sqSize, yCrd+sqSize, paint);
int sq = Position.getSquare(x, y);
if (!animActive || !anim.squareHidden(sq)) {
int p = pos.getPiece(sq);
drawPiece(canvas, xCrd, yCrd, p);
}
if (drawSquareLabels) {
if (x == (flipped ? 7 : 0)) {
drawLabel(canvas, xCrd, yCrd, false, false, "12345678".charAt(y));
}
if (y == (flipped ? 7 : 0)) {
drawLabel(canvas, xCrd, yCrd, true, true, "abcdefgh".charAt(x));
}
}
}
}
drawExtraSquares(canvas);
if (!animActive && (selectedSquare != -1)) {
int selX = getXFromSq(selectedSquare);
int selY = getYFromSq(selectedSquare);
selectedSquarePaint.setStrokeWidth(sqSize/(float)16);
int x0 = getXCrd(selX);
int y0 = getYCrd(selY);
canvas.drawRect(x0, y0, x0 + sqSize, y0 + sqSize, selectedSquarePaint);
}
if (cursorVisible) {
int x = Math.round(cursorX);
int y = Math.round(cursorY);
int x0 = getXCrd(x);
int y0 = getYCrd(y);
cursorSquarePaint.setStrokeWidth(sqSize/(float)16);
canvas.drawRect(x0, y0, x0 + sqSize, y0 + sqSize, cursorSquarePaint);
}
if (!animActive)
drawMoveHints(canvas);
anim.draw(canvas);
// long t1 = System.currentTimeMillis();
// System.out.printf("draw: %d\n", t1-t0);
}
private final void drawMoveHints(Canvas canvas) {
if (moveHints == null)
return;
float h = (float)(sqSize / 2.0);
float d = (float)(sqSize / 8.0);
double v = 35 * Math.PI / 180;
double cosv = Math.cos(v);
double sinv = Math.sin(v);
double tanv = Math.tan(v);
int n = Math.min(moveMarkPaint.size(), moveHints.size());
for (int i = 0; i < n; i++) {
Move m = moveHints.get(i);
if (m.from == m.to)
continue;
float x0 = getXCrd(Position.getX(m.from)) + h;
float y0 = getYCrd(Position.getY(m.from)) + h;
float x1 = getXCrd(Position.getX(m.to)) + h;
float y1 = getYCrd(Position.getY(m.to)) + h;
float x2 = (float)(Math.hypot(x1 - x0, y1 - y0) + d);
float y2 = 0;
float x3 = (float)(x2 - h * cosv);
float y3 = (float)(y2 - h * sinv);
float x4 = (float)(x3 - d * sinv);
float y4 = (float)(y3 + d * cosv);
float x5 = (float)(x4 + (-d/2 - y4) / tanv);
float y5 = (float)(-d / 2);
float x6 = 0;
float y6 = y5 / 2;
Path path = new Path();
path.moveTo(x2, y2);
path.lineTo(x3, y3);
// path.lineTo(x4, y4);
path.lineTo(x5, y5);
path.lineTo(x6, y6);
path.lineTo(x6, -y6);
path.lineTo(x5, -y5);
// path.lineTo(x4, -y4);
path.lineTo(x3, -y3);
path.close();
Matrix mtx = new Matrix();
mtx.postRotate((float)(Math.atan2(y1 - y0, x1 - x0) * 180 / Math.PI));
mtx.postTranslate(x0, y0);
path.transform(mtx);
Paint p = moveMarkPaint.get(i);
canvas.drawPath(path, p);
}
}
protected void drawExtraSquares(Canvas canvas) {
}
protected final void drawPiece(Canvas canvas, int xCrd, int yCrd, int p) {
String psb, psw;
boolean rotate = false;
switch (p) {
default:
case Piece.EMPTY: psb = null; psw = null; break;
case Piece.WKING: psb = "H"; psw = "k"; break;
case Piece.WQUEEN: psb = "I"; psw = "l"; break;
case Piece.WROOK: psb = "J"; psw = "m"; break;
case Piece.WBISHOP: psb = "K"; psw = "n"; break;
case Piece.WKNIGHT: psb = "L"; psw = "o"; break;
case Piece.WPAWN: psb = "M"; psw = "p"; break;
case Piece.BKING: psb = "N"; psw = "q"; rotate = true; break;
case Piece.BQUEEN: psb = "O"; psw = "r"; rotate = true; break;
case Piece.BROOK: psb = "P"; psw = "s"; rotate = true; break;
case Piece.BBISHOP: psb = "Q"; psw = "t"; rotate = true; break;
case Piece.BKNIGHT: psb = "R"; psw = "u"; rotate = true; break;
case Piece.BPAWN: psb = "S"; psw = "v"; rotate = true; break;
}
if (psb != null) {
if (pieceXDelta < 0) {
Rect bounds = new Rect();
blackPiecePaint.getTextBounds("H", 0, 1, bounds);
pieceXDelta = (sqSize - (bounds.left + bounds.right)) / 2;
pieceYDelta = (sqSize - (bounds.top + bounds.bottom)) / 2;
}
rotate ^= flipped;
rotate = false; // Disabled for now
if (rotate) {
canvas.save();
canvas.rotate(180, xCrd + sqSize * 0.5f, yCrd + sqSize * 0.5f);
}
xCrd += pieceXDelta;
yCrd += pieceYDelta;
canvas.drawText(psw, xCrd, yCrd, whitePiecePaint);
canvas.drawText(psb, xCrd, yCrd, blackPiecePaint);
if (rotate)
canvas.restore();
}
}
private Rect labelBounds = null;
private void drawLabel(Canvas canvas, int xCrd, int yCrd, boolean right,
boolean bottom, char c) {
String s = "";
s += c;
if (labelBounds == null) {
labelBounds = new Rect();
labelPaint.getTextBounds("f", 0, 1, labelBounds);
}
int margin = sqSize / 16;
if (right) {
xCrd += sqSize - labelBounds.right - margin;
} else {
xCrd += -labelBounds.left + margin;
}
if (bottom) {
yCrd += sqSize - labelBounds.bottom - margin;
} else {
yCrd += -labelBounds.top + margin;
}
canvas.drawText(s, xCrd, yCrd, labelPaint);
}
protected int getXCrd(int x) { return x0 + sqSize * (flipped ? 7 - x : x); }
protected int getYCrd(int y) { return y0 + sqSize * (flipped ? y : 7 - y); }
protected int getXSq(int xCrd) { int t = (xCrd - x0) / sqSize; return flipped ? 7 - t : t; }
protected int getYSq(int yCrd) { int t = (yCrd - y0) / sqSize; return flipped ? t : 7 - t; }
/**
* Compute the square corresponding to the coordinates of a mouse event.
* @param evt Details about the mouse event.
* @return The square corresponding to the mouse event, or -1 if outside board.
*/
public int eventToSquare(MotionEvent evt) {
int xCrd = (int)(evt.getX());
int yCrd = (int)(evt.getY());
int sq = -1;
if (sqSize > 0) {
int x = getXSq(xCrd);
int y = getYSq(yCrd);
if ((x >= 0) && (x < 8) && (y >= 0) && (y < 8)) {
sq = Position.getSquare(x, y);
}
}
return sq;
}
final private boolean myColor(int piece) {
return (piece != Piece.EMPTY) && (Piece.isWhite(piece) == pos.whiteMove);
}
public Move mousePressed(int sq) {
if (sq < 0)
return null;
cursorVisible = false;
if (selectedSquare != -1) {
int p = pos.getPiece(selectedSquare);
if (!myColor(p)) {
setSelection(-1); // Remove selection of opponents last moving piece
}
}
int p = pos.getPiece(sq);
if (selectedSquare != -1) {
if (sq != selectedSquare) {
if (!myColor(p)) {
Move m = new Move(selectedSquare, sq, Piece.EMPTY);
setSelection(sq);
return m;
}
}
setSelection(-1);
} else {
if (oneTouchMoves) {
ArrayList<Move> moves = new MoveGen().pseudoLegalMoves(pos);
moves = MoveGen.removeIllegal(pos, moves);
Move matchingMove = null;
int toSq = -1;
for (Move m : moves) {
if ((m.from == sq) || (m.to == sq)) {
if (matchingMove == null) {
matchingMove = m;
toSq = m.to;
} else {
matchingMove = null;
break;
}
}
}
if (matchingMove != null) {
setSelection(toSq);
return matchingMove;
}
}
if (myColor(p)) {
setSelection(sq);
}
}
return null;
}
public static class OnTrackballListener {
public void onTrackballEvent(MotionEvent event) { }
}
private OnTrackballListener otbl = null;
public final void setOnTrackballListener(OnTrackballListener onTrackballListener) {
otbl = onTrackballListener;
}
@Override
public boolean onTrackballEvent(MotionEvent event) {
if (otbl != null) {
otbl.onTrackballEvent(event);
return true;
}
return false;
}
protected int minValidY() { return 0; }
protected int getSquare(int x, int y) { return Position.getSquare(x, y); }
public final Move handleTrackballEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
invalidate();
if (cursorVisible) {
int x = Math.round(cursorX);
int y = Math.round(cursorY);
cursorX = x;
cursorY = y;
int sq = getSquare(x, y);
return mousePressed(sq);
}
return null;
}
cursorVisible = true;
int c = flipped ? -1 : 1;
cursorX += c * event.getX();
cursorY -= c * event.getY();
if (cursorX < 0) cursorX = 0;
if (cursorX > 7) cursorX = 7;
if (cursorY < minValidY()) cursorY = minValidY();
if (cursorY > 7) cursorY = 7;
invalidate();
return null;
}
public final void setMoveHints(List<Move> moveHints) {
boolean equal = false;
if ((this.moveHints == null) || (moveHints == null)) {
equal = this.moveHints == moveHints;
} else {
equal = this.moveHints.equals(moveHints);
}
if (!equal) {
this.moveHints = moveHints;
invalidate();
}
}
}

View File

@ -0,0 +1,105 @@
/*
DroidFish - An Android chess program.
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.petero.droidfish;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.Color;
public class ColorTheme {
private static ColorTheme inst = null;
/** Get singleton instance. */
static final ColorTheme instance() {
if (inst == null)
inst = new ColorTheme();
return inst;
}
final static int DARK_SQUARE = 0;
final static int BRIGHT_SQUARE = 1;
final static int SELECTED_SQUARE = 2;
final static int CURSOR_SQUARE = 3;
final static int DARK_PIECE = 4;
final static int BRIGHT_PIECE = 5;
final static int CURRENT_MOVE = 6;
final static int ARROW_0 = 7;
final static int ARROW_1 = 8;
final static int ARROW_2 = 9;
final static int ARROW_3 = 10;
final static int ARROW_4 = 11;
final static int ARROW_5 = 12;
final static int SQUARE_LABEL = 13;
private final static int numColors = 14;
private int colorTable[] = new int[numColors];
private static final String[] prefNames = {
"darkSquare", "brightSquare", "selectedSquare", "cursorSquare", "darkPiece", "brightPiece", "currentMove",
"arrow0", "arrow1", "arrow2", "arrow3", "arrow4", "arrow5", "squareLabel"
};
private static final String prefPrefix = "color_";
private final static int defaultTheme = 2;
final static String[] themeNames = { "Original", "XBoard", "Blue", "Grey" };
private final static String themeColors[][] = {
{
"#FF808080", "#FFBEBE5A", "#FFFF0000", "#FF00FF00", "#FF000000", "#FFFFFFFF", "#FF888888",
"#A01F1FFF", "#A0FF1F1F", "#501F1FFF", "#50FF1F1F", "#1E1F1FFF", "#28FF1F1F", "#FFFF0000"
},
{
"#FF77A26D", "#FFC8C365", "#FFFFFF00", "#FF00FF00", "#FF202020", "#FFFFFFCC", "#FF6B9262",
"#A01F1FFF", "#A0FF1F1F", "#501F1FFF", "#50FF1F1F", "#1E1F1FFF", "#28FF1F1F", "#FFFF0000"
},
{
"#FF83A5D2", "#FFFFFFFA", "#FF3232D1", "#FF5F5FFD", "#FF282828", "#FFF0F0F0", "#FF3333FF",
"#A01F1FFF", "#A01FFF1F", "#501F1FFF", "#501FFF1F", "#1E1F1FFF", "#281FFF1F", "#FFFF0000"
},
{
"#FF666666", "#FFDDDDDD", "#FFFF0000", "#FF0000FF", "#FF000000", "#FFFFFFFF", "#FF888888",
"#A01F1FFF", "#A0FF1F1F", "#501F1FFF", "#50FF1F1F", "#1E1F1FFF", "#28FF1F1F", "#FFFF0000"
}
};
final void readColors(SharedPreferences settings) {
for (int i = 0; i < numColors; i++) {
String prefName = prefPrefix + prefNames[i];
String defaultColor = themeColors[defaultTheme][i];
String colorString = settings.getString(prefName, defaultColor);
colorTable[i] = 0;
try {
colorTable[i] = Color.parseColor(colorString);
} catch (IllegalArgumentException e) {
} catch (StringIndexOutOfBoundsException e) {
}
}
}
final void setTheme(SharedPreferences settings, int themeType) {
Editor editor = settings.edit();
for (int i = 0; i < numColors; i++)
editor.putString(prefPrefix + prefNames[i], themeColors[themeType][i]);
editor.commit();
readColors(settings);
}
final int getColor(int colorType) {
return colorTable[colorType];
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,72 @@
/*
DroidFish - An Android chess program.
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.petero.droidfish;
import java.util.ArrayList;
import java.util.List;
import org.petero.droidfish.gamelogic.Move;
import org.petero.droidfish.gamelogic.Position;
/** Interface between the GUI and the ChessController. */
public interface GUIInterface {
/** Update the displayed board position. */
public void setPosition(Position pos, String variantInfo, List<Move> variantMoves);
/** Mark square i as selected. Set to -1 to clear selection. */
public void setSelection(int sq);
/** Set the status text. */
public void setStatusString(String str);
/** Update the list of moves. */
public void moveListUpdated();
/** Update the computer thinking information. */
public void setThinkingInfo(String pvStr, String bookInfo, ArrayList<ArrayList<Move>> pvMoves, List<Move> bookMoves);
/** Ask what to promote a pawn to. Should call reportPromotePiece() when done. */
public void requestPromotePiece();
/** Run code on the GUI thread. */
public void runOnUIThread(Runnable runnable);
/** Report that user attempted to make an invalid move. */
public void reportInvalidMove(Move m);
/** Called when computer made a move. GUI can notify user, for example by playing a sound. */
public void computerMoveMade();
/** Report remaining thinking time to GUI. */
public void setRemainingTime(long wTime, long bTime, long nextUpdate);
/** Report a move made that is a candidate for GUI animation. */
public void setAnimMove(Position sourcePos, Move move, boolean forward);
/** Return true if positive analysis scores means good for white. */
public boolean whiteBasedScores();
/** Return true if pondering (permanent brain) is enabled. */
public boolean ponderMode();
/** Return the number of engine threads to use. */
int engineThreads();
}

View File

@ -0,0 +1,111 @@
/*
DroidFish - An Android chess program.
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.petero.droidfish;
public class GameMode {
private final boolean playerWhite;
private final boolean playerBlack;
private final boolean analysisMode;
private final boolean clocksActive;
public static final int PLAYER_WHITE = 1;
public static final int PLAYER_BLACK = 2;
public static final int TWO_PLAYERS = 3;
public static final int ANALYSIS = 4;
public static final int TWO_COMPUTERS = 5;
public static final int EDIT_GAME = 6;
public GameMode(int modeNr) {
switch (modeNr) {
case PLAYER_WHITE: default:
playerWhite = true;
playerBlack = false;
analysisMode = false;
clocksActive = true;
break;
case PLAYER_BLACK:
playerWhite = false;
playerBlack = true;
analysisMode = false;
clocksActive = true;
break;
case TWO_PLAYERS:
playerWhite = true;
playerBlack = true;
analysisMode = false;
clocksActive = true;
break;
case ANALYSIS:
playerWhite = true;
playerBlack = true;
analysisMode = true;
clocksActive = false;
break;
case TWO_COMPUTERS:
playerWhite = false;
playerBlack = false;
analysisMode = false;
clocksActive = true;
break;
case EDIT_GAME:
playerWhite = true;
playerBlack = true;
analysisMode = false;
clocksActive = false;
break;
}
}
public final boolean playerWhite() {
return playerWhite;
}
public final boolean playerBlack() {
return playerBlack;
}
public final boolean analysisMode() {
return analysisMode;
}
public final boolean humansTurn(boolean whiteMove) {
return (whiteMove ? playerWhite : playerBlack) || analysisMode;
}
public final boolean clocksActive() {
return clocksActive;
}
@Override
public boolean equals(Object o) {
if ((o == null) || (o.getClass() != this.getClass()))
return false;
GameMode other = (GameMode)o;
if (playerWhite != other.playerWhite)
return false;
if (playerBlack != other.playerBlack)
return false;
if (analysisMode != other.analysisMode)
return false;
if (clocksActive != other.clocksActive)
return false;
return true;
}
@Override
public int hashCode() {
return 0;
}
}

View File

@ -0,0 +1,27 @@
package org.petero.droidfish;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ScrollView;
/** A ScrollView that uses at most 75% of the parent height. */
public class MyScrollView extends ScrollView {
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
if (getParent() instanceof View) {
int parentHeight = ((View)getParent()).getHeight();
if (parentHeight > 0)
height = Math.min(height, parentHeight * 3 / 4);
}
setMeasuredDimension(width, height);
}
}

View File

@ -0,0 +1,54 @@
/*
DroidFish - An Android chess program.
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.petero.droidfish;
/** Settings controlling PGN import/export */
public class PGNOptions {
public static class Viewer {
public boolean variations;
public boolean comments;
public boolean nag;
public boolean headers;
}
public static class Import {
public boolean variations;
public boolean comments;
public boolean nag;
}
public static class Export {
public boolean variations;
public boolean comments;
public boolean nag;
public boolean playerAction;
public boolean clockInfo;
public boolean pgnPromotions;
public boolean moveNrAfterNag;
}
public Viewer view;
public Import imp;
public Export exp;
public PGNOptions() {
view = new Viewer();
imp = new Import();
exp = new Export();
exp.moveNrAfterNag = true;
}
}

View File

@ -0,0 +1,183 @@
package org.petero.droidfish;
import android.app.Dialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.preference.Preference;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.SeekBar.OnSeekBarChangeListener;
/** Lets user enter a percentage value using a seek bar. */
public class SeekBarPreference extends Preference
implements OnSeekBarChangeListener {
private final static int maxValue = 1000;
private final static int DEFAULT_VALUE = 1000;
private int currVal = DEFAULT_VALUE;
private TextView currValBox;
public SeekBarPreference(Context context) {
super(context);
}
public SeekBarPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SeekBarPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected View onCreateView(ViewGroup parent) {
TextView name = new TextView(getContext());
name.setText(getTitle());
name.setTextSize(20);
name.setTypeface(Typeface.SANS_SERIF, Typeface.BOLD);
name.setGravity(Gravity.LEFT);
LinearLayout.LayoutParams lp =
new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.LEFT;
lp.weight = 1.0f;
name.setLayoutParams(lp);
currValBox = new TextView(getContext());
currValBox.setTextSize(12);
currValBox.setTypeface(Typeface.MONOSPACE, Typeface.ITALIC);
currValBox.setPadding(2, 5, 0, 0);
currValBox.setText(valToString());
lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.CENTER;
currValBox.setLayoutParams(lp);
LinearLayout row1 = new LinearLayout(getContext());
row1.setOrientation(LinearLayout.HORIZONTAL);
row1.addView(name);
row1.addView(currValBox);
final SeekBar bar = new SeekBar(getContext());
bar.setMax(maxValue);
bar.setProgress(currVal);
bar.setOnSeekBarChangeListener(this);
lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.RIGHT;
bar.setLayoutParams(lp);
LinearLayout layout = new LinearLayout(getContext());
layout.setPadding(25, 5, 25, 5);
layout.setOrientation(LinearLayout.VERTICAL);
layout.addView(row1);
layout.addView(bar);
layout.setId(android.R.id.widget_frame);
currValBox.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
final Dialog dialog = new Dialog(getContext());
dialog.setContentView(R.layout.select_percentage);
String title = "";
String key = getKey();
if (key.equals("strength")) {
title = getContext().getString(R.string.edit_strength);
} else if (key.equals("bookRandom")) {
title = getContext().getString(R.string.edit_randomization);
}
dialog.setTitle(title);
final EditText valueView = (EditText)dialog.findViewById(R.id.selpercentage_number);
Button ok = (Button)dialog.findViewById(R.id.selpercentage_ok);
Button cancel = (Button)dialog.findViewById(R.id.selpercentage_cancel);
valueView.setText(currValBox.getText().toString().replaceAll("%", ""));
final Runnable selectValue = new Runnable() {
public void run() {
try {
String txt = valueView.getText().toString();
int value = (int)(Double.parseDouble(txt) * 10 + 0.5);
if (value < 0) value = 0;
if (value > maxValue) value = maxValue;
dialog.cancel();
onProgressChanged(bar, value, false);
} catch (NumberFormatException nfe) {
}
}
};
valueView.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
selectValue.run();
return true;
}
return false;
}
});
ok.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
selectValue.run();
}
});
cancel.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
dialog.cancel();
}
});
dialog.show();
}
});
return layout;
}
private final String valToString() {
return String.format("%.1f%%", currVal*0.1);
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,boolean fromUser) {
if (!callChangeListener(progress)) {
seekBar.setProgress(currVal);
return;
}
seekBar.setProgress(progress);
currVal = progress;
currValBox.setText(valToString());
SharedPreferences.Editor editor = getEditor();
editor.putInt(getKey(), progress);
editor.commit();
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
notifyChanged();
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
int defVal = a.getInt(index, DEFAULT_VALUE);
if (defVal > maxValue) defVal = maxValue;
if (defVal < 0) defVal = 0;
return defVal;
}
@Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
int val = restorePersistedValue ? getPersistedInt(DEFAULT_VALUE) : (Integer)defaultValue;
if (!restorePersistedValue)
persistInt(val);
currVal = val;
}
}

View File

@ -0,0 +1,56 @@
/*
DroidFish - An Android chess program.
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.petero.droidfish.activities;
import org.petero.droidfish.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnDismissListener;
import android.os.Bundle;
public class CPUWarning extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.cpu_warning);
showDialog(CPU_WARNING_DIALOG);
}
static final int CPU_WARNING_DIALOG = 1;
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case CPU_WARNING_DIALOG:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.app_name).setMessage(R.string.cpu_warning);
AlertDialog alert = builder.create();
alert.setOnDismissListener(new OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
finish();
}
});
return alert;
}
return null;
}
}

View File

@ -0,0 +1,210 @@
/*
DroidFish - An Android chess program.
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.petero.droidfish.activities;
import org.petero.droidfish.ChessBoard;
import org.petero.droidfish.gamelogic.Move;
import org.petero.droidfish.gamelogic.Piece;
import org.petero.droidfish.gamelogic.Position;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
/**
* Chess board widget suitable for edit mode.
* @author petero
*/
public class ChessBoardEdit extends ChessBoard {
public ChessBoardEdit(Context context, AttributeSet attrs) {
super(context, attrs);
drawSquareLabels = true;
}
private final static int gap = 2;
@Override
protected int getWidth(int sqSize) { return sqSize * 8 + 4; }
@Override
protected int getHeight(int sqSize) { return sqSize * 10 + 4 + gap; }
@Override
protected int getSqSizeW(int width) { return (width - 4) / 8; }
@Override
protected int getSqSizeH(int height) { return (height - 4 - gap) / 10; }
@Override
protected int getMaxHeightPercentage() { return 85; }
@Override
protected void computeOrigin(int width, int height) {
x0 = (width - sqSize * 8) / 2;
y0 = (height - (sqSize * 10 + gap)) / 2;
}
int extraPieces(int x, int y) {
if (y == -1) { // White pieces
switch (x) {
case 0: return Piece.WKING;
case 1: return Piece.WQUEEN;
case 2: return Piece.WROOK;
case 3: return Piece.WBISHOP;
case 4: return Piece.WKNIGHT;
case 5: return Piece.WPAWN;
}
} else if (y == -2) {
switch (x) {
case 0: return Piece.BKING;
case 1: return Piece.BQUEEN;
case 2: return Piece.BROOK;
case 3: return Piece.BBISHOP;
case 4: return Piece.BKNIGHT;
case 5: return Piece.BPAWN;
}
}
return Piece.EMPTY;
}
@Override
protected int getXFromSq(int sq) {
if (sq >= 0) {
return Position.getX(sq);
} else {
int p = -2 - sq;
switch (p) {
case Piece.WKING: case Piece.BKING: return 0;
case Piece.WQUEEN: case Piece.BQUEEN: return 1;
case Piece.WROOK: case Piece.BROOK: return 2;
case Piece.WBISHOP: case Piece.BBISHOP: return 3;
case Piece.WKNIGHT: case Piece.BKNIGHT: return 4;
case Piece.WPAWN: case Piece.BPAWN: return 5;
default: return 6;
}
}
}
@Override
protected int getYFromSq(int sq) {
if (sq >= 0) {
return Position.getY(sq);
} else {
int p = -2 - sq;
return Piece.isWhite(p) ? -1 : -2;
}
}
@Override
protected int getSquare(int x, int y) {
if (y >= 0) {
return Position.getSquare(x, y);
} else {
int p = extraPieces(x, y);
return -p - 2;
}
}
@Override
protected void drawExtraSquares(Canvas canvas) {
for (int x = 0; x < 8; x++) {
for (int y = -2; y < 0; y++) {
final int xCrd = getXCrd(x);
final int yCrd = getYCrd(y);
Paint paint = Position.darkSquare(x, y) ? darkPaint : brightPaint;
canvas.drawRect(xCrd, yCrd, xCrd+sqSize, yCrd+sqSize, paint);
int p = extraPieces(x, y);
drawPiece(canvas, xCrd, yCrd, p);
}
}
}
@Override
public
Move mousePressed(int sq) {
if (sq == -1)
return null;
cursorVisible = false;
if (selectedSquare != -1) {
if (sq != selectedSquare) {
Move m = new Move(selectedSquare, sq, Piece.EMPTY);
setSelection(sq);
return m;
}
setSelection(-1);
} else {
setSelection(sq);
}
return null;
}
@Override
protected int minValidY() { return -2; }
@Override
protected int getXCrd(int x) {
return x0 + sqSize * (flipped ? 7 - x : x);
}
@Override
protected int getYCrd(int y) {
if (y >= 0) {
return y0 + sqSize * (flipped ? y : 7 - y);
} else {
return y0 + gap + sqSize * (7 - y);
}
}
@Override
protected int getXSq(int xCrd) {
int t = (xCrd - x0) / sqSize; return flipped ? 7 - t : t;
}
@Override
protected int getYSq(int yCrd) {
int t = (yCrd - y0) / sqSize;
t = flipped ? t : 7 - t;
if ((t >= 0) && (t < 8))
return t;
return 7 - (yCrd - y0 - gap) / sqSize;
}
/**
* Compute the square corresponding to the coordinates of a mouse event.
* @param evt Details about the mouse event.
* @return The square corresponding to the mouse event, or -1 if outside board.
*/
@Override
public int eventToSquare(MotionEvent evt) {
int sq = super.eventToSquare(evt);
if (sq != -1)
return sq;
int xCrd = (int)(evt.getX());
int yCrd = (int)(evt.getY());
if (sqSize > 0) {
int x = getXSq(xCrd);
int y = getYSq(yCrd);
if ((x >= 0) && (x < 8) && (y >= -2) && (y < 0)) {
int p = extraPieces(x, y);
sq = -p - 2;
}
}
return sq;
}
}

View File

@ -0,0 +1,427 @@
/*
DroidFish - An Android chess program.
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.petero.droidfish.activities;
import org.petero.droidfish.ChessBoard;
import org.petero.droidfish.R;
import org.petero.droidfish.gamelogic.ChessParseError;
import org.petero.droidfish.gamelogic.Move;
import org.petero.droidfish.gamelogic.Piece;
import org.petero.droidfish.gamelogic.Position;
import org.petero.droidfish.gamelogic.TextIO;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.text.ClipboardManager;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class EditBoard extends Activity {
private ChessBoardEdit cb;
private TextView status;
private Button okButton;
private Button cancelButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initUI();
Intent i = getIntent();
Position pos;
try {
pos = TextIO.readFEN(i.getAction());
cb.setPosition(pos);
} catch (ChessParseError e) {
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
ChessBoardEdit oldCB = cb;
String statusStr = status.getText().toString();
initUI();
cb.cursorX = oldCB.cursorX;
cb.cursorY = oldCB.cursorY;
cb.cursorVisible = oldCB.cursorVisible;
cb.setPosition(oldCB.pos);
cb.setSelection(oldCB.selectedSquare);
status.setText(statusStr);
}
private final void initUI() {
setContentView(R.layout.editboard);
cb = (ChessBoardEdit)findViewById(R.id.eb_chessboard);
status = (TextView)findViewById(R.id.eb_status);
okButton = (Button)findViewById(R.id.eb_ok);
cancelButton = (Button)findViewById(R.id.eb_cancel);
okButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
sendBackResult();
}
});
cancelButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
setResult(RESULT_CANCELED);
finish();
}
});
status.setFocusable(false);
cb.setFocusable(true);
cb.requestFocus();
cb.setClickable(true);
cb.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
int sq = cb.eventToSquare(event);
Move m = cb.mousePressed(sq);
if (m != null) {
doMove(m);
}
return false;
}
return false;
}
});
cb.setOnTrackballListener(new ChessBoard.OnTrackballListener() {
public void onTrackballEvent(MotionEvent event) {
Move m = cb.handleTrackballEvent(event);
if (m != null) {
doMove(m);
}
}
});
cb.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
showDialog(EDIT_DIALOG);
return true;
}
});
}
private void doMove(Move m) {
if (m.to < 0) {
if ((m.from < 0) || (cb.pos.getPiece(m.from) == Piece.EMPTY)) {
cb.setSelection(m.to);
return;
}
}
Position pos = new Position(cb.pos);
int piece = Piece.EMPTY;
if (m.from >= 0) {
piece = pos.getPiece(m.from);
} else {
piece = -(m.from + 2);
}
if (m.to >= 0)
pos.setPiece(m.to, piece);
if (m.from >= 0)
pos.setPiece(m.from, Piece.EMPTY);
cb.setPosition(pos);
cb.setSelection(-1);
checkValid();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
sendBackResult();
return true;
}
return super.onKeyDown(keyCode, event);
}
private final void sendBackResult() {
if (checkValid()) {
setPosFields();
String fen = TextIO.toFEN(cb.pos);
setResult(RESULT_OK, (new Intent()).setAction(fen));
} else {
setResult(RESULT_CANCELED);
}
finish();
}
private final void setPosFields() {
setEPFile(getEPFile()); // To handle sideToMove change
TextIO.fixupEPSquare(cb.pos);
TextIO.removeBogusCastleFlags(cb.pos);
}
private final int getEPFile() {
int epSquare = cb.pos.getEpSquare();
if (epSquare < 0) return 8;
return Position.getX(epSquare);
}
private final void setEPFile(int epFile) {
int epSquare = -1;
if ((epFile >= 0) && (epFile < 8)) {
int epRank = cb.pos.whiteMove ? 5 : 2;
epSquare = Position.getSquare(epFile, epRank);
}
cb.pos.setEpSquare(epSquare);
}
/** Test if a position is valid. */
private final boolean checkValid() {
try {
String fen = TextIO.toFEN(cb.pos);
TextIO.readFEN(fen);
status.setText("");
return true;
} catch (ChessParseError e) {
status.setText(e.getMessage());
}
return false;
}
static final int EDIT_DIALOG = 0;
static final int SIDE_DIALOG = 1;
static final int CASTLE_DIALOG = 2;
static final int EP_DIALOG = 3;
static final int MOVCNT_DIALOG = 4;
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case EDIT_DIALOG: {
final CharSequence[] items = {
getString(R.string.side_to_move),
getString(R.string.clear_board), getString(R.string.initial_position),
getString(R.string.castling_flags), getString(R.string.en_passant_file),
getString(R.string.move_counters),
getString(R.string.copy_position), getString(R.string.paste_position)
};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.edit_board);
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
switch (item) {
case 0: // Edit side to move
showDialog(SIDE_DIALOG);
cb.setSelection(-1);
checkValid();
break;
case 1: { // Clear board
Position pos = new Position();
cb.setPosition(pos);
cb.setSelection(-1);
checkValid();
break;
}
case 2: { // Set initial position
try {
Position pos = TextIO.readFEN(TextIO.startPosFEN);
cb.setPosition(pos);
cb.setSelection(-1);
checkValid();
} catch (ChessParseError e) {
}
break;
}
case 3: // Edit castling flags
removeDialog(CASTLE_DIALOG);
showDialog(CASTLE_DIALOG);
cb.setSelection(-1);
checkValid();
break;
case 4: // Edit en passant file
removeDialog(EP_DIALOG);
showDialog(EP_DIALOG);
cb.setSelection(-1);
checkValid();
break;
case 5: // Edit move counters
removeDialog(MOVCNT_DIALOG);
showDialog(MOVCNT_DIALOG);
cb.setSelection(-1);
checkValid();
break;
case 6: { // Copy position
setPosFields();
String fen = TextIO.toFEN(cb.pos) + "\n";
ClipboardManager clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE);
clipboard.setText(fen);
cb.setSelection(-1);
break;
}
case 7: { // Paste position
ClipboardManager clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE);
if (clipboard.hasText()) {
String fen = clipboard.getText().toString();
try {
Position pos = TextIO.readFEN(fen);
cb.setPosition(pos);
} catch (ChessParseError e) {
if (e.pos != null)
cb.setPosition(e.pos);
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
}
cb.setSelection(-1);
checkValid();
}
break;
}
}
}
});
AlertDialog alert = builder.create();
return alert;
}
case SIDE_DIALOG: {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.select_side_to_move_first)
.setPositiveButton(R.string.white, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
cb.pos.setWhiteMove(true);
checkValid();
dialog.cancel();
}
})
.setNegativeButton(R.string.black, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
cb.pos.setWhiteMove(false);
checkValid();
dialog.cancel();
}
});
AlertDialog alert = builder.create();
return alert;
}
case CASTLE_DIALOG: {
final CharSequence[] items = {
getString(R.string.white_king_castle), getString(R.string.white_queen_castle),
getString(R.string.black_king_castle), getString(R.string.black_queen_castle)
};
boolean[] checkedItems = {
cb.pos.h1Castle(), cb.pos.a1Castle(),
cb.pos.h8Castle(), cb.pos.a8Castle()
};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.castling_flags);
builder.setMultiChoiceItems(items, checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
Position pos = new Position(cb.pos);
boolean a1Castle = pos.a1Castle();
boolean h1Castle = pos.h1Castle();
boolean a8Castle = pos.a8Castle();
boolean h8Castle = pos.h8Castle();
switch (which) {
case 0: h1Castle = isChecked; break;
case 1: a1Castle = isChecked; break;
case 2: h8Castle = isChecked; break;
case 3: a8Castle = isChecked; break;
}
int castleMask = 0;
if (a1Castle) castleMask |= 1 << Position.A1_CASTLE;
if (h1Castle) castleMask |= 1 << Position.H1_CASTLE;
if (a8Castle) castleMask |= 1 << Position.A8_CASTLE;
if (h8Castle) castleMask |= 1 << Position.H8_CASTLE;
pos.setCastleMask(castleMask);
cb.setPosition(pos);
checkValid();
}
});
AlertDialog alert = builder.create();
return alert;
}
case EP_DIALOG: {
final CharSequence[] items = {
"A", "B", "C", "D", "E", "F", "G", "H", getString(R.string.none)
};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.select_en_passant_file);
builder.setSingleChoiceItems(items, getEPFile(), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
setEPFile(item);
}
});
AlertDialog alert = builder.create();
return alert;
}
case MOVCNT_DIALOG: {
final Dialog dialog = new Dialog(this);
dialog.setContentView(R.layout.edit_move_counters);
dialog.setTitle(R.string.edit_move_counters);
final EditText halfMoveClock = (EditText)dialog.findViewById(R.id.ed_cnt_halfmove);
final EditText fullMoveCounter = (EditText)dialog.findViewById(R.id.ed_cnt_fullmove);
Button ok = (Button)dialog.findViewById(R.id.ed_cnt_ok);
Button cancel = (Button)dialog.findViewById(R.id.ed_cnt_cancel);
halfMoveClock.setText(String.format("%d", cb.pos.halfMoveClock));
fullMoveCounter.setText(String.format("%d", cb.pos.fullMoveCounter));
final Runnable setCounters = new Runnable() {
public void run() {
try {
int halfClock = Integer.parseInt(halfMoveClock.getText().toString());
int fullCount = Integer.parseInt(fullMoveCounter.getText().toString());
cb.pos.halfMoveClock = halfClock;
cb.pos.fullMoveCounter = fullCount;
dialog.cancel();
} catch (NumberFormatException nfe) {
Toast.makeText(getApplicationContext(), R.string.invalid_number_format, Toast.LENGTH_SHORT).show();
}
}
};
fullMoveCounter.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
setCounters.run();
return true;
}
return false;
}
});
ok.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
setCounters.run();
}
});
cancel.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
dialog.cancel();
}
});
return dialog;
}
}
return null;
}
}

View File

@ -0,0 +1,93 @@
/*
DroidFish - An Android chess program.
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.petero.droidfish.activities;
import org.petero.droidfish.R;
import org.petero.droidfish.gamelogic.GameTree.Node;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
/** Activity to edit PGN comments. */
public class EditComments extends Activity {
TextView preComment, postComment, nag;
private Button okButton;
private Button cancelButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_comments);
preComment = (TextView)findViewById(R.id.ed_comments_pre);
TextView moveView = (TextView)findViewById(R.id.ed_comments_move);
nag = (TextView)findViewById(R.id.ed_comments_nag);
postComment = (TextView)findViewById(R.id.ed_comments_post);
okButton = (Button)findViewById(R.id.ed_comments_ok);
cancelButton = (Button)findViewById(R.id.ed_comments_cancel);
postComment.requestFocus();
Intent data = getIntent();
Bundle bundle = data.getBundleExtra("org.petero.droidfish.comments");
String pre = bundle.getString("preComment");
String post = bundle.getString("postComment");
String move = bundle.getString("move");
int nagVal = bundle.getInt("nag");
preComment.setText(pre);
postComment.setText(post);
moveView.setText(move);
String nagStr = Node.nagStr(nagVal).trim();
if ((nagStr.length() == 0) && (nagVal > 0))
nagStr = String.format("%d", nagVal);
nag.setText(nagStr);
okButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
sendBackResult();
}
});
cancelButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
setResult(RESULT_CANCELED);
finish();
}
});
}
private final void sendBackResult() {
String pre = preComment.getText().toString().trim();
String post = postComment.getText().toString().trim();
int nagVal = Node.strToNag(nag.getText().toString());
Bundle bundle = new Bundle();
bundle.putString("preComment", pre);
bundle.putString("postComment", post);
bundle.putInt("nag", nagVal);
Intent data = new Intent();
data.putExtra("org.petero.droidfish.comments", bundle);
setResult(RESULT_OK, data);
finish();
}
}

View File

@ -0,0 +1,121 @@
/*
DroidFish - An Android chess program.
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.petero.droidfish.activities;
import java.util.ArrayList;
import java.util.TreeMap;
import org.petero.droidfish.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
/** Activity to edit PGN headers. */
public class EditHeaders extends Activity {
private TextView event;
private TextView site;
private TextView date;
private TextView round;
private TextView white;
private TextView black;
private Button okButton;
private Button cancelButton;
private TreeMap<String,String> headers = new TreeMap<String,String>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_headers);
event = (TextView)findViewById(R.id.ed_header_event);
site = (TextView)findViewById(R.id.ed_header_site);
date = (TextView)findViewById(R.id.ed_header_date);
round = (TextView)findViewById(R.id.ed_header_round);
white = (TextView)findViewById(R.id.ed_header_white);
black = (TextView)findViewById(R.id.ed_header_black);
okButton = (Button)findViewById(R.id.ed_header_ok);
cancelButton = (Button)findViewById(R.id.ed_header_cancel);
Intent data = getIntent();
Bundle bundle = data.getBundleExtra("org.petero.droidfish.headers");
ArrayList<String> tags = bundle.getStringArrayList("tags");
ArrayList<String> tagValues = bundle.getStringArrayList("tagValues");
for (int i = 0; i < tags.size(); i++)
headers.put(tags.get(i), tagValues.get(i));
event.setText(headers.get("Event"));
site .setText(headers.get("Site"));
date .setText(headers.get("Date"));
round.setText(headers.get("Round"));
white.setText(headers.get("White"));
black.setText(headers.get("Black"));
okButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
sendBackResult();
}
});
cancelButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
setResult(RESULT_CANCELED);
finish();
}
});
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
sendBackResult();
return true;
}
return super.onKeyDown(keyCode, event);
}
private final void sendBackResult() {
headers.put("Event", event.getText().toString().trim());
headers.put("Site", site .getText().toString().trim());
headers.put("Date", date .getText().toString().trim());
headers.put("Round", round.getText().toString().trim());
headers.put("White", white.getText().toString().trim());
headers.put("Black", black.getText().toString().trim());
Bundle bundle = new Bundle();
ArrayList<String> tags = new ArrayList<String>();
ArrayList<String> values = new ArrayList<String>();
for (String k : headers.keySet()) {
tags.add(k);
values.add(headers.get(k));
}
bundle.putStringArrayList("tags", tags);
bundle.putStringArrayList("tagValues", values);
Intent data = new Intent();
data.putExtra("org.petero.droidfish.headers", bundle);
setResult(RESULT_OK, data);
finish();
}
}

View File

@ -0,0 +1,433 @@
/*
DroidFish - An Android chess program.
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.petero.droidfish.activities;
import java.io.File;
import java.util.ArrayList;
import org.petero.droidfish.R;
import org.petero.droidfish.activities.PGNFile.GameInfo;
import org.petero.droidfish.activities.PGNFile.GameInfoResult;
import org.petero.droidfish.gamelogic.Pair;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.DialogInterface.OnCancelListener;
import android.content.SharedPreferences.Editor;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
public class EditPGN extends ListActivity {
static ArrayList<GameInfo> gamesInFile = new ArrayList<GameInfo>();
static boolean cacheValid = false;
PGNFile pgnFile;
ProgressDialog progress;
GameInfo selectedGi = null;
ArrayAdapter<GameInfo> aa = null;
EditText filterText = null;
SharedPreferences settings;
int defaultItem = 0;
String lastSearchString = "";
String lastFileName = "";
long lastModTime = -1;
Thread workThread = null;
boolean loadGame; // True when loading game, false when saving
String pgnToSave;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
settings = PreferenceManager.getDefaultSharedPreferences(this);
if (savedInstanceState != null) {
defaultItem = savedInstanceState.getInt("defaultItem");
lastSearchString = savedInstanceState.getString("lastSearchString");
if (lastSearchString == null) lastSearchString = "";
lastFileName = savedInstanceState.getString("lastFileName");
if (lastFileName == null) lastFileName = "";
lastModTime = savedInstanceState.getLong("lastModTime");
} else {
defaultItem = settings.getInt("defaultItem", 0);
lastSearchString = settings.getString("lastSearchString", "");
lastFileName = settings.getString("lastFileName", "");
lastModTime = settings.getLong("lastModTime", 0);
}
Intent i = getIntent();
String action = i.getAction();
String fileName = i.getStringExtra("org.petero.droidfish.pathname");
if (action.equals("org.petero.droidfish.loadFile")) {
pgnFile = new PGNFile(fileName);
loadGame = true;
showDialog(PROGRESS_DIALOG);
final EditPGN lpgn = this;
workThread = new Thread(new Runnable() {
public void run() {
if (!readFile())
return;
runOnUiThread(new Runnable() {
public void run() {
lpgn.showList();
}
});
}
});
workThread.start();
} else if (action.equals("org.petero.droidfish.loadFileNextGame") ||
action.equals("org.petero.droidfish.loadFilePrevGame")) {
pgnFile = new PGNFile(fileName);
loadGame = true;
boolean next = action.equals("org.petero.droidfish.loadFileNextGame");
final int loadItem = defaultItem + (next ? 1 : -1);
if (loadItem < 0) {
Toast.makeText(getApplicationContext(), R.string.no_prev_game,
Toast.LENGTH_SHORT).show();
setResult(RESULT_CANCELED);
finish();
} else {
workThread = new Thread(new Runnable() {
public void run() {
if (!readFile())
return;
runOnUiThread(new Runnable() {
public void run() {
if (loadItem >= gamesInFile.size()) {
Toast.makeText(getApplicationContext(), R.string.no_next_game,
Toast.LENGTH_SHORT).show();
setResult(RESULT_CANCELED);
finish();
} else {
defaultItem = loadItem;
sendBackResult(gamesInFile.get(loadItem));
}
}
});
}
});
workThread.start();
}
} else if (action.equals("org.petero.droidfish.saveFile")) {
loadGame = false;
pgnToSave = i.getStringExtra("org.petero.droidfish.pgn");
boolean silent = i.getBooleanExtra("org.petero.droidfish.silent", false);
if (silent) { // Silently append to file
PGNFile pgnFile2 = new PGNFile(fileName);
pgnFile2.appendPGN(pgnToSave, null);
} else {
pgnFile = new PGNFile(fileName);
showDialog(PROGRESS_DIALOG);
final EditPGN lpgn = this;
workThread = new Thread(new Runnable() {
public void run() {
if (!readFile())
return;
runOnUiThread(new Runnable() {
public void run() {
if (gamesInFile.size() == 0) {
pgnFile.appendPGN(pgnToSave, getApplicationContext());
finish();
} else {
lpgn.showList();
}
}
});
}
});
workThread.start();
}
} else { // Unsupported action
setResult(RESULT_CANCELED);
finish();
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("defaultItem", defaultItem);
outState.putString("lastSearchString", lastSearchString);
outState.putString("lastFileName", lastFileName);
outState.putLong("lastModTime", lastModTime);
}
@Override
protected void onPause() {
Editor editor = settings.edit();
editor.putInt("defaultItem", defaultItem);
editor.putString("lastSearchString", lastSearchString);
editor.putString("lastFileName", lastFileName);
editor.putLong("lastModTime", lastModTime);
editor.commit();
super.onPause();
}
@Override
protected void onDestroy() {
if (workThread != null) {
workThread.interrupt();
try {
workThread.join();
} catch (InterruptedException e) {
}
workThread = null;
}
super.onDestroy();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.edit_file_options_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.item_delete_file:
removeDialog(DELETE_PGN_FILE_DIALOG);
showDialog(DELETE_PGN_FILE_DIALOG);
break;
}
return false;
}
private final void showList() {
progress.dismiss();
setContentView(R.layout.select_game);
aa = new ArrayAdapter<GameInfo>(this, R.layout.select_game_list_item, gamesInFile);
setListAdapter(aa);
ListView lv = getListView();
lv.setSelectionFromTop(defaultItem, 0);
lv.setFastScrollEnabled(true);
lv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int pos, long id) {
selectedGi = aa.getItem(pos);
if (loadGame) {
defaultItem = pos;
sendBackResult(selectedGi);
} else {
removeDialog(SAVE_GAME_DIALOG);
showDialog(SAVE_GAME_DIALOG);
}
}
});
lv.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int pos, long id) {
selectedGi = aa.getItem(pos);
if (!selectedGi.isNull()) {
removeDialog(DELETE_GAME_DIALOG);
showDialog(DELETE_GAME_DIALOG);
}
return true;
}
});
// lv.setTextFilterEnabled(true);
filterText = (EditText)findViewById(R.id.select_game_filter);
filterText.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) { }
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
aa.getFilter().filter(s);
lastSearchString = s.toString();
}
});
filterText.setText(lastSearchString);
lv.requestFocus();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
final static int PROGRESS_DIALOG = 0;
final static int DELETE_GAME_DIALOG = 1;
final static int SAVE_GAME_DIALOG = 2;
final static int DELETE_PGN_FILE_DIALOG = 3;
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case PROGRESS_DIALOG:
progress = new ProgressDialog(this);
progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progress.setTitle(R.string.reading_pgn_file);
progress.setMessage(getString(R.string.please_wait));
progress.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
Thread thr = workThread;
if (thr != null)
thr.interrupt();
}
});
return progress;
case DELETE_GAME_DIALOG: {
final GameInfo gi = selectedGi;
selectedGi = null;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Delete game?");
String msg = gi.toString();
builder.setMessage(msg);
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteGame(gi);
dialog.cancel();
}
});
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
return alert;
}
case SAVE_GAME_DIALOG: {
final GameInfo gi = selectedGi;
selectedGi = null;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Save game?");
final CharSequence[] items = { "Before Selected", "After Selected", "Replace Selected" };
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
GameInfo giToReplace;
switch (item) {
case 0: giToReplace = new GameInfo().setNull(gi.startPos); break;
case 1: giToReplace = new GameInfo().setNull(gi.endPos); break;
case 2: giToReplace = gi; break;
default:
finish(); return;
}
pgnFile.replacePGN(pgnToSave, giToReplace, getApplicationContext());
finish();
}
});
AlertDialog alert = builder.create();
return alert;
}
case DELETE_PGN_FILE_DIALOG: {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Delete file?");
String name = new File(pgnFile.getName()).getName();
String msg = String.format("Delete file %s?", name);
builder.setMessage(msg);
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
pgnFile.delete();
finish();
}
});
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
return alert;
}
default:
return null;
}
}
private final boolean readFile() {
String fileName = pgnFile.getName();
if (!fileName.equals(lastFileName))
defaultItem = 0;
long modTime = new File(fileName).lastModified();
if (cacheValid && (modTime == lastModTime) && fileName.equals(lastFileName))
return true;
pgnFile = new PGNFile(fileName);
Pair<GameInfoResult, ArrayList<GameInfo>> p = pgnFile.getGameInfo(this, progress);
if (p.first != GameInfoResult.OK) {
gamesInFile = new ArrayList<GameInfo>();
if (p.first == GameInfoResult.OUT_OF_MEMORY) {
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), "File too large", Toast.LENGTH_SHORT).show();
}
});
}
setResult(RESULT_CANCELED);
finish();
return false;
}
gamesInFile = p.second;
cacheValid = true;
lastModTime = modTime;
lastFileName = fileName;
return true;
}
private final void sendBackResult(GameInfo gi) {
String pgn = pgnFile.readOneGame(gi);
if (pgn != null) {
setResult(RESULT_OK, (new Intent()).setAction(pgn));
finish();
} else {
setResult(RESULT_CANCELED);
finish();
}
}
private final void deleteGame(GameInfo gi) {
if (pgnFile.deleteGame(gi, getApplicationContext(), gamesInFile)) {
ListView lv = getListView();
int pos = lv.pointToPosition(0,0);
aa = new ArrayAdapter<GameInfo>(this, R.layout.select_game_list_item, gamesInFile);
setListAdapter(aa);
String s = filterText.getText().toString();
aa.getFilter().filter(s);
lv.setSelection(pos);
// Update lastModTime, since current change has already been handled
String fileName = pgnFile.getName();
long modTime = new File(fileName).lastModified();
lastModTime = modTime;
}
}
}

Some files were not shown because too many files have changed in this diff Show More