1
0
Fork 0
pull/3726/merge
ab-chesspad 2021-11-15 19:20:50 -05:00 committed by GitHub
commit b65db2c86c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 1639 additions and 140 deletions

View File

@ -1,3 +1,24 @@
## Preamble
The main goal of my work is to build Stockfish as a shared library,
so that it can be used with the latest Android SDK (currently 30).
Additionally, I replaced all 'exit' statements with exceptions and
removed the large nnue file download from build when nnue is opted out.<br>
This repository is a fork from the official Stockfish repository
and most probably it will stay this way.
SF people think that the suggested updates are too large which is
probably true, considering that I had to change the program architecture.
I tried my best to minimize the code impact, but apparently I failed.<br>
It is possible to split this work into smaller, more manageable
parts, but to think that each update will cause the long discussion,
taking into account that most SF people believe that it is ok for
a program to crash and some other differences of opinions, I have decided
to leave it as is.<br>
To prove the concept, I created a simple GUI-minimalistic Android
program that is built with SDK 30. It does not verify any user's input
(which is intentional), but non the less it should not crash.
<b>If it still crashes, it's all on me.
Please report it and I'll fix it ASAIC.</b>
## Overview
[![Build Status](https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml/badge.svg)](https://github.com/official-stockfish/Stockfish/actions)

15
android/NDKTest/.gitignore vendored 100644
View File

@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
</component>
</project>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DesignSurface">
<option name="filePathToZoomLevelMap">
<map>
<entry key="app/src/main/res/layout/activity_main.xml" value="0.1" />
</map>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>

1
android/NDKTest/app/.gitignore vendored 100644
View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,52 @@
plugins {
id 'com.android.application'
}
android {
compileSdk 30
defaultConfig {
applicationId "com.ab.ndktest"
minSdk 19
targetSdk 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags '-std=c++17'
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.10.2'
}
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,26 @@
package com.ab.ndktest;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.ab.ndktest", appContext.getPackageName());
}
}

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ab.ndktest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.NDKTest">
<activity
android:name="com.ab.pgn.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,57 @@
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.10.2)
# Declares and names the project.
project("ndktest")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNNUE_EMBEDDING_OFF")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch x86_64")
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
set(sf_dir ../../../../../../src)
file(GLOB SOURCES ${sf_dir}/*.cpp ${sf_dir}/syzygy/*.cpp
${sf_dir}/nnue/*.cpp ${sf_dir}/nnue/features/*.cpp
${sf_dir}/nnue/layers/*.cpp ${sf_dir}/incbin/*.cpp)
add_library( # Sets the name of the library.
stockfish
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
${SOURCES}
)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
stockfish
# Links the target library to the log library
# included in the NDK.
${log-lib})

View File

@ -0,0 +1,94 @@
/*
Copyright (C) 2021 Alexander Bootman, alexbootman@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 com.ab.pgn;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.KeyEvent;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import com.ab.ndktest.databinding.ActivityMainBinding;
import com.ab.pgn.stockfish.Stockfish;
/**
* this is a minimalistic example of using Stockfish as a shared library on Android
*/
public class MainActivity extends AppCompatActivity {
enum READ_COMMAND {
READ_NORMAL,
READ_ERR,
}
private final Stockfish stockfish = new Stockfish();
private String fromSF;
private TextView tvResponse;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
tvResponse = binding.textViewResponse;
TextView tvCommand = binding.editTextCommand;
tvCommand.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
/* Hide Soft Keyboard IN ONE LINE */
((InputMethodManager)MainActivity.this.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(textView.getWindowToken(),0);
String cmd = tvCommand.getText().toString().trim();
if (!"quit".equals(cmd)) {
stockfish.write(cmd);
}
return false;
}
});
bgCall(() -> sfRead(READ_COMMAND.READ_NORMAL));
bgCall(() -> sfRead(READ_COMMAND.READ_ERR));
}
private void sfRead(READ_COMMAND read_command) {
while ((fromSF = read_command == READ_COMMAND.READ_ERR ? stockfish.read_err() : stockfish.read()) != null) {
if (fromSF.trim().isEmpty()) {
continue;
}
// tvResponse cannot be modified directly in a BG thread
new Handler(Looper.getMainLooper()).post(() -> {
int color = read_command == READ_COMMAND.READ_ERR ?
Color.parseColor("#ff0000") :
Color.parseColor("#000000");
tvResponse.setTextColor(color);
tvResponse.setText(fromSF);
});
}
}
private void bgCall(BgCall caller) {
new Thread(caller::exec).start();
}
private interface BgCall {
void exec();
}
}

View File

@ -0,0 +1,54 @@
/*
Copyright (C) 2021 Alexander Bootman, alexbootman@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 com.ab.pgn.stockfish;
public class Stockfish {
public static final String DEFAULT_SF_PATH = "stockfish";
public native void _init();
public native void _write(String command);
public native String _read();
public native String _read_err();
public Stockfish() {
init(DEFAULT_SF_PATH);
}
public Stockfish(String path) {
init(path);
}
private void init(String path) {
System.loadLibrary(path);
_init();
}
public void write(String command) {
_write(command);
}
public String read() {
return _read();
}
public String read_err() {
return _read_err();
}
public void quit() {
_write("quit");
}
}

View File

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.ab.pgn.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="1dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="1dp"
android:orientation="vertical"
>
<EditText
android:id="@+id/editTextCommand"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00ff00"
android:inputType="textPersonName"
android:text="uci" />
<TextView
android:id="@+id/textViewResponse"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Response"
/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -0,0 +1,16 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.NDKTest" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/black</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>

View File

@ -0,0 +1,3 @@
<resources>
<string name="app_name">Stockfish Test</string>
</resources>

View File

@ -0,0 +1,16 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.NDKTest" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>

View File

@ -0,0 +1,17 @@
package com.ab.ndktest;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

View File

@ -0,0 +1,17 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.2"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@ -0,0 +1,19 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true

Binary file not shown.

View File

@ -0,0 +1,6 @@
#Thu Oct 07 10:15:56 EDT 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

185
android/NDKTest/gradlew vendored 100755
View File

@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

89
android/NDKTest/gradlew.bat vendored 100644
View File

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -0,0 +1,10 @@
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
jcenter() // Warning: this repository is going to shut down soon
}
}
rootProject.name = "NDKTest"
include ':app'

View File

@ -0,0 +1,43 @@
/*
Copyright (C) 2021 Alexander Bootman, alexbootman@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/>.
export JAVA_HOME=/usr/lib/jvm/java-8-oracle (or something like this)
navigate to java directory
javac com/ab/pgn/Cli.java com/ab/pgn/StockfishRunner.java com/ab/pgn/stockfish/Stockfish.java
java -cp . com.ab.pgn.Cli ../src/stockfish.so
*/
package com.ab.pgn;
import com.ab.pgn.stockfish.Stockfish;
import java.io.*;
public class Cli {
public static final String SF_LIB_NAME = "stockfish.so";
public static void main(String[] args) throws Exception {
String libPath = null;
if (args.length >= 1) {
libPath = args[0];
} else {
libPath = "x";
}
File f = new File(libPath);
libPath = f.getAbsolutePath();
int i = libPath.lastIndexOf(File.separator);
libPath = libPath.substring(0, i + 1) + SF_LIB_NAME;
StockfishRunner stockfishRunner = new StockfishRunner(libPath);
stockfishRunner.run();
}
}

View File

@ -0,0 +1,78 @@
/*
Copyright (C) 2021 Alexander Bootman, alexbootman@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 com.ab.pgn;
import com.ab.pgn.stockfish.Stockfish;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;
public class StockfishRunner {
private final Stockfish stockfish;
public StockfishRunner(String path) {
stockfish = new Stockfish(path);
}
void input_reader() throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Enter Stockfish commands:");
String cmd;
do {
cmd = reader.readLine();
stockfish.write(cmd);
} while (!"quit".equals(cmd));
}
void output_reader() {
String fromSF;
while((fromSF = stockfish.read()) != null) {
System.out.print(fromSF);
}
}
void err_reader() {
String fromSF;
while((fromSF = stockfish.read_err()) != null) {
System.err.print(fromSF);
}
}
public void run() throws InterruptedException {
Thread inputThread = new Thread(() -> {
try {
input_reader();
} catch (IOException e) {
e.printStackTrace();
}
});
inputThread.start();
Thread outputThread = new Thread(() -> output_reader());
outputThread.start();
Thread errorThread = new Thread(() -> err_reader());
errorThread.start();
inputThread.join();
outputThread.join();
errorThread.join();
}
}

View File

@ -0,0 +1,52 @@
/*
Copyright (C) 2021 Alexander Bootman, alexbootman@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 com.ab.pgn.stockfish;
public class Stockfish {
public static final String DEFAULT_SF_PATH = "";
public native void _init();
public native void _write(String command);
public native String _read();
public native String _read_err();
public Stockfish() {
init(DEFAULT_SF_PATH);
}
public Stockfish(String path) {
init(path);
}
private void init(String path) {
System.load(path);
_init();
}
public void write(String command) {
_write(command);
}
public String read() {
return _read();
}
public String read_err() {
return _read_err();
}
public void quit() {
_write("quit");
}
}

View File

@ -20,10 +20,18 @@
### ==========================================================================
### Executable name
ifeq ($(COMP),mingw)
EXE = stockfish.exe
ifeq ($(jni),yes)
ifeq ($(COMP),mingw)
EXE = stockfish.dll
else
EXE = stockfish.so
endif
else
EXE = stockfish
ifeq ($(COMP),mingw)
EXE = stockfish.exe
else
EXE = stockfish
endif
endif
### Installation dir definitions
@ -42,6 +50,9 @@ SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp
material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \
search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
nnue/evaluate_nnue.cpp nnue/features/half_ka_v2_hm.cpp
ifeq ($(jni),yes)
SRCS += stockfishlib.cpp
endif
OBJS = $(notdir $(SRCS:.cpp=.o))
@ -82,6 +93,8 @@ endif
# vnni256 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 256
# vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512
# neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture
# nnue = yes/no --- -DNNUE_EMBEDDING_OFF --- Do not use NNUE
# jni = yes --- -DBUILD_JNI --- Build SF as shared library for use with JNI
#
# Note that Makefile is space sensitive, so when adding new architectures
# or modifying existing flags, you have to make sure there are no extra spaces
@ -127,6 +140,7 @@ vnni256 = no
vnni512 = no
neon = no
STRIP = strip
nnue = yes
### 2.2 Architecture specific
@ -315,7 +329,7 @@ endif
### ==========================================================================
### 3.1 Selecting compiler (default = gcc)
CXXFLAGS += -Wall -Wcast-qual -fno-exceptions -std=c++17 $(EXTRACXXFLAGS)
CXXFLAGS += -Wall -Wcast-qual -fexceptions -std=c++17 $(EXTRACXXFLAGS)
DEPENDFLAGS += -std=c++17
LDFLAGS += $(EXTRALDFLAGS)
@ -414,6 +428,14 @@ ifeq ($(KERNEL),Darwin)
XCRUN = xcrun
endif
ifeq ($(nnue),no)
CXXFLAGS += -DNNUE_EMBEDDING_OFF
endif
ifeq ($(jni),yes)
CXXFLAGS += -shared -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin
endif
# To cross-compile for Android, NDK version r21 or later is recommended.
# In earlier NDK versions, you'll need to pass -fno-addrsig if using GNU binutils.
# Currently we don't know how to make PGO builds with the NDK yet.
@ -717,6 +739,11 @@ help:
@echo "icc > Intel compiler"
@echo "ndk > Google NDK to cross-compile for Android"
@echo ""
@echo "Build options:"
@echo ""
@echo "nnue=no > Build SF without NNUE"
@echo "jni=yes > Build SF as shared library for JVI, don't forget to specify JAVA_HOME"
@echo ""
@echo "Simple examples. If you don't know what to do, you likely want to run: "
@echo ""
@echo "make -j build ARCH=x86-64 (A portable, slow compile for 64-bit systems)"
@ -774,6 +801,7 @@ clean: objclean profileclean
# evaluation network (nnue)
net:
ifneq ($(nnue),no)
$(eval nnuenet := $(shell grep EvalFileDefaultName evaluate.h | grep define | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/'))
@echo "Default net: $(nnuenet)"
$(eval nnuedownloadurl := https://tests.stockfishchess.org/api/nn/$(nnuenet))
@ -795,6 +823,7 @@ net:
else \
echo "shasum / sha256sum not found, skipping net validation"; \
fi
endif
# clean binaries and objects
objclean:

View File

@ -135,8 +135,8 @@ vector<string> setup_bench(const Position& current, istream& is) {
if (!file.is_open())
{
cerr << "Unable to open file " << fenFile << endl;
exit(EXIT_FAILURE);
throw std::runtime_error("Unable to open file " + fenFile);
throw std::runtime_error("Unable to open file " + fenFile);
}
while (getline(file, fen))
@ -157,16 +157,20 @@ vector<string> setup_bench(const Position& current, istream& is) {
list.emplace_back(fen);
else
{
#ifndef NNUE_EMBEDDING_OFF
if (evalType == "classical" || (evalType == "mixed" && posCounter % 2 == 0))
list.emplace_back("setoption name Use NNUE value false");
else if (evalType == "NNUE" || (evalType == "mixed" && posCounter % 2 != 0))
list.emplace_back("setoption name Use NNUE value true");
#endif
list.emplace_back("position fen " + fen);
list.emplace_back(go);
++posCounter;
}
#ifndef NNUE_EMBEDDING_OFF
list.emplace_back("setoption name Use NNUE value true");
#endif
return list;
}

View File

@ -16,11 +16,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cassert>
#include "bitboard.h"
#include "endgame.h"
#include "movegen.h"
#include "types.h"
namespace Stockfish {
@ -44,11 +43,9 @@ namespace {
inline int push_close(Square s1, Square s2) { return 140 - 20 * distance(s1, s2); }
inline int push_away(Square s1, Square s2) { return 120 - push_close(s1, s2); }
#ifndef NDEBUG
bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) {
return pos.non_pawn_material(c) == npm && pos.count<PAWN>(c) == pawnsCnt;
}
#endif
// Map the square as if strongSide is white and strongSide's only pawn
// is on the left half of the board.

View File

@ -17,13 +17,10 @@
*/
#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <cstring> // For std::memset
#include <fstream>
#include <iomanip>
#include <sstream>
#include <iostream>
#include <streambuf>
#include <vector>
@ -49,7 +46,9 @@
INCBIN(EmbeddedNNUE, EvalFileDefaultName);
#else
const unsigned char gEmbeddedNNUEData[1] = {0x0};
#ifndef NNUE_EMBEDDING_OFF
const unsigned char *const gEmbeddedNNUEEnd = &gEmbeddedNNUEData[1];
#endif
const unsigned int gEmbeddedNNUESize = 1;
#endif
@ -58,6 +57,7 @@ using namespace std;
namespace Stockfish {
#ifndef _NNUE_EMBEDDING_OFF
namespace Eval {
bool useNNUE;
@ -125,20 +125,14 @@ namespace Eval {
if (useNNUE && currentEvalFileName != eval_file)
{
string msg1 = "If the UCI option \"Use NNUE\" is set to true, network evaluation parameters compatible with the engine must be available.";
string msg2 = "The option is set to true, but the network file " + eval_file + " was not loaded successfully.";
string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file.";
string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + std::string(EvalFileDefaultName);
string msg5 = "The engine will be terminated now.";
sync_cout << "info string ERROR: " << msg1 << sync_endl;
sync_cout << "info string ERROR: " << msg2 << sync_endl;
sync_cout << "info string ERROR: " << msg3 << sync_endl;
sync_cout << "info string ERROR: " << msg4 << sync_endl;
sync_cout << "info string ERROR: " << msg5 << sync_endl;
exit(EXIT_FAILURE);
std::stringstream stream;
stream
<< "info string ERROR: If the UCI option \"Use NNUE\" is set to true, network evaluation parameters compatible with the engine must be available." << std::endl
<< "info string ERROR: The option is set to true, but the network file " << eval_file << " was not loaded successfully." << std::endl
<< "info string ERROR: The UCI option EvalFile might need to specify the full path, including the directory name, to the network file." << std::endl
<< "info string ERROR: The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" << EvalFileDefaultName << std::endl
;
throw std::runtime_error(stream.str());
}
if (useNNUE)
@ -147,6 +141,7 @@ namespace Eval {
sync_cout << "info string classical evaluation enabled" << sync_endl;
}
}
#endif
namespace Trace {

View File

@ -23,18 +23,17 @@
#include "position.h"
#include "psqt.h"
#include "search.h"
#include "syzygy/tbprobe.h"
#include "thread.h"
#include "tt.h"
#include "uci.h"
using namespace Stockfish;
int main(int argc, char* argv[]) {
Outstream outstream, errstream;
std::atomic<int> done = 0;
std::cout << engine_info() << std::endl;
CommandLine::init(argc, argv);
int sf_init() {
sync_cout << engine_info() << sync_endl;
UCI::init(Options);
Tune::init();
PSQT::init();
@ -45,9 +44,77 @@ int main(int argc, char* argv[]) {
Threads.set(size_t(Options["Threads"]));
Search::clear(); // After threads are up
Eval::NNUE::init();
UCI::loop(argc, argv);
Threads.set(0);
UCI::init_pos();
return 0;
}
void unblock_readers() {
done = 1;
outstream.finish();
errstream.finish();
}
#ifndef jni
/// input_reader() waits for a command from stdin and invokes UCI::execute()
/// Also intercepts EOF from stdin to ensure gracefully exiting if the
/// GUI dies unexpectedly.
void input_reader() {
std::string cmd;
while (getline(std::cin, cmd)) {
UCI::execute(cmd);
if (cmd == "quit")
break;
}
}
static std::mutex mutex_;
void output_reader() {
while (!done) {
std::string res;
int len = outstream.read(res);
if (len < 0) {
break;
}
mutex_.lock();
std::cout << res;
mutex_.unlock();
}
}
void error_reader() {
while (!done) {
std::string res;
int len = errstream.read(res);
if (len < 0) {
break;
}
mutex_.lock();
std::cerr << res;
mutex_.unlock();
}
}
/// When SF is called with some command line arguments, e.g. to
/// run 'bench', once the command is executed the program stops.
int main(int argc, char* argv[]) {
std::thread output_reader_thread(output_reader);
std::thread error_reader_thread(error_reader);
int res = sf_init();
if (argc > 1) {
std::string cmd;
for (int i = 1; i < argc; ++i)
cmd += std::string(argv[i]) + " ";
UCI::execute(cmd);
} else {
std::thread input_reader_thread(input_reader);
input_reader_thread.join();
}
unblock_readers();
output_reader_thread.join();
error_reader_thread.join();
return res;
}
#endif

View File

@ -42,9 +42,7 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <vector>
#include <cstdlib>
#if defined(__linux__) && !defined(__ANDROID__)
#include <stdlib.h>
@ -59,6 +57,18 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
#include "misc.h"
#include "thread.h"
int throw_exception(const char* func, const char* file, int line, const char* msg) {
std::stringstream stream;
stream << "Assertion failed: " << msg << ", function " << func <<", file " << file
<< ", line " << line;
throw std::runtime_error(stream.str());
return 0;
}
int check_expect(int actual, int expect) {
return actual == expect;
}
using namespace std;
namespace Stockfish {
@ -123,8 +133,7 @@ public:
if (!l.file.is_open())
{
cerr << "Unable to open debug log file " << fname << endl;
exit(EXIT_FAILURE);
throw std::runtime_error("Unable to open debug log file " + fname);
}
cin.rdbuf(&l.in);
@ -288,32 +297,14 @@ void dbg_mean_of(int v) { ++means[0]; means[1] += v; }
void dbg_print() {
if (hits[0])
cerr << "Total " << hits[0] << " Hits " << hits[1]
<< " hit rate (%) " << 100 * hits[1] / hits[0] << endl;
sync_cerr << "Total " << hits[0] << " Hits " << hits[1]
<< " hit rate (%) " << 100 * hits[1] / hits[0] << sync_endl;
if (means[0])
cerr << "Total " << means[0] << " Mean "
<< (double)means[1] / means[0] << endl;
sync_cerr << "Total " << means[0] << " Mean "
<< (double)means[1] / means[0] << sync_endl;
}
/// Used to serialize access to std::cout to avoid multiple threads writing at
/// the same time.
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
static std::mutex m;
if (sc == IO_LOCK)
m.lock();
if (sc == IO_UNLOCK)
m.unlock();
return os;
}
/// Trampoline helper to avoid moving Logger to misc.h
void start_logger(const std::string& fname) { Logger::start(fname); }
@ -471,10 +462,11 @@ void aligned_large_pages_free(void* mem) {
if (mem && !VirtualFree(mem, 0, MEM_RELEASE))
{
DWORD err = GetLastError();
std::cerr << "Failed to free large page memory. Error code: 0x"
std::stringstream stream;
stream << "Failed to free large page memory. Error code: 0x"
<< std::hex << err
<< std::dec << std::endl;
exit(EXIT_FAILURE);
throw std::runtime_error(stream.str());
}
}

View File

@ -19,15 +19,96 @@
#ifndef MISC_H_INCLUDED
#define MISC_H_INCLUDED
#include <cassert>
#include <chrono>
#include <iostream>
#include <sstream>
#include <ostream>
#include <string>
#include <vector>
#include <cstdint>
#include <mutex>
#include <thread>
#include <condition_variable>
#include "types.h"
enum SyncCout { IO_LOCK, IO_UNLOCK };
#define sync_endl IO_UNLOCK
/*
* It is recommended to unblock sync_cout (with sync_endl) prior to using sync_cerr
* to avoid possible deadlock.
*/
#define sync_cout outstream << IO_LOCK
#define sync_cerr errstream << IO_LOCK
class Outstream {
public:
int read(std::string& buf) {
if (done) {
return -1;
} else {
std::unique_lock<std::mutex> mlock(mutex_);
cond_.wait(mlock);
if (done) {
mlock.unlock();
return -1;
}
buf = buf_;
buf_.clear();
mlock.unlock();
return buf.length();
}
}
void finish() {
done = true;
cond_.notify_one();
}
Outstream& operator<<(SyncCout sc) {
if (sc == IO_LOCK) {
std::thread::id current_thread_id = std::this_thread::get_id();
if (current_thread_id != owner_thread_id) {
mutex_.lock();
owner_thread_id = current_thread_id;
}
}
if (sc == IO_UNLOCK) {
buf_ += "\n";
owner_thread_id = no_thread_id;
mutex_.unlock();
cond_.notify_one();
}
return *this;
}
Outstream& operator<<(const char* str) {
buf_ += str;
return *this;
}
Outstream& operator<<(std::string str) {
buf_ += str;
return *this;
}
Outstream& operator<<(int num) {
buf_ += std::to_string(num);
return *this;
}
private:
const std::thread::id no_thread_id;
std::mutex mutex_;
std::thread::id owner_thread_id;
std::condition_variable cond_;
std::string buf_;
bool done = false;
};
extern Outstream outstream, errstream;
namespace Stockfish {
std::string engine_info(bool to_uci = false);
@ -59,14 +140,6 @@ private:
std::vector<Entry> table = std::vector<Entry>(Size); // Allocate on the heap
};
enum SyncCout { IO_LOCK, IO_UNLOCK };
std::ostream& operator<<(std::ostream&, SyncCout);
#define sync_cout std::cout << IO_LOCK
#define sync_endl std::endl << IO_UNLOCK
// align_ptr_up() : get the first aligned element of an array.
// ptr must point to an array of size at least `sizeof(T) * N + alignment` bytes,
// where N is the number of elements in the array.

View File

@ -17,7 +17,6 @@
*/
#include <algorithm>
#include <cassert>
#include <cstddef> // For offsetof()
#include <cstring> // For std::memset, std::memcmp
#include <iomanip>
@ -55,7 +54,7 @@ constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING
/// operator<<(Position) returns an ASCII representation of the position
std::ostream& operator<<(std::ostream& os, const Position& pos) {
Outstream& operator<<(Outstream& os, const Position& pos) {
os << "\n +---+---+---+---+---+---+---+---+\n";
@ -67,10 +66,12 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) {
os << " | " << (1 + r) << "\n +---+---+---+---+---+---+---+---+\n";
}
os << " a b c d e f g h\n"
<< "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase
<< std::setfill('0') << std::setw(16) << pos.key()
<< std::setfill(' ') << std::dec << "\nCheckers: ";
std::stringstream stream;
stream << " a b c d e f g h\n"
<< "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase
<< std::setfill('0') << std::setw(16) << pos.key()
<< std::setfill(' ') << std::dec << "\nCheckers: ";
os << stream.str();
for (Bitboard b = pos.checkers(); b; )
os << UCI::square(pop_lsb(b)) << " ";
@ -86,8 +87,10 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) {
Tablebases::ProbeState s1, s2;
Tablebases::WDLScore wdl = Tablebases::probe_wdl(p, &s1);
int dtz = Tablebases::probe_dtz(p, &s2);
os << "\nTablebases WDL: " << std::setw(4) << wdl << " (" << s1 << ")"
stream.clear();
stream << "\nTablebases WDL: " << std::setw(4) << wdl << " (" << s1 << ")"
<< "\nTablebases DTZ: " << std::setw(4) << dtz << " (" << s2 << ")";
os << stream.str();
}
return os;

View File

@ -19,7 +19,6 @@
#ifndef POSITION_H_INCLUDED
#define POSITION_H_INCLUDED
#include <cassert>
#include <deque>
#include <memory> // For std::unique_ptr
#include <string>
@ -201,7 +200,7 @@ private:
bool chess960;
};
extern std::ostream& operator<<(std::ostream& os, const Position& pos);
extern Outstream& operator<<(Outstream&, const Position&);
inline Color Position::side_to_move() const {
return sideToMove;

View File

@ -17,7 +17,6 @@
*/
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstring> // For std::memset
#include <iostream>
@ -268,9 +267,9 @@ void MainThread::search() {
sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960());
if (bestThread->rootMoves[0].pv.size() > 1 || bestThread->rootMoves[0].extract_ponder_from_tt(rootPos))
std::cout << " ponder " << UCI::move(bestThread->rootMoves[0].pv[1], rootPos.is_chess960());
sync_cout << " ponder " << UCI::move(bestThread->rootMoves[0].pv[1], rootPos.is_chess960());
std::cout << sync_endl;
sync_cout << sync_endl;
}

View File

@ -0,0 +1,109 @@
/*
Copyright (C) 2021 Alexander Bootman, alexbootman@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 <jni.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sstream>
#include "uci.h"
#ifndef _Included_com_ab_pgn_stockfish_Stockfish
#define _Included_com_ab_pgn_stockfish_Stockfish
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_ab_pgn_stockfish_Stockfish
* Method: _init
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_com_ab_pgn_stockfish_Stockfish__1init
(JNIEnv *, jobject) {
sf_init();
}
/*
* Class: com_ab_pgn_stockfish_Stockfish
* Method: _quit
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_com_ab_pgn_stockfish_Stockfish__1quit
(JNIEnv *, jobject) {
unblock_readers();
}
/*
* Class: com_ab_pgn_stockfish_Stockfish
* Method: _write
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_com_ab_pgn_stockfish_Stockfish__1write
(JNIEnv *env, jobject, jstring _command) {
const char *cmd = env->GetStringUTFChars(_command, NULL);
Stockfish::UCI::execute(cmd);
if (strcmp(cmd, "quit") == 0) {
unblock_readers();
}
}
jstring _read(JNIEnv *env, Outstream& os) {
std::string from_uci;
int len = os.read(from_uci);
jstring res = NULL;
if (len >= 0) {
res = env->NewStringUTF(from_uci.c_str());
}
return res;
}
/*
* Class: com_ab_pgn_stockfish_Stockfish
* Method: _read
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL
Java_com_ab_pgn_stockfish_Stockfish__1read
(JNIEnv *env, jobject) {
return _read(env, outstream);
}
/*
* Class: com_ab_pgn_stockfish_Stockfish
* Method: _read_err
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL
Java_com_ab_pgn_stockfish_Stockfish__1read_1err
(JNIEnv *env, jobject) {
return _read(env, errstream);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -217,8 +217,7 @@ public:
if (statbuf.st_size % 64 != 16)
{
std::cerr << "Corrupt tablebase file " << fname << std::endl;
exit(EXIT_FAILURE);
throw std::runtime_error("Corrupt tablebase file " + fname);
}
*mapping = statbuf.st_size;
@ -230,8 +229,7 @@ public:
if (*baseAddress == MAP_FAILED)
{
std::cerr << "Could not mmap() " << fname << std::endl;
exit(EXIT_FAILURE);
throw std::runtime_error("Could not mmap() " + fname);
}
#else
// Note FILE_FLAG_RANDOM_ACCESS is only a hint to Windows and as such may get ignored.
@ -246,8 +244,9 @@ public:
if (size_low % 64 != 16)
{
std::cerr << "Corrupt tablebase file " << fname << std::endl;
exit(EXIT_FAILURE);
std::stringstream stream;
stream << "Corrupt tablebase file " << fname << std::endl;
throw std::runtime_error(stream.str());
}
HANDLE mmap = CreateFileMapping(fd, nullptr, PAGE_READONLY, size_high, size_low, nullptr);
@ -255,8 +254,7 @@ public:
if (!mmap)
{
std::cerr << "CreateFileMapping() failed" << std::endl;
exit(EXIT_FAILURE);
throw std::runtime_error("CreateFileMapping() failed");
}
*mapping = (uint64_t)mmap;
@ -264,9 +262,10 @@ public:
if (!*baseAddress)
{
std::cerr << "MapViewOfFile() failed, name = " << fname
std::stringstream stream;
stream << "MapViewOfFile() failed, name = " << fname
<< ", error = " << GetLastError() << std::endl;
exit(EXIT_FAILURE);
throw std::runtime_error(stream.str());
}
#endif
uint8_t* data = (uint8_t*)*baseAddress;
@ -276,7 +275,7 @@ public:
if (memcmp(data, Magics[type == WDL], 4))
{
std::cerr << "Corrupted table in file " << fname << std::endl;
sync_cerr << "Corrupted table in file " << fname << sync_endl;
unmap(*baseAddress, *mapping);
return *baseAddress = nullptr, nullptr;
}
@ -445,8 +444,7 @@ class TBTables {
homeBucket = otherHomeBucket;
}
}
std::cerr << "TB hash table size too low!" << std::endl;
exit(EXIT_FAILURE);
throw std::runtime_error("TB hash table size too low!");
}
public:

View File

@ -18,6 +18,7 @@
#include <cassert>
#include <iostream>
#include <algorithm> // For std::count
#include "movegen.h"
#include "search.h"
@ -114,11 +115,15 @@ void Thread::idle_loop() {
cv.wait(lk, [&]{ return searching; });
if (exit)
return;
break;
lk.unlock();
search();
try {
search();
} catch (const std::exception& e) {
sync_cerr << e.what() << sync_endl; // report error and continue
}
}
}

View File

@ -71,9 +71,10 @@ void TranspositionTable::resize(size_t mbSize) {
table = static_cast<Cluster*>(aligned_large_pages_alloc(clusterCount * sizeof(Cluster)));
if (!table)
{
std::cerr << "Failed to allocate " << mbSize
std::stringstream stream;
stream << "Failed to allocate " << mbSize
<< "MB for transposition table." << std::endl;
exit(EXIT_FAILURE);
throw std::runtime_error(stream.str());
}
clear();

View File

@ -70,12 +70,12 @@ static void make_option(const string& n, int v, const SetRange& r) {
LastOption = &Options[n];
// Print formatted parameters, ready to be copy-pasted in Fishtest
std::cout << n << ","
sync_cout << n << ","
<< v << ","
<< r(v).first << "," << r(v).second << ","
<< (r(v).second - r(v).first) / 20.0 << ","
<< "0.0020"
<< std::endl;
<< sync_endl;
}
template<> void Tune::Entry<int>::init_option() { make_option(name, value, range); }

View File

@ -63,6 +63,16 @@
#define ASSERT_ALIGNED(ptr, alignment) assert(reinterpret_cast<uintptr_t>(ptr) % alignment == 0)
int throw_exception(const char* func, const char* file, int line, const char* msg);
int check_expect(int actual, int expect);
#undef assert
#define assert(e) \
(check_expect(!(e), 0) ? (void)0 : (void)throw_exception(__func__, __FILE__, __LINE__, #e))
// TODO: replace non-informative assert with assert_m
#define assert_m(e, m) \
(check_expect(!(e), 0) ? (void)0 : (void)throw_exception(__func__, __FILE__, __LINE__, #m))
#if defined(_WIN64) && defined(_MSC_VER) // No Makefile used
# include <intrin.h> // Microsoft header for _BitScanForward64()
# define IS_64BIT

View File

@ -16,7 +16,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cassert>
#include <cmath>
#include <iostream>
#include <sstream>
@ -172,7 +171,7 @@ namespace {
if (token == "go" || token == "eval")
{
cerr << "\nPosition: " << cnt++ << '/' << num << " (" << pos.fen() << ")" << endl;
sync_cerr << "\nPosition: " << cnt++ << '/' << num << " (" << pos.fen() << ")" << sync_endl;
if (token == "go")
{
go(pos, is, states);
@ -191,10 +190,10 @@ namespace {
dbg_print(); // Just before exiting
cerr << "\n==========================="
sync_cerr << "\n==========================="
<< "\nTotal time (ms) : " << elapsed
<< "\nNodes searched : " << nodes
<< "\nNodes/second : " << 1000 * nodes / elapsed << endl;
<< "\nNodes/second : " << 1000 * nodes / elapsed << sync_endl;
}
// The win rate model returns the probability (per mille) of winning given an eval
@ -222,35 +221,30 @@ namespace {
} // namespace
/// UCI::loop() waits for a command from stdin, parses it and calls the appropriate
/// function. Also intercepts EOF from stdin to ensure gracefully exiting if the
/// GUI dies unexpectedly. When called with some command line arguments, e.g. to
/// run 'bench', once the command is executed the function returns immediately.
static StateListPtr states(new std::deque<StateInfo>(1));
static Position pos;
void UCI::init_pos() {
pos.set(StartFEN, false, &states->back(), Threads.main());
}
/// UCI::execute parses the command and calls the appropriate function.
/// In addition to the UCI ones, also some additional debug commands are supported.
void UCI::loop(int argc, char* argv[]) {
Position pos;
string token, cmd;
StateListPtr states(new std::deque<StateInfo>(1));
pos.set(StartFEN, false, &states->back(), Threads.main());
for (int i = 1; i < argc; ++i)
cmd += std::string(argv[i]) + " ";
do {
if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF
cmd = "quit";
void UCI::execute(std::string cmd) {
try {
string token;
istringstream is(cmd);
token.clear(); // Avoid a stale if getline() returns empty or blank line
is >> skipws >> token;
if ( token == "quit"
|| token == "stop")
if (token == "quit") {
Threads.stop = true;
Threads.set(0);
} else if (token == "stop") {
Threads.stop = true;
}
// The GUI sends 'ponderhit' to tell us the user has played the expected move.
// So 'ponderhit' will be sent if we were told to ponder on the same move the
@ -286,9 +280,12 @@ void UCI::loop(int argc, char* argv[]) {
Eval::NNUE::save_eval(filename);
}
else if (!token.empty() && token[0] != '#')
sync_cout << "Unknown command: " << cmd << sync_endl;
} while (token != "quit" && argc == 1); // Command line args are one-shot
throw std::invalid_argument("Unknown command: " + cmd);
} catch (const std::exception& e) {
sync_cerr << e.what() << sync_endl;
} catch(...) {
sync_cerr << "unknown error" << sync_endl;
}
}
@ -368,13 +365,13 @@ string UCI::move(Move m, bool chess960) {
/// UCI::to_move() converts a string representing a move in coordinate notation
/// (g1f3, a7a8q) to the corresponding legal Move, if any.
Move UCI::to_move(const Position& pos, string& str) {
Move UCI::to_move(const Position& _pos, string& str) {
if (str.length() == 5) // Junior could send promotion piece in uppercase
str[4] = char(tolower(str[4]));
for (const auto& m : MoveList<LEGAL>(pos))
if (str == UCI::move(m, pos.is_chess960()))
for (const auto& m : MoveList<LEGAL>(_pos))
if (str == UCI::move(m, _pos.is_chess960()))
return m;
return MOVE_NONE;

View File

@ -23,6 +23,10 @@
#include <string>
#include "types.h"
#include "misc.h"
int sf_init();
void unblock_readers();
namespace Stockfish {
@ -59,7 +63,7 @@ public:
bool operator==(const char*) const;
private:
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
friend Outstream& operator<<(Outstream&, const OptionsMap&);
std::string defaultValue, currentValue, type;
int min, max;
@ -68,13 +72,14 @@ private:
};
void init(OptionsMap&);
void loop(int argc, char* argv[]);
void init_pos();
void execute(std::string cmd);
std::string value(Value v);
std::string square(Square s);
std::string move(Move m, bool chess960);
std::string pv(const Position& pos, Depth depth, Value alpha, Value beta);
std::string wdl(Value v, int ply);
Move to_move(const Position& pos, std::string& str);
Move to_move(const Position& _pos, std::string& str);
} // namespace UCI

View File

@ -17,7 +17,6 @@
*/
#include <algorithm>
#include <cassert>
#include <ostream>
#include <sstream>
@ -79,7 +78,11 @@ void init(OptionsMap& o) {
o["SyzygyProbeDepth"] << Option(1, 1, 100);
o["Syzygy50MoveRule"] << Option(true);
o["SyzygyProbeLimit"] << Option(7, 0, 7);
#ifndef NNUE_EMBEDDING_OFF
o["Use NNUE"] << Option(true, on_use_NNUE);
#else
o["Use NNUE"] << Option(false, on_use_NNUE);
#endif
o["EvalFile"] << Option(EvalFileDefaultName, on_eval_file);
}
@ -87,7 +90,7 @@ void init(OptionsMap& o) {
/// operator<<() is used to print all the options default values in chronological
/// insertion order (the idx field) and in the format defined by the UCI protocol.
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
Outstream& operator<<(Outstream& os, const OptionsMap& om) {
for (size_t idx = 0; idx < om.size(); ++idx)
for (const auto& it : om)