├── .editorconfig
├── .gitattributes
├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── .idea
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── copyright
│ ├── MIT.xml
│ └── profiles_settings.xml
├── encodings.xml
├── gradle.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── pulse-go.iml
├── scopes
│ ├── C___Sources.xml
│ ├── Go_Sources.xml
│ ├── Java_Sources.xml
│ └── Kotlin_Sources.xml
└── vcs.xml
├── .run
├── Run Pulse C++.run.xml
├── Run Pulse Go.run.xml
├── Run Pulse Java.run.xml
├── Run Pulse Kotlin Linux.run.xml
├── Run Pulse Kotlin Windows.run.xml
└── Run Pulse Kotlin macOS.run.xml
├── CMakeLists.txt
├── LICENSE
├── README.md
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── pulse-cpp
├── src
│ ├── CMakeLists.txt
│ ├── bitboard.cpp
│ ├── bitboard.h
│ ├── evaluation.cpp
│ ├── evaluation.h
│ ├── main.cpp
│ ├── model
│ │ ├── castling.cpp
│ │ ├── castling.h
│ │ ├── castlingtype.h
│ │ ├── color.cpp
│ │ ├── color.h
│ │ ├── depth.h
│ │ ├── file.cpp
│ │ ├── file.h
│ │ ├── move.cpp
│ │ ├── move.h
│ │ ├── movetype.h
│ │ ├── piece.cpp
│ │ ├── piece.h
│ │ ├── piecetype.cpp
│ │ ├── piecetype.h
│ │ ├── rank.cpp
│ │ ├── rank.h
│ │ ├── square.cpp
│ │ ├── square.h
│ │ ├── value.cpp
│ │ └── value.h
│ ├── movegenerator.cpp
│ ├── movegenerator.h
│ ├── movelist.cpp
│ ├── movelist.h
│ ├── notation.cpp
│ ├── notation.h
│ ├── perft.cpp
│ ├── perft.h
│ ├── position.cpp
│ ├── position.h
│ ├── protocol.h
│ ├── pulse.cpp
│ ├── pulse.h
│ ├── search.cpp
│ ├── search.h
│ └── threadpool.h
└── test
│ ├── CMakeLists.txt
│ ├── CMakeLists.txt.in
│ ├── bitboardtest.cpp
│ ├── evaluationtest.cpp
│ ├── model
│ ├── castlingtest.cpp
│ ├── castlingtypetest.cpp
│ ├── colortest.cpp
│ ├── filetest.cpp
│ ├── movetest.cpp
│ ├── piecetest.cpp
│ ├── piecetypetest.cpp
│ ├── ranktest.cpp
│ └── squaretest.cpp
│ ├── movegeneratortest.cpp
│ ├── movelisttest.cpp
│ ├── notationtest.cpp
│ ├── positiontest.cpp
│ └── threadpooltest.cpp
├── pulse-go
├── Taskfile.yaml
├── cmd
│ └── pulse-go
│ │ └── pulse-go.go
├── go.mod
├── go.sum
├── internal
│ └── pulse
│ │ ├── engine
│ │ ├── attack.go
│ │ ├── bitboard.go
│ │ ├── bitboard_test.go
│ │ ├── castling.go
│ │ ├── castling_test.go
│ │ ├── castling_type.go
│ │ ├── castling_type_test.go
│ │ ├── color.go
│ │ ├── color_test.go
│ │ ├── depth.go
│ │ ├── direction.go
│ │ ├── file.go
│ │ ├── file_test.go
│ │ ├── move.go
│ │ ├── move_generator.go
│ │ ├── move_list.go
│ │ ├── move_list_test.go
│ │ ├── move_test.go
│ │ ├── move_type.go
│ │ ├── move_type_test.go
│ │ ├── piece.go
│ │ ├── piece_test.go
│ │ ├── piece_type.go
│ │ ├── piece_type_test.go
│ │ ├── position.go
│ │ ├── position_test.go
│ │ ├── rank.go
│ │ ├── rank_test.go
│ │ ├── square.go
│ │ ├── square_test.go
│ │ └── value.go
│ │ ├── perft.go
│ │ ├── pulse.go
│ │ └── uci
│ │ ├── engine.go
│ │ ├── mock
│ │ └── engine.go
│ │ ├── notation.go
│ │ ├── notation_test.go
│ │ ├── reader.go
│ │ ├── receiver.go
│ │ ├── receiver_test.go
│ │ ├── sender.go
│ │ ├── sender_test.go
│ │ └── writer.go
├── test
│ └── move_generator_test.go
└── vendor
│ ├── go.uber.org
│ └── mock
│ │ ├── AUTHORS
│ │ ├── LICENSE
│ │ └── gomock
│ │ ├── call.go
│ │ ├── callset.go
│ │ ├── controller.go
│ │ ├── doc.go
│ │ ├── matchers.go
│ │ └── string.go
│ └── modules.txt
├── pulse-java
├── pulse-java.gradle.kts
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── fluxchess
│ │ └── pulse
│ │ └── java
│ │ ├── Bitboard.java
│ │ ├── Evaluation.java
│ │ ├── Main.java
│ │ ├── MoveGenerator.java
│ │ ├── MoveList.java
│ │ ├── Notation.java
│ │ ├── Perft.java
│ │ ├── Position.java
│ │ ├── Protocol.java
│ │ ├── Pulse.java
│ │ ├── Search.java
│ │ └── model
│ │ ├── Castling.java
│ │ ├── CastlingType.java
│ │ ├── Color.java
│ │ ├── Depth.java
│ │ ├── File.java
│ │ ├── Move.java
│ │ ├── MoveType.java
│ │ ├── Piece.java
│ │ ├── PieceType.java
│ │ ├── Rank.java
│ │ ├── Square.java
│ │ └── Value.java
│ └── test
│ └── java
│ └── com
│ └── fluxchess
│ └── pulse
│ └── java
│ ├── BitboardTest.java
│ ├── EvaluationTest.java
│ ├── MoveGeneratorTest.java
│ ├── MoveListTest.java
│ ├── NotationTest.java
│ ├── PositionTest.java
│ ├── PulseTest.java
│ ├── SearchTest.java
│ └── model
│ ├── CastlingTest.java
│ ├── CastlingTypeTest.java
│ ├── ColorTest.java
│ ├── FileTest.java
│ ├── MoveTest.java
│ ├── PieceTest.java
│ ├── PieceTypeTest.java
│ ├── RankTest.java
│ └── SquareTest.java
├── pulse-kotlin
├── pulse-kotlin.gradle.kts
└── src
│ ├── commonMain
│ └── kotlin
│ │ ├── Main.kt
│ │ └── com
│ │ └── fluxchess
│ │ └── pulse
│ │ └── kotlin
│ │ ├── Perft.kt
│ │ ├── Pulse.kt
│ │ ├── engine
│ │ ├── Attack.kt
│ │ ├── Bitboard.kt
│ │ ├── Castling.kt
│ │ ├── CastlingType.kt
│ │ ├── Color.kt
│ │ ├── Depth.kt
│ │ ├── Direction.kt
│ │ ├── File.kt
│ │ ├── Move.kt
│ │ ├── MoveGenerator.kt
│ │ ├── MoveList.kt
│ │ ├── MoveType.kt
│ │ ├── Piece.kt
│ │ ├── PieceType.kt
│ │ ├── Position.kt
│ │ ├── Rank.kt
│ │ ├── Square.kt
│ │ └── Value.kt
│ │ └── uci
│ │ ├── Engine.kt
│ │ ├── Notation.kt
│ │ ├── Reader.kt
│ │ ├── Receiver.kt
│ │ ├── Sender.kt
│ │ └── Writer.kt
│ └── commonTest
│ └── kotlin
│ └── com
│ └── fluxchess
│ └── pulse
│ └── kotlin
│ ├── engine
│ ├── BitboardTest.kt
│ ├── CastlingTest.kt
│ ├── CastlingTypeTest.kt
│ ├── ColorTest.kt
│ ├── FileTest.kt
│ ├── MoveListTest.kt
│ ├── MoveTest.kt
│ ├── MoveTypeTest.kt
│ ├── PieceTest.kt
│ ├── PieceTypeTest.kt
│ ├── PositionTest.kt
│ ├── RankTest.kt
│ └── SquareTest.kt
│ ├── integration
│ └── MoveGeneratorTest.kt
│ └── uci
│ ├── DefaultReceiverTest.kt
│ ├── DefaultSenderTest.kt
│ └── NotationTest.kt
├── settings.gradle.kts
└── src
└── main
└── dist
└── logo.bmp
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = tab
5 | indent_size = 4
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.{yml,yaml}]
12 | indent_size = 2
13 |
14 | [*.{kt,kts}]
15 | ij_kotlin_allow_trailing_comma = true
16 | ij_kotlin_allow_trailing_comma_on_call_site = true
17 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
3 | *.cmd eol=crlf
4 | *.bat eol=crlf
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Gradle
2 | /.gradle/
3 | /*/build/
4 |
5 | # CMake
6 | /build/
7 | cmake-build-debug/
8 |
9 | # IntelliJ IDEA
10 | /.idea/
11 |
12 | # Visual Studio
13 | /.vs/
14 |
15 | # Kotlin
16 | /.kotlin/
17 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/copyright/MIT.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/pulse-go.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/scopes/C___Sources.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/scopes/Go_Sources.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/scopes/Java_Sources.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/scopes/Kotlin_Sources.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.run/Run Pulse C++.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.run/Run Pulse Go.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.run/Run Pulse Java.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | true
19 | true
20 | false
21 |
22 |
23 |
--------------------------------------------------------------------------------
/.run/Run Pulse Kotlin Linux.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | true
19 | true
20 | false
21 | false
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.run/Run Pulse Kotlin Windows.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | true
19 | true
20 | false
21 | false
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.run/Run Pulse Kotlin macOS.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | true
19 | true
20 | false
21 | false
22 |
23 |
24 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.10)
2 | project(pulse VERSION 2.0.0)
3 |
4 | if (CMAKE_SYSTEM_NAME STREQUAL Windows)
5 | set(PLATFORM_SUFFIX windows)
6 | set(CPACK_GENERATOR ZIP)
7 | elseif (CMAKE_SYSTEM_NAME STREQUAL Linux)
8 | set(PLATFORM_SUFFIX linux)
9 | set(CPACK_GENERATOR TGZ)
10 | else ()
11 | message(FATAL_ERROR "Unknown platform ${CMAKE_SYSTEM_NAME}")
12 | endif ()
13 |
14 | set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-cpp-${PLATFORM_SUFFIX}-${pulse_VERSION}")
15 | include(CPack)
16 |
17 | enable_testing()
18 |
19 | set(CMAKE_CXX_STANDARD 17)
20 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
21 | set(CMAKE_CXX_EXTENSIONS OFF)
22 |
23 | set(THREADS_PREFER_PTHREAD_FLAG ON)
24 | find_package(Threads REQUIRED)
25 |
26 | add_subdirectory(pulse-cpp/src)
27 | add_subdirectory(pulse-cpp/test)
28 |
29 | install(FILES README.md LICENSE src/main/dist/logo.bmp DESTINATION .)
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) 2013-2023 Phokham Nonava
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | version=2.0.0-SNAPSHOT
2 | group=com.fluxchess.pulse
3 | org.gradle.parallel=true
4 | org.gradle.caching=true
5 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | assertj = "3.24.2"
3 | jcpi = "1.4.1"
4 | junit = "5.9.2"
5 | kotlin = "2.0.21"
6 |
7 | [libraries]
8 | assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" }
9 | jcpi = { module = "com.fluxchess.jcpi:jcpi", version.ref = "jcpi" }
10 | junit = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
11 | kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
12 |
13 | [plugins]
14 | kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
15 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluxroot/pulse/7a06b87b61e90adaeb362e72f0659131141092e9/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/pulse-cpp/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.10)
2 | project(main)
3 |
4 | add_library(core STATIC
5 | bitboard.cpp
6 | model/castling.cpp
7 | model/color.cpp
8 | evaluation.cpp
9 | notation.cpp
10 | model/file.cpp
11 | model/move.cpp
12 | movegenerator.cpp
13 | movelist.cpp
14 | perft.cpp
15 | model/piece.cpp
16 | model/piecetype.cpp
17 | position.cpp
18 | pulse.cpp
19 | model/rank.cpp
20 | search.cpp
21 | model/square.cpp
22 | model/value.cpp
23 | )
24 |
25 | add_executable(pulse main.cpp)
26 | set_target_properties(pulse PROPERTIES OUTPUT_NAME "pulse-cpp-${PLATFORM_SUFFIX}-${pulse_VERSION}")
27 |
28 | target_link_libraries(pulse core Threads::Threads)
29 |
30 | install(TARGETS pulse DESTINATION .)
31 |
--------------------------------------------------------------------------------
/pulse-cpp/src/bitboard.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "bitboard.h"
7 |
8 | namespace pulse::bitboard {
9 | namespace {
10 | constexpr uint64_t DEBRUIJN64 = 0x03F79D71B4CB0A89ULL;
11 | constexpr std::array lsbTable = {
12 | 0, 47, 1, 56, 48, 27, 2, 60,
13 | 57, 49, 41, 37, 28, 16, 3, 61,
14 | 54, 58, 35, 52, 50, 42, 21, 44,
15 | 38, 32, 29, 23, 17, 11, 4, 62,
16 | 46, 55, 26, 59, 40, 36, 15, 53,
17 | 34, 51, 20, 43, 31, 22, 10, 45,
18 | 25, 39, 14, 33, 19, 30, 9, 24,
19 | 13, 18, 8, 12, 7, 6, 5, 63
20 | };
21 |
22 | int toX88Square(int square) {
23 | return ((square & ~7) << 1) | (square & 7);
24 | }
25 |
26 | int toBitSquare(int square) {
27 | return ((square & ~7) >> 1) | (square & 7);
28 | }
29 | }
30 |
31 | uint64_t add(int square, uint64_t bitboard) {
32 | return bitboard | 1ULL << toBitSquare(square);
33 | }
34 |
35 | uint64_t remove(int square, uint64_t bitboard) {
36 | return bitboard & ~(1ULL << toBitSquare(square));
37 | }
38 |
39 | int next(uint64_t bitboard) {
40 | return toX88Square(numberOfTrailingZeros(bitboard));
41 | }
42 |
43 | uint64_t remainder(uint64_t bitboard) {
44 | return bitboard & (bitboard - 1);
45 | }
46 |
47 | int size(uint64_t bitboard) {
48 | return bitCount(bitboard);
49 | }
50 |
51 | int numberOfTrailingZeros(uint64_t b) {
52 | return lsbTable[((b ^ (b - 1)) * DEBRUIJN64) >> 58];
53 | }
54 |
55 | int bitCount(uint64_t b) {
56 | b = b - ((b >> 1) & 0x5555555555555555ULL);
57 | b = (b & 0x3333333333333333ULL) + ((b >> 2) & 0x3333333333333333ULL);
58 | b = (b + (b >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
59 | return (b * 0x0101010101010101ULL) >> 56;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/pulse-cpp/src/bitboard.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include
8 | #include
9 |
10 | namespace pulse::bitboard {
11 |
12 | uint64_t add(int square, uint64_t bitboard);
13 |
14 | uint64_t remove(int square, uint64_t bitboard);
15 |
16 | int next(uint64_t bitboard);
17 |
18 | uint64_t remainder(uint64_t bitboard);
19 |
20 | int size(uint64_t bitboard);
21 |
22 | int numberOfTrailingZeros(uint64_t b);
23 |
24 | int bitCount(uint64_t b);
25 | }
26 |
--------------------------------------------------------------------------------
/pulse-cpp/src/evaluation.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include "position.h"
8 |
9 | namespace pulse::evaluation {
10 |
11 | constexpr int TEMPO = 1;
12 |
13 | int evaluate(Position& position);
14 | }
15 |
--------------------------------------------------------------------------------
/pulse-cpp/src/main.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "pulse.h"
7 | #include "perft.h"
8 |
9 | #include
10 |
11 | void printUsage() {
12 | std::cerr << "Usage: pulse-cpp [perft]" << std::endl;
13 | }
14 |
15 | int main(int argc, char* argv[]) {
16 | if (argc == 1) {
17 | std::unique_ptr pulse(new pulse::Pulse());
18 | pulse->run();
19 | } else if (argc == 2) {
20 | std::string token(argv[1]);
21 | if (token == "perft") {
22 | std::unique_ptr perft(new pulse::Perft());
23 | perft->run();
24 | } else {
25 | printUsage();
26 | return 1;
27 | }
28 | } else {
29 | printUsage();
30 | return 1;
31 | }
32 | return 0;
33 | }
34 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/castling.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include
7 | #include "castling.h"
8 | #include "color.h"
9 | #include "castlingtype.h"
10 |
11 | namespace pulse::castling {
12 |
13 | int valueOf(int color, int castlingtype) {
14 | switch (color) {
15 | case color::WHITE:
16 | switch (castlingtype) {
17 | case castlingtype::KINGSIDE:
18 | return WHITE_KINGSIDE;
19 | case castlingtype::QUEENSIDE:
20 | return WHITE_QUEENSIDE;
21 | default:
22 | throw std::exception();
23 | }
24 | case color::BLACK:
25 | switch (castlingtype) {
26 | case castlingtype::KINGSIDE:
27 | return BLACK_KINGSIDE;
28 | case castlingtype::QUEENSIDE:
29 | return BLACK_QUEENSIDE;
30 | default:
31 | throw std::exception();
32 | }
33 | default:
34 | throw std::exception();
35 | }
36 | }
37 |
38 | int getType(int castling) {
39 | switch (castling) {
40 | case WHITE_KINGSIDE:
41 | case BLACK_KINGSIDE:
42 | return castlingtype::KINGSIDE;
43 | case WHITE_QUEENSIDE:
44 | case BLACK_QUEENSIDE:
45 | return castlingtype::QUEENSIDE;
46 | default:
47 | throw std::exception();
48 | }
49 | }
50 |
51 | int getColor(int castling) {
52 | switch (castling) {
53 | case WHITE_KINGSIDE:
54 | case WHITE_QUEENSIDE:
55 | return color::WHITE;
56 | case BLACK_KINGSIDE:
57 | case BLACK_QUEENSIDE:
58 | return color::BLACK;
59 | default:
60 | throw std::exception();
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/castling.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include
8 |
9 | namespace pulse::castling {
10 |
11 | constexpr int WHITE_KINGSIDE = 1 << 0;
12 | constexpr int WHITE_QUEENSIDE = 1 << 1;
13 | constexpr int BLACK_KINGSIDE = 1 << 2;
14 | constexpr int BLACK_QUEENSIDE = 1 << 3;
15 |
16 | constexpr int NOCASTLING = 0;
17 |
18 | constexpr int VALUES_LENGTH = 16;
19 |
20 | int valueOf(int color, int castlingtype);
21 |
22 | int getType(int castling);
23 |
24 | int getColor(int castling);
25 | }
26 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/castlingtype.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include
8 |
9 | namespace pulse::castlingtype {
10 |
11 | constexpr int KINGSIDE = 0;
12 | constexpr int QUEENSIDE = 1;
13 |
14 | constexpr int NOCASTLINGTYPE = 2;
15 |
16 | constexpr int VALUES_SIZE = 2;
17 | constexpr std::array values = {
18 | KINGSIDE, QUEENSIDE
19 | };
20 | }
21 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/color.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include
7 | #include "color.h"
8 |
9 | namespace pulse::color {
10 |
11 | int opposite(int color) {
12 | switch (color) {
13 | case WHITE:
14 | return BLACK;
15 | case BLACK:
16 | return WHITE;
17 | default:
18 | throw std::exception();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/color.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include
8 |
9 | namespace pulse::color {
10 |
11 | constexpr int WHITE = 0;
12 | constexpr int BLACK = 1;
13 |
14 | constexpr int NOCOLOR = 2;
15 |
16 | constexpr int VALUES_SIZE = 2;
17 | constexpr std::array values = {
18 | WHITE, BLACK
19 | };
20 |
21 | int opposite(int color);
22 | }
23 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/depth.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | namespace pulse::depth {
8 |
9 | constexpr int MAX_PLY = 256;
10 | constexpr int MAX_DEPTH = 64;
11 | }
12 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/file.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "file.h"
7 |
8 | namespace pulse::file {
9 |
10 | bool isValid(int file) {
11 | switch (file) {
12 | case a:
13 | case b:
14 | case c:
15 | case d:
16 | case e:
17 | case f:
18 | case g:
19 | case h:
20 | return true;
21 | default:
22 | return false;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/file.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include
8 |
9 | namespace pulse::file {
10 |
11 | constexpr int a = 0;
12 | constexpr int b = 1;
13 | constexpr int c = 2;
14 | constexpr int d = 3;
15 | constexpr int e = 4;
16 | constexpr int f = 5;
17 | constexpr int g = 6;
18 | constexpr int h = 7;
19 |
20 | constexpr int NOFILE = 8;
21 |
22 | constexpr int VALUES_SIZE = 8;
23 | constexpr std::array values = {
24 | a, b, c, d, e, f, g, h
25 | };
26 |
27 | bool isValid(int file);
28 | }
29 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/move.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "move.h"
7 |
8 | namespace pulse::move {
9 | int valueOf(int type, int originSquare, int targetSquare, int originPiece, int targetPiece, int promotion) {
10 | int move = 0;
11 |
12 | // Encode type
13 | move |= type << TYPE_SHIFT;
14 |
15 | // Encode origin square
16 | move |= originSquare << ORIGIN_SQUARE_SHIFT;
17 |
18 | // Encode target square
19 | move |= targetSquare << TARGET_SQUARE_SHIFT;
20 |
21 | // Encode origin piece
22 | move |= originPiece << ORIGIN_PIECE_SHIFT;
23 |
24 | // Encode target piece
25 | move |= targetPiece << TARGET_PIECE_SHIFT;
26 |
27 | // Encode promotion
28 | move |= promotion << PROMOTION_SHIFT;
29 |
30 | return move;
31 | }
32 |
33 | int getType(int move) {
34 | return (move & TYPE_MASK) >> TYPE_SHIFT;
35 | }
36 |
37 | int getOriginSquare(int move) {
38 | return (move & ORIGIN_SQUARE_MASK) >> ORIGIN_SQUARE_SHIFT;
39 | }
40 |
41 | int getTargetSquare(int move) {
42 | return (move & TARGET_SQUARE_MASK) >> TARGET_SQUARE_SHIFT;
43 | }
44 |
45 | int getOriginPiece(int move) {
46 | return (move & ORIGIN_PIECE_MASK) >> ORIGIN_PIECE_SHIFT;
47 | }
48 |
49 | int getTargetPiece(int move) {
50 | return (move & TARGET_PIECE_MASK) >> TARGET_PIECE_SHIFT;
51 | }
52 |
53 | int getPromotion(int move) {
54 | return (move & PROMOTION_MASK) >> PROMOTION_SHIFT;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/move.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include "square.h"
8 | #include "piece.h"
9 | #include "piecetype.h"
10 | #include "movetype.h"
11 |
12 | /**
13 | * A move is encoded as a int value. The fields are represented by
14 | * the following bits.
15 | *
16 | * 0 - 2
: type (required)
17 | * 3 - 9
: origin square (required)
18 | * 10 - 16
: target square (required)
19 | * 17 - 21
: origin piece (required)
20 | * 22 - 26
: target piece (optional)
21 | * 27 - 29
: promotion type (optional)
22 | *
23 | */
24 | namespace pulse::move {
25 | namespace {
26 | // These are our bit masks
27 | constexpr int TYPE_SHIFT = 0;
28 | constexpr int TYPE_MASK = movetype::MASK << TYPE_SHIFT;
29 | constexpr int ORIGIN_SQUARE_SHIFT = 3;
30 | constexpr int ORIGIN_SQUARE_MASK = square::MASK << ORIGIN_SQUARE_SHIFT;
31 | constexpr int TARGET_SQUARE_SHIFT = 10;
32 | constexpr int TARGET_SQUARE_MASK = square::MASK << TARGET_SQUARE_SHIFT;
33 | constexpr int ORIGIN_PIECE_SHIFT = 17;
34 | constexpr int ORIGIN_PIECE_MASK = piece::MASK << ORIGIN_PIECE_SHIFT;
35 | constexpr int TARGET_PIECE_SHIFT = 22;
36 | constexpr int TARGET_PIECE_MASK = piece::MASK << TARGET_PIECE_SHIFT;
37 | constexpr int PROMOTION_SHIFT = 27;
38 | constexpr int PROMOTION_MASK = piecetype::MASK << PROMOTION_SHIFT;
39 | }
40 |
41 | // We don't use 0 as a null value to protect against errors.
42 | constexpr int NOMOVE = (movetype::NOMOVETYPE << TYPE_SHIFT)
43 | | (square::NOSQUARE << ORIGIN_SQUARE_SHIFT)
44 | | (square::NOSQUARE << TARGET_SQUARE_SHIFT)
45 | | (piece::NOPIECE << ORIGIN_PIECE_SHIFT)
46 | | (piece::NOPIECE << TARGET_PIECE_SHIFT)
47 | | (piecetype::NOPIECETYPE << PROMOTION_SHIFT);
48 |
49 | int valueOf(int type, int originSquare, int targetSquare, int originPiece, int targetPiece, int promotion);
50 |
51 | int getType(int move);
52 |
53 | int getOriginSquare(int move);
54 |
55 | int getTargetSquare(int move);
56 |
57 | int getOriginPiece(int move);
58 |
59 | int getTargetPiece(int move);
60 |
61 | int getPromotion(int move);
62 | }
63 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/movetype.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | namespace pulse::movetype {
8 |
9 | constexpr int MASK = 0x7;
10 |
11 | constexpr int NORMAL = 0;
12 | constexpr int PAWNDOUBLE = 1;
13 | constexpr int PAWNPROMOTION = 2;
14 | constexpr int ENPASSANT = 3;
15 | constexpr int CASTLING = 4;
16 |
17 | constexpr int NOMOVETYPE = 5;
18 | }
19 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/piece.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include
7 | #include "piece.h"
8 | #include "color.h"
9 | #include "piecetype.h"
10 |
11 | namespace pulse::piece {
12 |
13 | bool isValid(int piece) {
14 | switch (piece) {
15 | case WHITE_PAWN:
16 | case WHITE_KNIGHT:
17 | case WHITE_BISHOP:
18 | case WHITE_ROOK:
19 | case WHITE_QUEEN:
20 | case WHITE_KING:
21 | case BLACK_PAWN:
22 | case BLACK_KNIGHT:
23 | case BLACK_BISHOP:
24 | case BLACK_ROOK:
25 | case BLACK_QUEEN:
26 | case BLACK_KING:
27 | return true;
28 | default:
29 | return false;
30 | }
31 | }
32 |
33 | int valueOf(int color, int piecetype) {
34 | switch (color) {
35 | case color::WHITE:
36 | switch (piecetype) {
37 | case piecetype::PAWN:
38 | return WHITE_PAWN;
39 | case piecetype::KNIGHT:
40 | return WHITE_KNIGHT;
41 | case piecetype::BISHOP:
42 | return WHITE_BISHOP;
43 | case piecetype::ROOK:
44 | return WHITE_ROOK;
45 | case piecetype::QUEEN:
46 | return WHITE_QUEEN;
47 | case piecetype::KING:
48 | return WHITE_KING;
49 | default:
50 | throw std::exception();
51 | }
52 | case color::BLACK:
53 | switch (piecetype) {
54 | case piecetype::PAWN:
55 | return BLACK_PAWN;
56 | case piecetype::KNIGHT:
57 | return BLACK_KNIGHT;
58 | case piecetype::BISHOP:
59 | return BLACK_BISHOP;
60 | case piecetype::ROOK:
61 | return BLACK_ROOK;
62 | case piecetype::QUEEN:
63 | return BLACK_QUEEN;
64 | case piecetype::KING:
65 | return BLACK_KING;
66 | default:
67 | throw std::exception();
68 | }
69 | default:
70 | throw std::exception();
71 | }
72 | }
73 |
74 | int getType(int piece) {
75 | switch (piece) {
76 | case WHITE_PAWN:
77 | case BLACK_PAWN:
78 | return piecetype::PAWN;
79 | case WHITE_KNIGHT:
80 | case BLACK_KNIGHT:
81 | return piecetype::KNIGHT;
82 | case WHITE_BISHOP:
83 | case BLACK_BISHOP:
84 | return piecetype::BISHOP;
85 | case WHITE_ROOK:
86 | case BLACK_ROOK:
87 | return piecetype::ROOK;
88 | case WHITE_QUEEN:
89 | case BLACK_QUEEN:
90 | return piecetype::QUEEN;
91 | case WHITE_KING:
92 | case BLACK_KING:
93 | return piecetype::KING;
94 | default:
95 | throw std::exception();
96 | }
97 | }
98 |
99 | int getColor(int piece) {
100 | switch (piece) {
101 | case WHITE_PAWN:
102 | case WHITE_KNIGHT:
103 | case WHITE_BISHOP:
104 | case WHITE_ROOK:
105 | case WHITE_QUEEN:
106 | case WHITE_KING:
107 | return color::WHITE;
108 | case BLACK_PAWN:
109 | case BLACK_KNIGHT:
110 | case BLACK_BISHOP:
111 | case BLACK_ROOK:
112 | case BLACK_QUEEN:
113 | case BLACK_KING:
114 | return color::BLACK;
115 | default:
116 | throw std::exception();
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/piece.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include
8 |
9 | namespace pulse::piece {
10 |
11 | constexpr int MASK = 0x1F;
12 |
13 | constexpr int WHITE_PAWN = 0;
14 | constexpr int WHITE_KNIGHT = 1;
15 | constexpr int WHITE_BISHOP = 2;
16 | constexpr int WHITE_ROOK = 3;
17 | constexpr int WHITE_QUEEN = 4;
18 | constexpr int WHITE_KING = 5;
19 | constexpr int BLACK_PAWN = 6;
20 | constexpr int BLACK_KNIGHT = 7;
21 | constexpr int BLACK_BISHOP = 8;
22 | constexpr int BLACK_ROOK = 9;
23 | constexpr int BLACK_QUEEN = 10;
24 | constexpr int BLACK_KING = 11;
25 |
26 | constexpr int NOPIECE = 12;
27 |
28 | constexpr int VALUES_SIZE = 12;
29 | constexpr std::array values = {
30 | WHITE_PAWN, WHITE_KNIGHT, WHITE_BISHOP, WHITE_ROOK, WHITE_QUEEN, WHITE_KING,
31 | BLACK_PAWN, BLACK_KNIGHT, BLACK_BISHOP, BLACK_ROOK, BLACK_QUEEN, BLACK_KING
32 | };
33 |
34 | bool isValid(int piece);
35 |
36 | int valueOf(int color, int piecetype);
37 |
38 | int getType(int piece);
39 |
40 | int getColor(int piece);
41 | }
42 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/piecetype.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include
7 | #include "piecetype.h"
8 |
9 | namespace pulse::piecetype {
10 |
11 | bool isValidPromotion(int piecetype) {
12 | switch (piecetype) {
13 | case KNIGHT:
14 | case BISHOP:
15 | case ROOK:
16 | case QUEEN:
17 | return true;
18 | default:
19 | return false;
20 | }
21 | }
22 |
23 | bool isSliding(int piecetype) {
24 | switch (piecetype) {
25 | case BISHOP:
26 | case ROOK:
27 | case QUEEN:
28 | return true;
29 | case PAWN:
30 | case KNIGHT:
31 | case KING:
32 | return false;
33 | default:
34 | throw std::exception();
35 | }
36 | }
37 |
38 | int getValue(int piecetype) {
39 | switch (piecetype) {
40 | case PAWN:
41 | return PAWN_VALUE;
42 | case KNIGHT:
43 | return KNIGHT_VALUE;
44 | case BISHOP:
45 | return BISHOP_VALUE;
46 | case ROOK:
47 | return ROOK_VALUE;
48 | case QUEEN:
49 | return QUEEN_VALUE;
50 | case KING:
51 | return KING_VALUE;
52 | default:
53 | throw std::exception();
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/piecetype.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include
8 |
9 | namespace pulse::piecetype {
10 |
11 | constexpr int MASK = 0x7;
12 |
13 | constexpr int PAWN = 0;
14 | constexpr int KNIGHT = 1;
15 | constexpr int BISHOP = 2;
16 | constexpr int ROOK = 3;
17 | constexpr int QUEEN = 4;
18 | constexpr int KING = 5;
19 |
20 | constexpr int NOPIECETYPE = 6;
21 |
22 | constexpr int VALUES_SIZE = 6;
23 | constexpr std::array values = {
24 | PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING
25 | };
26 |
27 | // Piece values as defined by Larry Kaufman
28 | constexpr int PAWN_VALUE = 100;
29 | constexpr int KNIGHT_VALUE = 325;
30 | constexpr int BISHOP_VALUE = 325;
31 | constexpr int ROOK_VALUE = 500;
32 | constexpr int QUEEN_VALUE = 975;
33 | constexpr int KING_VALUE = 20000;
34 |
35 | bool isValidPromotion(int piecetype);
36 |
37 | bool isSliding(int piecetype);
38 |
39 | int getValue(int piecetype);
40 | }
41 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/rank.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "rank.h"
7 |
8 | namespace pulse::rank {
9 |
10 | bool isValid(int rank) {
11 | switch (rank) {
12 | case r1:
13 | case r2:
14 | case r3:
15 | case r4:
16 | case r5:
17 | case r6:
18 | case r7:
19 | case r8:
20 | return true;
21 | default:
22 | return false;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/rank.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include
8 |
9 | namespace pulse::rank {
10 |
11 | constexpr int r1 = 0;
12 | constexpr int r2 = 1;
13 | constexpr int r3 = 2;
14 | constexpr int r4 = 3;
15 | constexpr int r5 = 4;
16 | constexpr int r6 = 5;
17 | constexpr int r7 = 6;
18 | constexpr int r8 = 7;
19 |
20 | constexpr int NORANK = 8;
21 |
22 | constexpr int VALUES_SIZE = 8;
23 | constexpr std::array values = {
24 | r1, r2, r3, r4, r5, r6, r7, r8
25 | };
26 |
27 | bool isValid(int rank);
28 | }
29 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/square.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "square.h"
7 |
8 | namespace pulse::square {
9 |
10 | bool isValid(int square) {
11 | return (square & 0x88) == 0;
12 | }
13 |
14 | int valueOf(int file, int rank) {
15 | return (rank << 4) + file;
16 | }
17 |
18 | int getFile(int square) {
19 | return square & 0xF;
20 | }
21 |
22 | int getRank(int square) {
23 | return square >> 4;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/square.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include
8 | #include
9 |
10 | namespace pulse::square {
11 |
12 | constexpr int MASK = 0x7F;
13 |
14 | constexpr int a1 = 0, a2 = 16;
15 | constexpr int b1 = 1, b2 = 17;
16 | constexpr int c1 = 2, c2 = 18;
17 | constexpr int d1 = 3, d2 = 19;
18 | constexpr int e1 = 4, e2 = 20;
19 | constexpr int f1 = 5, f2 = 21;
20 | constexpr int g1 = 6, g2 = 22;
21 | constexpr int h1 = 7, h2 = 23;
22 |
23 | constexpr int a3 = 32, a4 = 48;
24 | constexpr int b3 = 33, b4 = 49;
25 | constexpr int c3 = 34, c4 = 50;
26 | constexpr int d3 = 35, d4 = 51;
27 | constexpr int e3 = 36, e4 = 52;
28 | constexpr int f3 = 37, f4 = 53;
29 | constexpr int g3 = 38, g4 = 54;
30 | constexpr int h3 = 39, h4 = 55;
31 |
32 | constexpr int a5 = 64, a6 = 80;
33 | constexpr int b5 = 65, b6 = 81;
34 | constexpr int c5 = 66, c6 = 82;
35 | constexpr int d5 = 67, d6 = 83;
36 | constexpr int e5 = 68, e6 = 84;
37 | constexpr int f5 = 69, f6 = 85;
38 | constexpr int g5 = 70, g6 = 86;
39 | constexpr int h5 = 71, h6 = 87;
40 |
41 | constexpr int a7 = 96, a8 = 112;
42 | constexpr int b7 = 97, b8 = 113;
43 | constexpr int c7 = 98, c8 = 114;
44 | constexpr int d7 = 99, d8 = 115;
45 | constexpr int e7 = 100, e8 = 116;
46 | constexpr int f7 = 101, f8 = 117;
47 | constexpr int g7 = 102, g8 = 118;
48 | constexpr int h7 = 103, h8 = 119;
49 |
50 | constexpr int NOSQUARE = 127;
51 |
52 | constexpr int VALUES_LENGTH = 128;
53 | constexpr int VALUES_SIZE = 64;
54 | constexpr std::array values = {
55 | a1, b1, c1, d1, e1, f1, g1, h1,
56 | a2, b2, c2, d2, e2, f2, g2, h2,
57 | a3, b3, c3, d3, e3, f3, g3, h3,
58 | a4, b4, c4, d4, e4, f4, g4, h4,
59 | a5, b5, c5, d5, e5, f5, g5, h5,
60 | a6, b6, c6, d6, e6, f6, g6, h6,
61 | a7, b7, c7, d7, e7, f7, g7, h7,
62 | a8, b8, c8, d8, e8, f8, g8, h8
63 | };
64 |
65 | // These are our move directions
66 | // N = north, E = east, S = south, W = west
67 | constexpr int N = 16;
68 | constexpr int E = 1;
69 | constexpr int S = -16;
70 | constexpr int W = -1;
71 | constexpr int NE = N + E;
72 | constexpr int SE = S + E;
73 | constexpr int SW = S + W;
74 | constexpr int NW = N + W;
75 |
76 | inline const std::vector> pawnDirections = {
77 | {N, NE, NW}, // color::WHITE
78 | {S, SE, SW} // color::BLACK
79 | };
80 | inline const std::vector knightDirections = {
81 | N + N + E,
82 | N + N + W,
83 | N + E + E,
84 | N + W + W,
85 | S + S + E,
86 | S + S + W,
87 | S + E + E,
88 | S + W + W
89 | };
90 | inline const std::vector bishopDirections = {
91 | NE, NW, SE, SW
92 | };
93 | inline const std::vector rookDirections = {
94 | N, E, S, W
95 | };
96 | inline const std::vector queenDirections = {
97 | N, E, S, W,
98 | NE, NW, SE, SW
99 | };
100 | inline const std::vector kingDirections = {
101 | N, E, S, W,
102 | NE, NW, SE, SW
103 | };
104 |
105 | bool isValid(int square);
106 |
107 | int valueOf(int file, int rank);
108 |
109 | int getFile(int square);
110 |
111 | int getRank(int square);
112 | }
113 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/value.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "value.h"
7 |
8 | #include
9 |
10 | namespace pulse::value {
11 |
12 | bool isCheckmate(int value) {
13 | int absvalue = std::abs(value);
14 | return absvalue >= CHECKMATE_THRESHOLD && absvalue <= CHECKMATE;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/pulse-cpp/src/model/value.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include "depth.h"
8 |
9 | namespace pulse::value {
10 |
11 | constexpr int INFINITE = 200000;
12 | constexpr int CHECKMATE = 100000;
13 | constexpr int CHECKMATE_THRESHOLD = CHECKMATE - depth::MAX_PLY;
14 | constexpr int DRAW = 0;
15 |
16 | constexpr int NOVALUE = 300000;
17 |
18 | bool isCheckmate(int value);
19 | }
20 |
--------------------------------------------------------------------------------
/pulse-cpp/src/movegenerator.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include "position.h"
8 | #include "movelist.h"
9 |
10 | namespace pulse {
11 |
12 | class MoveGenerator final {
13 | public:
14 | MoveList& getLegalMoves(Position& position, int depth, bool isCheck);
15 |
16 | MoveList& getMoves(Position& position, int depth, bool isCheck);
17 |
18 | private:
19 | MoveList moves;
20 |
21 | static void addMoves(MoveList& list, Position& position);
22 |
23 | static void
24 | addMoves(MoveList& list, int originSquare, const std::vector& directions, Position& position);
25 |
26 | static void addPawnMoves(MoveList& list, int pawnSquare, Position& position);
27 |
28 | static void addCastlingMoves(MoveList& list, int kingSquare, Position& position);
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/pulse-cpp/src/movelist.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "movelist.h"
7 |
8 | namespace pulse {
9 |
10 | template
11 | MoveList::MoveList() {
12 | for (unsigned int i = 0; i < entries.size(); i++) {
13 | entries[i] = std::shared_ptr(new T());
14 | }
15 | }
16 |
17 | /**
18 | * Sorts the move list using a stable insertion sort.
19 | */
20 | template
21 | void MoveList::sort() {
22 | for (int i = 1; i < size; i++) {
23 | std::shared_ptr entry(entries[i]);
24 |
25 | int j = i;
26 | while ((j > 0) && (entries[j - 1]->value < entry->value)) {
27 | entries[j] = entries[j - 1];
28 | j--;
29 | }
30 |
31 | entries[j] = entry;
32 | }
33 | }
34 |
35 | /**
36 | * Rates the moves in the list according to "Most Valuable Victim - Least Valuable Aggressor".
37 | */
38 | template
39 | void MoveList::rateFromMVVLVA() {
40 | for (int i = 0; i < size; i++) {
41 | int move = entries[i]->move;
42 | int value = 0;
43 |
44 | int piecetypeValue = piecetype::getValue(piece::getType(move::getOriginPiece(move)));
45 | value += piecetype::KING_VALUE / piecetypeValue;
46 |
47 | int target = move::getTargetPiece(move);
48 | if (piece::isValid(target)) {
49 | value += 10 * piecetype::getValue(piece::getType(target));
50 | }
51 |
52 | entries[i]->value = value;
53 | }
54 | }
55 |
56 | template
57 | class MoveList;
58 |
59 | template
60 | class MoveList;
61 | }
62 |
--------------------------------------------------------------------------------
/pulse-cpp/src/movelist.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include "model/value.h"
8 | #include "model/move.h"
9 |
10 | #include
11 | #include
12 |
13 | namespace pulse {
14 |
15 | /**
16 | * This class stores our moves for a specific position. For the root node we
17 | * will populate pv for every root move.
18 | */
19 | template
20 | class MoveList final {
21 | private:
22 | static const int MAX_MOVES = 256;
23 |
24 | public:
25 | std::array, MAX_MOVES> entries;
26 | int size = 0;
27 |
28 | MoveList();
29 |
30 | void sort();
31 |
32 | void rateFromMVVLVA();
33 | };
34 |
35 | class MoveVariation final {
36 | public:
37 | std::array moves;
38 | int size = 0;
39 | };
40 |
41 | class MoveEntry {
42 | public:
43 | int move = move::NOMOVE;
44 | int value = value::NOVALUE;
45 | };
46 |
47 | class RootEntry final : public MoveEntry {
48 | public:
49 | MoveVariation pv;
50 | };
51 | }
52 |
--------------------------------------------------------------------------------
/pulse-cpp/src/notation.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include "position.h"
8 |
9 | #include
10 |
11 | namespace pulse::notation {
12 |
13 | constexpr auto STANDARDPOSITION = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
14 |
15 | Position toPosition(const std::string& fen);
16 |
17 | std::string fromPosition(const Position& position);
18 |
19 | int toColor(char notation);
20 |
21 | char fromColor(int color);
22 |
23 | int toPieceType(char notation);
24 |
25 | char fromPieceType(int piecetype);
26 |
27 | int toPiece(char notation);
28 |
29 | char fromPiece(int piece);
30 |
31 | int toCastlingType(char notation);
32 |
33 | char fromCastlingType(int castlingtype);
34 |
35 | int toCastling(char notation);
36 |
37 | char fromCastling(int castling);
38 |
39 | int toFile(char notation);
40 |
41 | char fromFile(int file);
42 |
43 | int toRank(char notation);
44 |
45 | char fromRank(int rank);
46 |
47 | std::string fromSquare(int square);
48 | }
49 |
--------------------------------------------------------------------------------
/pulse-cpp/src/perft.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "perft.h"
7 | #include "notation.h"
8 |
9 | #include
10 | #include
11 | #include
12 |
13 | namespace pulse {
14 |
15 | void Perft::run() {
16 | std::unique_ptr position(new Position(notation::toPosition(notation::STANDARDPOSITION)));
17 | int depth = MAX_DEPTH;
18 |
19 | std::cout << "Testing " << notation::fromPosition(*position) << " at depth " << depth << std::endl;
20 |
21 | auto startTime = std::chrono::system_clock::now();
22 | uint64_t result = miniMax(depth, *position, 0);
23 | auto endTime = std::chrono::system_clock::now();
24 |
25 | auto duration = endTime - startTime;
26 |
27 | std::cout << "Nodes: ";
28 | std::cout << result << std::endl;
29 | std::cout << "Duration: ";
30 | std::cout << std::setfill('0') << std::setw(2)
31 | << std::chrono::duration_cast(duration).count() << ":";
32 | std::cout << std::setfill('0') << std::setw(2)
33 | << (std::chrono::duration_cast(duration)
34 | - std::chrono::duration_cast(
35 | std::chrono::duration_cast(duration))).count() << ":";
36 | std::cout << std::setfill('0') << std::setw(2)
37 | << (std::chrono::duration_cast(duration)
38 | - std::chrono::duration_cast(
39 | std::chrono::duration_cast(duration))).count() << ".";
40 | std::cout << std::setfill('0') << std::setw(2)
41 | << (std::chrono::duration_cast(duration)
42 | - std::chrono::duration_cast(
43 | std::chrono::duration_cast(duration))).count();
44 | std::cout << std::endl;
45 |
46 | std::cout << "n/ms: "
47 | << result / std::chrono::duration_cast(duration).count() << std::endl;
48 | }
49 |
50 | uint64_t Perft::miniMax(int depth, Position& position, int ply) {
51 | if (depth == 0) {
52 | return 1;
53 | }
54 |
55 | uint64_t totalNodes = 0;
56 |
57 | bool isCheck = position.isCheck();
58 | MoveGenerator& moveGenerator = moveGenerators[ply];
59 | MoveList& moves = moveGenerator.getMoves(position, depth, isCheck);
60 | for (int i = 0; i < moves.size; i++) {
61 | int move = moves.entries[i]->move;
62 |
63 | position.makeMove(move);
64 | if (!position.isCheck(color::opposite(position.activeColor))) {
65 | totalNodes += miniMax(depth - 1, position, ply + 1);
66 | }
67 | position.undoMove(move);
68 | }
69 |
70 | return totalNodes;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/pulse-cpp/src/perft.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include "movegenerator.h"
8 |
9 | namespace pulse {
10 |
11 | class Perft final {
12 | public:
13 | void run();
14 |
15 | private:
16 | static const int MAX_DEPTH = 6;
17 |
18 | std::array moveGenerators;
19 |
20 | uint64_t miniMax(int depth, Position& position, int ply);
21 | };
22 | }
23 |
--------------------------------------------------------------------------------
/pulse-cpp/src/position.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include "bitboard.h"
8 | #include "model/color.h"
9 | #include "model/castling.h"
10 | #include "model/square.h"
11 | #include "model/piece.h"
12 | #include "model/piecetype.h"
13 | #include "model/depth.h"
14 |
15 | #include
16 |
17 | namespace pulse {
18 |
19 | class Position final {
20 | public:
21 | std::array board;
22 |
23 | std::array, color::VALUES_SIZE> pieces = {};
24 |
25 | std::array material = {};
26 |
27 | int castlingRights = castling::NOCASTLING;
28 | int enPassantSquare = square::NOSQUARE;
29 | int activeColor = color::WHITE;
30 | int halfmoveClock = 0;
31 |
32 | uint64_t zobristKey = 0;
33 |
34 | Position();
35 |
36 | Position(const Position& position);
37 |
38 | Position& operator=(const Position& position);
39 |
40 | bool operator==(const Position& position) const;
41 |
42 | bool operator!=(const Position& position) const;
43 |
44 | void setActiveColor(int _activeColor);
45 |
46 | void setCastlingRight(int castling);
47 |
48 | void setEnPassantSquare(int _enPassantSquare);
49 |
50 | void setHalfmoveClock(int _halfmoveClock);
51 |
52 | int getFullmoveNumber() const;
53 |
54 | void setFullmoveNumber(int fullmoveNumber);
55 |
56 | bool isRepetition();
57 |
58 | bool hasInsufficientMaterial();
59 |
60 | void put(int piece, int square);
61 |
62 | int remove(int square);
63 |
64 | void makeMove(int move);
65 |
66 | void undoMove(int move);
67 |
68 | bool isCheck();
69 |
70 | bool isCheck(int color);
71 |
72 | bool isAttacked(int targetSquare, int attackerColor);
73 |
74 | private:
75 | class Zobrist final {
76 | public:
77 | std::array, piece::VALUES_SIZE> board;
78 | std::array castlingRights;
79 | std::array enPassantSquare;
80 | uint64_t activeColor;
81 |
82 | static Zobrist& instance();
83 |
84 | private:
85 | std::independent_bits_engine generator;
86 |
87 | Zobrist();
88 |
89 | uint64_t next();
90 | };
91 |
92 | class State final {
93 | public:
94 | uint64_t zobristKey = 0;
95 | int castlingRights = castling::NOCASTLING;
96 | int enPassantSquare = square::NOSQUARE;
97 | int halfmoveClock = 0;
98 | };
99 |
100 | static const int MAX_MOVES = depth::MAX_PLY + 1024;
101 |
102 | int halfmoveNumber = 2;
103 |
104 | // We will save some position parameters in a State before making a move.
105 | // Later we will restore them before undoing a move.
106 | std::array states;
107 | int statesSize = 0;
108 |
109 | Zobrist& zobrist;
110 |
111 | void clearCastling(int square);
112 |
113 | bool isAttacked(int targetSquare, int attackerPiece, const std::vector& directions);
114 |
115 | bool isAttacked(int targetSquare, int attackerPiece, int queenPiece, const std::vector& directions);
116 | };
117 | }
118 |
--------------------------------------------------------------------------------
/pulse-cpp/src/protocol.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include
8 | #include "movelist.h"
9 |
10 | namespace pulse {
11 |
12 | class Protocol {
13 | public:
14 | virtual ~Protocol() = default;
15 |
16 | virtual void sendBestMove(int bestMove, int ponderMove) = 0;
17 |
18 | virtual void
19 | sendStatus(int currentDepth, int currentMaxDepth, uint64_t totalNodes, int currentMove, int currentMoveNumber) = 0;
20 |
21 | virtual void
22 | sendStatus(bool force, int currentDepth, int currentMaxDepth, uint64_t totalNodes, int currentMove,
23 | int currentMoveNumber) = 0;
24 |
25 | virtual void sendMove(RootEntry entry, int currentDepth, int currentMaxDepth, uint64_t totalNodes) = 0;
26 |
27 | virtual void sendInfo(const std::string& message) = 0;
28 |
29 | virtual void sendDebug(const std::string& message) = 0;
30 | };
31 | }
32 |
--------------------------------------------------------------------------------
/pulse-cpp/src/pulse.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 | #pragma once
6 |
7 | #include
8 |
9 | #include "search.h"
10 | #include "notation.h"
11 |
12 | namespace pulse {
13 |
14 | class Pulse final : public Protocol {
15 | public:
16 | void run();
17 |
18 | void sendBestMove(int bestMove, int ponderMove) override;
19 |
20 | void sendStatus(
21 | int currentDepth, int currentMaxDepth, uint64_t totalNodes, int currentMove,
22 | int currentMoveNumber) override;
23 |
24 | void sendStatus(
25 | bool force, int currentDepth, int currentMaxDepth, uint64_t totalNodes, int currentMove,
26 | int currentMoveNumber) override;
27 |
28 | void sendMove(RootEntry entry, int currentDepth, int currentMaxDepth, uint64_t totalNodes) override;
29 |
30 | void sendInfo(const std::string& message) override;
31 |
32 | static std::string fromMove(int move);
33 |
34 | void sendDebug(const std::string& message) override;
35 |
36 | private:
37 | bool debug;
38 | std::unique_ptr search = std::make_unique(*this);
39 | std::chrono::system_clock::time_point startTime;
40 | std::chrono::system_clock::time_point statusStartTime;
41 |
42 | std::unique_ptr currentPosition = std::make_unique(
43 | notation::toPosition(notation::STANDARDPOSITION));
44 |
45 | void receiveInitialize();
46 |
47 | void receiveDebug(std::istringstream& istringstream);
48 |
49 | static void receiveReady();
50 |
51 | void receiveNewGame();
52 |
53 | void receivePosition(std::istringstream& input);
54 |
55 | void receiveGo(std::istringstream& input);
56 |
57 | void receivePonderHit();
58 |
59 | void receiveStop();
60 |
61 | void receiveQuit();
62 | };
63 | }
64 |
--------------------------------------------------------------------------------
/pulse-cpp/src/threadpool.h:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #pragma once
7 |
8 | #include
9 | #include
10 |
11 | namespace pulse {
12 |
13 | class ThreadPool final {
14 | public:
15 | ThreadPool() : running(true) {
16 | threads.emplace_back(&ThreadPool::worker, this);
17 | };
18 |
19 | ~ThreadPool() {
20 | {
21 | std::unique_lock lock(mutex);
22 | running = false;
23 | }
24 | condition.notify_all();
25 | for (auto& thread: threads) {
26 | thread.join();
27 | }
28 | }
29 |
30 | template
31 | std::future> submit(R&& task) {
32 | auto packagedTask = std::make_shared() >>(task);
33 | auto future = packagedTask->get_future();
34 | {
35 | std::unique_lock lock(mutex);
36 | tasks.emplace([packagedTask] { (*packagedTask)(); });
37 | }
38 | condition.notify_one();
39 | return future;
40 | }
41 |
42 | private:
43 | bool running;
44 | std::vector threads;
45 | std::queue> tasks;
46 | std::mutex mutex;
47 | std::condition_variable condition;
48 |
49 | void worker() {
50 | for (;;) {
51 | std::function < void() > task;
52 | {
53 | std::unique_lock lock(mutex);
54 | condition.wait(lock, [this] { return !this->running || !this->tasks.empty(); });
55 | if (!running) {
56 | return;
57 | }
58 | task = std::move(tasks.front());
59 | tasks.pop();
60 | }
61 | task();
62 | }
63 | };
64 | };
65 | }
66 |
--------------------------------------------------------------------------------
/pulse-cpp/test/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.10)
2 | project(test)
3 |
4 | configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
5 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
6 | RESULT_VARIABLE result
7 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download)
8 | if (result)
9 | message(FATAL_ERROR "CMake step for googletest failed: ${result}")
10 | endif ()
11 | execute_process(COMMAND ${CMAKE_COMMAND} --build .
12 | RESULT_VARIABLE result
13 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download)
14 | if (result)
15 | message(FATAL_ERROR "Build step for googletest failed: ${result}")
16 | endif ()
17 |
18 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
19 |
20 | add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src
21 | ${CMAKE_CURRENT_BINARY_DIR}/googletest-build
22 | EXCLUDE_FROM_ALL)
23 |
24 | if (CMAKE_VERSION VERSION_LESS 2.8.11)
25 | include_directories("${gtest_SOURCE_DIR}/include")
26 | endif ()
27 |
28 | include_directories(${main_SOURCE_DIR})
29 |
30 | add_executable(unittest
31 | bitboardtest.cpp
32 | model/castlingtest.cpp
33 | model/castlingtypetest.cpp
34 | model/colortest.cpp
35 | evaluationtest.cpp
36 | notationtest.cpp
37 | model/filetest.cpp
38 | movegeneratortest.cpp
39 | movelisttest.cpp
40 | model/movetest.cpp
41 | model/piecetest.cpp
42 | model/piecetypetest.cpp
43 | positiontest.cpp
44 | model/ranktest.cpp
45 | model/squaretest.cpp
46 | threadpooltest.cpp
47 | )
48 |
49 | target_link_libraries(unittest core gtest_main)
50 |
51 | add_test(unittest unittest --gtest_output=xml:test-results/)
52 |
--------------------------------------------------------------------------------
/pulse-cpp/test/CMakeLists.txt.in:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.10)
2 | project(googletest-download NONE)
3 |
4 | include(ExternalProject)
5 | ExternalProject_Add(
6 | googletest
7 | URL https://github.com/google/googletest/archive/release-1.11.0.zip
8 | URL_HASH SHA1=9ffb7b5923f4a8fcdabf2f42c6540cce299f44c0
9 | SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
10 | BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
11 | CONFIGURE_COMMAND ""
12 | BUILD_COMMAND ""
13 | INSTALL_COMMAND ""
14 | TEST_COMMAND ""
15 | )
16 |
--------------------------------------------------------------------------------
/pulse-cpp/test/bitboardtest.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "bitboard.h"
7 | #include "model/square.h"
8 |
9 | #include "gtest/gtest.h"
10 |
11 | #include
12 | #include
13 |
14 | using namespace pulse;
15 |
16 | class BitboardTest : public ::testing::Test {
17 | protected:
18 | std::list pool;
19 |
20 | void SetUp() override {
21 | std::random_device randomDevice;
22 | unsigned int seed = randomDevice();
23 | std::default_random_engine generator(seed);
24 |
25 | while (pool.size() < 64) {
26 | std::uniform_int_distribution distribution(0, 63);
27 | int value = distribution(generator);
28 | if (std::find(pool.begin(), pool.end(), square::values[value]) == pool.end()) {
29 | pool.push_back(square::values[value]);
30 | }
31 | }
32 | }
33 | };
34 |
35 | TEST_F(BitboardTest, shouldAddAllSquaresCorrectly) {
36 | uint64_t bitboard = 0;
37 |
38 | for (auto x88square: pool) {
39 | bitboard = bitboard::add(x88square, bitboard);
40 | }
41 |
42 | EXPECT_EQ(bitboard, std::numeric_limits::max());
43 | }
44 |
45 | TEST_F(BitboardTest, shouldRemoveAllSquaresCorrectly) {
46 | uint64_t bitboard = std::numeric_limits::max();
47 |
48 | for (auto x88square: pool) {
49 | bitboard = bitboard::remove(x88square, bitboard);
50 | }
51 |
52 | EXPECT_EQ(bitboard, 0);
53 | }
54 |
55 | TEST(bitboardtest, shouldReturnTheNextSquare) {
56 | uint64_t bitboard = bitboard::add(square::a6, 0);
57 |
58 | int square = bitboard::next(bitboard);
59 |
60 | EXPECT_EQ(square, +square::a6);
61 | }
62 |
63 | TEST(bitboardtest, shouldReturnCorrectRemainder) {
64 | uint64_t bitboard = 0b1110100;
65 |
66 | uint64_t remainder = bitboard::remainder(bitboard);
67 |
68 | EXPECT_EQ(remainder, 0b1110000);
69 | }
70 |
71 | TEST(bitboardtest, shouldReturnCorrectSize) {
72 | uint64_t bitboard = 0b111;
73 |
74 | int size = bitboard::size(bitboard);
75 |
76 | EXPECT_EQ(size, 3);
77 | }
78 |
79 | TEST(bitboardtest, testNumberOfTrailingZeros) {
80 | uint64_t bitboard = 0;
81 | int i = 0;
82 |
83 | for (auto square: square::values) {
84 | bitboard = bitboard::add(square, bitboard);
85 |
86 | EXPECT_EQ(i, bitboard::numberOfTrailingZeros(bitboard));
87 |
88 | bitboard = bitboard::remove(square, bitboard);
89 | i++;
90 | }
91 | }
92 |
93 | TEST(bitboardtest, testBitCount) {
94 | std::random_device randomDevice;
95 | unsigned int seed = randomDevice();
96 | std::default_random_engine generator(seed);
97 |
98 | for (int i = 0; i < 1000; i++) {
99 | uint64_t bitboard = 0;
100 | int count = 0;
101 |
102 | int index = 0;
103 | while (true) {
104 | std::uniform_int_distribution distribution(1, 4);
105 | index += distribution(generator);
106 | if (index < 64) {
107 | bitboard |= 1ULL << index;
108 | count++;
109 | } else {
110 | break;
111 | }
112 | }
113 |
114 | EXPECT_EQ(count, bitboard::bitCount(bitboard));
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/pulse-cpp/test/evaluationtest.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "evaluation.h"
7 | #include "notation.h"
8 |
9 | #include "gtest/gtest.h"
10 |
11 | using namespace pulse;
12 |
13 | TEST(evaluationtest, testEvaluate) {
14 | Position position(notation::toPosition(notation::STANDARDPOSITION));
15 |
16 | EXPECT_EQ(+evaluation::TEMPO, evaluation::evaluate(position));
17 | }
18 |
--------------------------------------------------------------------------------
/pulse-cpp/test/model/castlingtest.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include
7 | #include
8 | #include "model/castling.h"
9 |
10 | #include "gtest/gtest.h"
11 |
12 | using namespace pulse;
13 |
14 | TEST(castlingtest, testValueOf) {
15 | EXPECT_EQ(+castling::WHITE_KINGSIDE, castling::valueOf(color::WHITE, castlingtype::KINGSIDE));
16 | EXPECT_EQ(+castling::WHITE_QUEENSIDE, castling::valueOf(color::WHITE, castlingtype::QUEENSIDE));
17 | EXPECT_EQ(+castling::BLACK_KINGSIDE, castling::valueOf(color::BLACK, castlingtype::KINGSIDE));
18 | EXPECT_EQ(+castling::BLACK_QUEENSIDE, castling::valueOf(color::BLACK, castlingtype::QUEENSIDE));
19 | }
20 |
--------------------------------------------------------------------------------
/pulse-cpp/test/model/castlingtypetest.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "model/castlingtype.h"
7 |
8 | #include "gtest/gtest.h"
9 |
10 | using namespace pulse;
11 |
12 | TEST(castlingtypetest, testValues) {
13 | for (auto castlingtype: castlingtype::values) {
14 | EXPECT_EQ(castlingtype, castlingtype::values[castlingtype]);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/pulse-cpp/test/model/colortest.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "model/color.h"
7 |
8 | #include "gtest/gtest.h"
9 |
10 | using namespace pulse;
11 |
12 | TEST(colortest, testValues) {
13 | for (auto color: color::values) {
14 | EXPECT_EQ(color, color::values[color]);
15 | }
16 | }
17 |
18 | TEST(colortest, testOpposite) {
19 | EXPECT_EQ(+color::WHITE, color::opposite(color::BLACK));
20 | EXPECT_EQ(+color::BLACK, color::opposite(color::WHITE));
21 | }
22 |
--------------------------------------------------------------------------------
/pulse-cpp/test/model/filetest.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "model/file.h"
7 |
8 | #include "gtest/gtest.h"
9 |
10 | using namespace pulse;
11 |
12 | TEST(filetest, testValues) {
13 | for (auto file: file::values) {
14 | EXPECT_EQ(file, file::values[file]);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/pulse-cpp/test/model/movetest.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "model/move.h"
7 |
8 | #include "gtest/gtest.h"
9 |
10 | using namespace pulse;
11 |
12 | TEST(movetest, testCreation) {
13 | int move = move::valueOf(movetype::PAWNPROMOTION, square::a7, square::b8, piece::WHITE_PAWN, piece::BLACK_QUEEN,
14 | piecetype::KNIGHT);
15 |
16 | EXPECT_EQ(+movetype::PAWNPROMOTION, move::getType(move));
17 | EXPECT_EQ(+square::a7, move::getOriginSquare(move));
18 | EXPECT_EQ(+square::b8, move::getTargetSquare(move));
19 | EXPECT_EQ(+piece::WHITE_PAWN, move::getOriginPiece(move));
20 | EXPECT_EQ(+piece::BLACK_QUEEN, move::getTargetPiece(move));
21 | EXPECT_EQ(+piecetype::KNIGHT, move::getPromotion(move));
22 | }
23 |
24 | TEST(movetest, testPromotion) {
25 | int move = move::valueOf(movetype::PAWNPROMOTION, square::b7, square::c8, piece::WHITE_PAWN, piece::BLACK_QUEEN,
26 | piecetype::KNIGHT);
27 |
28 | EXPECT_EQ(+piecetype::KNIGHT, move::getPromotion(move));
29 | }
30 |
--------------------------------------------------------------------------------
/pulse-cpp/test/model/piecetest.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "model/piece.h"
7 | #include "model/color.h"
8 | #include "model/piecetype.h"
9 |
10 | #include "gtest/gtest.h"
11 |
12 | using namespace pulse;
13 |
14 | TEST(piecetest, testValues) {
15 | for (auto piece: piece::values) {
16 | EXPECT_EQ(piece, piece::values[piece]);
17 | }
18 | }
19 |
20 | TEST(piecetest, testValueOf) {
21 | EXPECT_EQ(+piece::WHITE_PAWN, piece::valueOf(color::WHITE, piecetype::PAWN));
22 | EXPECT_EQ(+piece::WHITE_KNIGHT, piece::valueOf(color::WHITE, piecetype::KNIGHT));
23 | EXPECT_EQ(+piece::WHITE_BISHOP, piece::valueOf(color::WHITE, piecetype::BISHOP));
24 | EXPECT_EQ(+piece::WHITE_ROOK, piece::valueOf(color::WHITE, piecetype::ROOK));
25 | EXPECT_EQ(+piece::WHITE_QUEEN, piece::valueOf(color::WHITE, piecetype::QUEEN));
26 | EXPECT_EQ(+piece::WHITE_KING, piece::valueOf(color::WHITE, piecetype::KING));
27 | EXPECT_EQ(+piece::BLACK_PAWN, piece::valueOf(color::BLACK, piecetype::PAWN));
28 | EXPECT_EQ(+piece::BLACK_KNIGHT, piece::valueOf(color::BLACK, piecetype::KNIGHT));
29 | EXPECT_EQ(+piece::BLACK_BISHOP, piece::valueOf(color::BLACK, piecetype::BISHOP));
30 | EXPECT_EQ(+piece::BLACK_ROOK, piece::valueOf(color::BLACK, piecetype::ROOK));
31 | EXPECT_EQ(+piece::BLACK_QUEEN, piece::valueOf(color::BLACK, piecetype::QUEEN));
32 | EXPECT_EQ(+piece::BLACK_KING, piece::valueOf(color::BLACK, piecetype::KING));
33 | }
34 |
35 | TEST(piecetest, testGetType) {
36 | EXPECT_EQ(+piecetype::PAWN, piece::getType(piece::WHITE_PAWN));
37 | EXPECT_EQ(+piecetype::PAWN, piece::getType(piece::BLACK_PAWN));
38 | EXPECT_EQ(+piecetype::KNIGHT, piece::getType(piece::WHITE_KNIGHT));
39 | EXPECT_EQ(+piecetype::KNIGHT, piece::getType(piece::BLACK_KNIGHT));
40 | EXPECT_EQ(+piecetype::BISHOP, piece::getType(piece::WHITE_BISHOP));
41 | EXPECT_EQ(+piecetype::BISHOP, piece::getType(piece::BLACK_BISHOP));
42 | EXPECT_EQ(+piecetype::ROOK, piece::getType(piece::WHITE_ROOK));
43 | EXPECT_EQ(+piecetype::ROOK, piece::getType(piece::BLACK_ROOK));
44 | EXPECT_EQ(+piecetype::QUEEN, piece::getType(piece::WHITE_QUEEN));
45 | EXPECT_EQ(+piecetype::QUEEN, piece::getType(piece::BLACK_QUEEN));
46 | EXPECT_EQ(+piecetype::KING, piece::getType(piece::WHITE_KING));
47 | EXPECT_EQ(+piecetype::KING, piece::getType(piece::BLACK_KING));
48 | }
49 |
50 | TEST(piecetest, testGetColor) {
51 | EXPECT_EQ(+color::WHITE, piece::getColor(piece::WHITE_PAWN));
52 | EXPECT_EQ(+color::BLACK, piece::getColor(piece::BLACK_PAWN));
53 | EXPECT_EQ(+color::WHITE, piece::getColor(piece::WHITE_KNIGHT));
54 | EXPECT_EQ(+color::BLACK, piece::getColor(piece::BLACK_KNIGHT));
55 | EXPECT_EQ(+color::WHITE, piece::getColor(piece::WHITE_BISHOP));
56 | EXPECT_EQ(+color::BLACK, piece::getColor(piece::BLACK_BISHOP));
57 | EXPECT_EQ(+color::WHITE, piece::getColor(piece::WHITE_ROOK));
58 | EXPECT_EQ(+color::BLACK, piece::getColor(piece::BLACK_ROOK));
59 | EXPECT_EQ(+color::WHITE, piece::getColor(piece::WHITE_QUEEN));
60 | EXPECT_EQ(+color::BLACK, piece::getColor(piece::BLACK_QUEEN));
61 | EXPECT_EQ(+color::WHITE, piece::getColor(piece::WHITE_KING));
62 | EXPECT_EQ(+color::BLACK, piece::getColor(piece::BLACK_KING));
63 | }
64 |
--------------------------------------------------------------------------------
/pulse-cpp/test/model/piecetypetest.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "model/piecetype.h"
7 |
8 | #include "gtest/gtest.h"
9 |
10 | using namespace pulse;
11 |
12 | TEST(piecetypetest, testValues) {
13 | for (auto piecetype: piecetype::values) {
14 | EXPECT_EQ(piecetype, piecetype::values[piecetype]);
15 | }
16 | }
17 |
18 | TEST(piecetypetest, testIsValidPromotion) {
19 | EXPECT_TRUE(piecetype::isValidPromotion(piecetype::KNIGHT));
20 | EXPECT_TRUE(piecetype::isValidPromotion(piecetype::BISHOP));
21 | EXPECT_TRUE(piecetype::isValidPromotion(piecetype::ROOK));
22 | EXPECT_TRUE(piecetype::isValidPromotion(piecetype::QUEEN));
23 | EXPECT_FALSE(piecetype::isValidPromotion(piecetype::PAWN));
24 | EXPECT_FALSE(piecetype::isValidPromotion(piecetype::KING));
25 | EXPECT_FALSE(piecetype::isValidPromotion(piecetype::NOPIECETYPE));
26 | }
27 |
28 | TEST(piecetypetest, testIsSliding) {
29 | EXPECT_TRUE(piecetype::isSliding(piecetype::BISHOP));
30 | EXPECT_TRUE(piecetype::isSliding(piecetype::ROOK));
31 | EXPECT_TRUE(piecetype::isSliding(piecetype::QUEEN));
32 | EXPECT_FALSE(piecetype::isSliding(piecetype::PAWN));
33 | EXPECT_FALSE(piecetype::isSliding(piecetype::KNIGHT));
34 | EXPECT_FALSE(piecetype::isSliding(piecetype::KING));
35 | }
36 |
--------------------------------------------------------------------------------
/pulse-cpp/test/model/ranktest.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "model/rank.h"
7 |
8 | #include "gtest/gtest.h"
9 |
10 | using namespace pulse;
11 |
12 | TEST(ranktest, testValues) {
13 | for (auto rank: rank::values) {
14 | EXPECT_EQ(rank, rank::values[rank]);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/pulse-cpp/test/model/squaretest.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "model/square.h"
7 | #include "model/file.h"
8 | #include "model/rank.h"
9 |
10 | #include "gtest/gtest.h"
11 |
12 | using namespace pulse;
13 |
14 | TEST(squaretest, testValues) {
15 | for (auto rank: rank::values) {
16 | for (auto file: file::values) {
17 | int square = square::valueOf(file, rank);
18 |
19 | EXPECT_EQ(file, square::getFile(square));
20 | EXPECT_EQ(rank, square::getRank(square));
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/pulse-cpp/test/movelisttest.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "movelist.h"
7 |
8 | #include "gtest/gtest.h"
9 |
10 | using namespace pulse;
11 |
12 | TEST(movelisttest, test) {
13 | MoveList moveList;
14 |
15 | EXPECT_EQ(0, moveList.size);
16 |
17 | moveList.entries[moveList.size++]->move = 1;
18 | EXPECT_EQ(1, moveList.size);
19 | }
20 |
--------------------------------------------------------------------------------
/pulse-cpp/test/notationtest.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "notation.h"
7 | #include "model/file.h"
8 | #include "model/rank.h"
9 |
10 | #include "gtest/gtest.h"
11 |
12 | using namespace pulse;
13 |
14 | TEST(notationtest, testStandardPosition) {
15 | Position position(notation::toPosition(notation::STANDARDPOSITION));
16 |
17 | // Test pawns
18 | for (auto file: file::values) {
19 | EXPECT_EQ(+piece::WHITE_PAWN, position.board[square::valueOf(file, rank::r2)]);
20 | EXPECT_EQ(+piece::BLACK_PAWN, position.board[square::valueOf(file, rank::r7)]);
21 | }
22 |
23 | // Test knights
24 | EXPECT_EQ(+piece::WHITE_KNIGHT, position.board[square::b1]);
25 | EXPECT_EQ(+piece::WHITE_KNIGHT, position.board[square::g1]);
26 | EXPECT_EQ(+piece::BLACK_KNIGHT, position.board[square::b8]);
27 | EXPECT_EQ(+piece::BLACK_KNIGHT, position.board[square::g8]);
28 |
29 | // Test bishops
30 | EXPECT_EQ(+piece::WHITE_BISHOP, position.board[square::c1]);
31 | EXPECT_EQ(+piece::WHITE_BISHOP, position.board[square::f1]);
32 | EXPECT_EQ(+piece::BLACK_BISHOP, position.board[square::c8]);
33 | EXPECT_EQ(+piece::BLACK_BISHOP, position.board[square::f8]);
34 |
35 | // Test rooks
36 | EXPECT_EQ(+piece::WHITE_ROOK, position.board[square::a1]);
37 | EXPECT_EQ(+piece::WHITE_ROOK, position.board[square::h1]);
38 | EXPECT_EQ(+piece::BLACK_ROOK, position.board[square::a8]);
39 | EXPECT_EQ(+piece::BLACK_ROOK, position.board[square::h8]);
40 |
41 | // Test queens
42 | EXPECT_EQ(+piece::WHITE_QUEEN, position.board[square::d1]);
43 | EXPECT_EQ(+piece::BLACK_QUEEN, position.board[square::d8]);
44 |
45 | // Test kings
46 | EXPECT_EQ(+piece::WHITE_KING, position.board[square::e1]);
47 | EXPECT_EQ(+piece::BLACK_KING, position.board[square::e8]);
48 |
49 | EXPECT_EQ(8 * piecetype::PAWN_VALUE
50 | + 2 * piecetype::KNIGHT_VALUE
51 | + 2 * piecetype::BISHOP_VALUE
52 | + 2 * piecetype::ROOK_VALUE
53 | + piecetype::QUEEN_VALUE
54 | + piecetype::KING_VALUE,
55 | position.material[color::WHITE]);
56 | EXPECT_EQ(8 * piecetype::PAWN_VALUE
57 | + 2 * piecetype::KNIGHT_VALUE
58 | + 2 * piecetype::BISHOP_VALUE
59 | + 2 * piecetype::ROOK_VALUE
60 | + piecetype::QUEEN_VALUE
61 | + piecetype::KING_VALUE,
62 | position.material[color::BLACK]);
63 |
64 | // Test castling
65 | EXPECT_NE(+castling::NOCASTLING, position.castlingRights & castling::WHITE_KINGSIDE);
66 | EXPECT_NE(+castling::NOCASTLING, position.castlingRights & castling::WHITE_QUEENSIDE);
67 | EXPECT_NE(+castling::NOCASTLING, position.castlingRights & castling::BLACK_KINGSIDE);
68 | EXPECT_NE(+castling::NOCASTLING, position.castlingRights & castling::BLACK_QUEENSIDE);
69 |
70 | // Test en passant
71 | EXPECT_EQ(+square::NOSQUARE, position.enPassantSquare);
72 |
73 | // Test active color
74 | EXPECT_EQ(+color::WHITE, position.activeColor);
75 |
76 | // Test half move clock
77 | EXPECT_EQ(0, position.halfmoveClock);
78 |
79 | // Test full move number
80 | EXPECT_EQ(1, position.getFullmoveNumber());
81 | }
82 |
--------------------------------------------------------------------------------
/pulse-cpp/test/threadpooltest.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2023 Phokham Nonava
2 | //
3 | // Use of this source code is governed by the MIT license that can be
4 | // found in the LICENSE file.
5 |
6 | #include "threadpool.h"
7 |
8 | #include "gtest/gtest.h"
9 |
10 | using namespace pulse;
11 |
12 | TEST(threadpooltest, test) {
13 | ThreadPool threadPool;
14 | std::future result = threadPool.submit([] {
15 | return 42;
16 | });
17 | EXPECT_EQ(result.get(), 42);
18 | }
19 |
--------------------------------------------------------------------------------
/pulse-go/Taskfile.yaml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | tasks:
4 | build:
5 | cmds:
6 | - go build -v ./...
7 | - go test -v ./...
8 |
--------------------------------------------------------------------------------
/pulse-go/cmd/pulse-go/pulse-go.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package main
9 |
10 | import (
11 | "fmt"
12 | "log"
13 | "os"
14 | "strings"
15 |
16 | "github.com/fluxroot/pulse/internal/pulse"
17 | "github.com/fluxroot/pulse/internal/pulse/uci"
18 | )
19 |
20 | func main() {
21 | args := os.Args[1:]
22 | switch {
23 | case len(args) == 0:
24 | sender := uci.NewDefaultSender(uci.NewStdoutWriter())
25 | engine := pulse.NewPulse(sender)
26 | receiver := uci.NewDefaultReceiver(uci.NewStdinReader(), sender, engine)
27 | if err := receiver.Run(); err != nil {
28 | log.Fatalf("Error: %v", err)
29 | }
30 | case (len(args)) == 1 && strings.EqualFold(args[0], "perft"):
31 | pulse.NewPerft().Run()
32 | default:
33 | printUsage()
34 | os.Exit(1)
35 | }
36 | }
37 |
38 | func printUsage() {
39 | _, _ = fmt.Fprintf(os.Stderr, "Usage: pulse-go [perft]")
40 | }
41 |
--------------------------------------------------------------------------------
/pulse-go/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/fluxroot/pulse
2 |
3 | go 1.23.4
4 |
5 | require go.uber.org/mock v0.5.0
6 |
--------------------------------------------------------------------------------
/pulse-go/go.sum:
--------------------------------------------------------------------------------
1 | go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
2 | go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
3 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/attack.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2025 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | func (p *Position) isCheck() bool {
11 | return p.isAttacked(next(p.pieces[p.ActiveColor][King]), OppositeOf(p.ActiveColor))
12 | }
13 |
14 | func (p *Position) isCheckFor(col Color) bool {
15 | return p.isAttacked(next(p.pieces[col][King]), OppositeOf(col))
16 | }
17 |
18 | func (p *Position) isAttacked(targetSq Square, attackerCol Color) bool {
19 | return p.isAttackedByPawn(targetSq, attackerCol) ||
20 | p.isAttackedByNonSlidingPiece(targetSq, PieceOf(attackerCol, Knight), knightDirections[:]) ||
21 | p.isAttackedBySlidingPiece(targetSq, PieceOf(attackerCol, Bishop), PieceOf(attackerCol, Queen), bishopDirections[:]) ||
22 | p.isAttackedBySlidingPiece(targetSq, PieceOf(attackerCol, Rook), PieceOf(attackerCol, Queen), rookDirections[:]) ||
23 | p.isAttackedByNonSlidingPiece(targetSq, PieceOf(attackerCol, King), kingDirections[:])
24 | }
25 |
26 | func (p *Position) isAttackedByPawn(targetSq Square, attackerCol Color) bool {
27 | attackerPawn := PieceOf(attackerCol, Pawn)
28 | for _, dir := range pawnCapturingDirections[attackerCol] {
29 | attackerSq := targetSq - dir
30 | if IsValidSquare(attackerSq) && p.board[attackerSq] == attackerPawn {
31 | return true
32 | }
33 | }
34 | return false
35 | }
36 |
37 | func (p *Position) isAttackedByNonSlidingPiece(targetSq Square, attackerPc Piece, directions []direction) bool {
38 | for _, dir := range directions {
39 | attackerSq := targetSq + dir
40 | if IsValidSquare(attackerSq) && p.board[attackerSq] == attackerPc {
41 | return true
42 | }
43 | }
44 | return false
45 | }
46 |
47 | func (p *Position) isAttackedBySlidingPiece(targetSq Square, attackerPc Piece, attackerQueen Piece, directions []direction) bool {
48 | for _, dir := range directions {
49 | attackerSq := targetSq + dir
50 | for IsValidSquare(attackerSq) {
51 | pc := p.board[attackerSq]
52 | if pc != NoPiece {
53 | if pc == attackerPc || pc == attackerQueen {
54 | return true
55 | }
56 | break
57 | } else {
58 | attackerSq += dir
59 | }
60 | }
61 | }
62 | return false
63 | }
64 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/bitboard.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | import (
11 | "math/bits"
12 | )
13 |
14 | type bitboard = uint64
15 | type bitSquare = int
16 |
17 | func addSquare(sq Square, bb bitboard) bitboard {
18 | return bb | (1 << toBitSquare(sq))
19 | }
20 |
21 | func removeSquare(sq Square, bb bitboard) bitboard {
22 | return bb & ^(1 << toBitSquare(sq))
23 | }
24 |
25 | func next(bb bitboard) Square {
26 | return toX88Square(bits.TrailingZeros64(bb))
27 |
28 | }
29 |
30 | func remainder(bb bitboard) bitboard {
31 | return bb & (bb - 1)
32 | }
33 |
34 | func toBitSquare(sq Square) bitSquare {
35 | return ((sq & ^7) >> 1) | (sq & 7)
36 | }
37 |
38 | func toX88Square(sq bitSquare) Square {
39 | return ((sq & ^7) << 1) | (sq & 7)
40 | }
41 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/bitboard_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | import (
11 | "math"
12 | "math/rand"
13 | "testing"
14 | )
15 |
16 | func TestBitboard_addSquare(t *testing.T) {
17 | t.Run("It should add all squares", func(t *testing.T) {
18 | bb := bitboard(0)
19 | for _, sq := range shuffledSquares() {
20 | bb = addSquare(sq, bb)
21 | }
22 | if math.MaxUint64 != bb {
23 | t.Errorf("addSquare() = %v, want %d", bb, bitboard(math.MaxUint64))
24 | }
25 | })
26 | }
27 |
28 | func TestBitboard_removeSquare(t *testing.T) {
29 | t.Run("It should remove all squares", func(t *testing.T) {
30 | bb := bitboard(math.MaxUint64)
31 | for _, sq := range shuffledSquares() {
32 | bb = removeSquare(sq, bb)
33 | }
34 | if 0 != bb {
35 | t.Errorf("removeSquare() = %v, want %d", bb, 0)
36 | }
37 | })
38 | }
39 |
40 | func TestBitboard_next(t *testing.T) {
41 | t.Run("It should return the next square", func(t *testing.T) {
42 | bb := addSquare(A6, 0)
43 | sq := next(bb)
44 | if A6 != sq {
45 | t.Errorf("next() = %v, want %v", sq, A6)
46 | }
47 | })
48 | }
49 |
50 | func TestBitboard_remainder(t *testing.T) {
51 | t.Run("It should return the remainder", func(t *testing.T) {
52 | bb := bitboard(0b1110100)
53 | rem := remainder(bb)
54 | if 0b1110000 != rem {
55 | t.Errorf("remainder() = %v, want %v", rem, 0b1110000)
56 | }
57 | })
58 | }
59 |
60 | func shuffledSquares() []Square {
61 | sqs := make([]Square, len(Squares))
62 | for i, v := range rand.Perm(len(Squares)) {
63 | sqs[v] = Squares[i]
64 | }
65 | return sqs
66 | }
67 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/castling.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | import "fmt"
11 |
12 | type Castling = int
13 |
14 | const (
15 | WhiteKingside Castling = 1 // 1 << 0
16 | WhiteQueenside Castling = 1 << 1
17 | BlackKingside Castling = 1 << 2
18 | BlackQueenside Castling = 1 << 3
19 |
20 | NoCastling Castling = 0
21 | )
22 |
23 | var (
24 | Castlings = [4]Castling{
25 | WhiteKingside, WhiteQueenside,
26 | BlackKingside, BlackQueenside,
27 | }
28 | )
29 |
30 | func IsValidCastling(cast Castling) bool {
31 | switch cast {
32 | case WhiteKingside, WhiteQueenside, BlackKingside, BlackQueenside:
33 | return true
34 | default:
35 | return false
36 | }
37 | }
38 |
39 | func CastlingOf(col Color, ct CastlingType) Castling {
40 | switch col {
41 | case White:
42 | switch ct {
43 | case Kingside:
44 | return WhiteKingside
45 | case Queenside:
46 | return WhiteQueenside
47 | default:
48 | panic(fmt.Sprintf("Invalid castling type: %v", ct))
49 | }
50 | case Black:
51 | switch ct {
52 | case Kingside:
53 | return BlackKingside
54 | case Queenside:
55 | return BlackQueenside
56 | default:
57 | panic(fmt.Sprintf("Invalid castling type: %v", ct))
58 | }
59 | default:
60 | panic(fmt.Sprintf("Invalid color: %v", col))
61 | }
62 | }
63 |
64 | func CastlingColorOf(cast Castling) Color {
65 | switch cast {
66 | case WhiteKingside, WhiteQueenside:
67 | return White
68 | case BlackKingside, BlackQueenside:
69 | return Black
70 | default:
71 | panic(fmt.Sprintf("Invalid castling: %v", cast))
72 | }
73 | }
74 |
75 | func CastlingTypeOf(cast Castling) CastlingType {
76 | switch cast {
77 | case WhiteKingside, BlackKingside:
78 | return Kingside
79 | case WhiteQueenside, BlackQueenside:
80 | return Queenside
81 | default:
82 | panic(fmt.Sprintf("Invalid castling: %v", cast))
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/castling_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | import (
11 | "testing"
12 | )
13 |
14 | func TestCastling_IsValidCastling(t *testing.T) {
15 | tests := []struct {
16 | castling Castling
17 | want bool
18 | }{
19 | {castling: WhiteKingside, want: true},
20 | {castling: WhiteQueenside, want: true},
21 | {castling: BlackKingside, want: true},
22 | {castling: BlackQueenside, want: true},
23 | {castling: NoCastling, want: false},
24 | }
25 | for _, tt := range tests {
26 | t.Run("IsValidCastling should return true if castling is valid, false otherwise", func(t *testing.T) {
27 | valid := IsValidCastling(tt.castling)
28 | if valid != tt.want {
29 | t.Errorf("IsValidCastling(%d) = %v, want %v", tt.castling, valid, tt.want)
30 | }
31 | })
32 | }
33 | }
34 |
35 | func TestCastling_CastlingOf(t *testing.T) {
36 | tests := []struct {
37 | color Color
38 | castlingType CastlingType
39 | want Castling
40 | }{
41 | {color: White, castlingType: Kingside, want: WhiteKingside},
42 | {color: White, castlingType: Queenside, want: WhiteQueenside},
43 | {color: Black, castlingType: Kingside, want: BlackKingside},
44 | {color: Black, castlingType: Queenside, want: BlackQueenside},
45 | }
46 | for _, tt := range tests {
47 | t.Run("CastlingOf should return the castling", func(t *testing.T) {
48 | cast := CastlingOf(tt.color, tt.castlingType)
49 | if cast != tt.want {
50 | t.Errorf("wanted castling of color %v and castling type %v to be %v, but got %v", tt.color, tt.castlingType, tt.want, cast)
51 | }
52 | })
53 | }
54 | }
55 |
56 | func TestCastling_CastlingColorOf(t *testing.T) {
57 | tests := []struct {
58 | castling Castling
59 | want Color
60 | }{
61 | {castling: WhiteKingside, want: White},
62 | {castling: WhiteQueenside, want: White},
63 | {castling: BlackKingside, want: Black},
64 | {castling: BlackQueenside, want: Black},
65 | }
66 | for _, tt := range tests {
67 | t.Run("CastlingColorOf should return the castling color", func(t *testing.T) {
68 | col := CastlingColorOf(tt.castling)
69 | if col != tt.want {
70 | t.Errorf("wanted color of %v to be %v, but got %v", tt.castling, tt.want, col)
71 | }
72 | })
73 | }
74 | }
75 |
76 | func TestCastling_CastlingTypeOf(t *testing.T) {
77 | tests := []struct {
78 | castling Castling
79 | want CastlingType
80 | }{
81 | {castling: WhiteKingside, want: Kingside},
82 | {castling: WhiteQueenside, want: Queenside},
83 | {castling: BlackKingside, want: Kingside},
84 | {castling: BlackQueenside, want: Queenside},
85 | }
86 | for _, tt := range tests {
87 | t.Run("CastlingTypeOf should return the castling type", func(t *testing.T) {
88 | pt := CastlingTypeOf(tt.castling)
89 | if pt != tt.want {
90 | t.Errorf("wanted castling type of %v to be %v, but got %v", tt.castling, tt.want, pt)
91 | }
92 | })
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/castling_type.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | type CastlingType = int
11 |
12 | const (
13 | Kingside CastlingType = 0
14 | Queenside CastlingType = 1
15 |
16 | NoCastlingType CastlingType = 2
17 | )
18 |
19 | var (
20 | CastlingTypes = [2]CastlingType{Kingside, Queenside}
21 | )
22 |
23 | func IsValidCastlingType(ct CastlingType) bool {
24 | switch ct {
25 | case Kingside, Queenside:
26 | return true
27 | default:
28 | return false
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/castling_type_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | import (
11 | "testing"
12 | )
13 |
14 | func TestCastlingType(t *testing.T) {
15 | t.Run("CastlingTypes array should be valid", func(t *testing.T) {
16 | for ct := range CastlingTypes {
17 | if CastlingTypes[ct] != ct {
18 | t.Errorf("wanted CastlingTypes at index %d to be %v, but got %v", ct, ct, CastlingTypes[ct])
19 | }
20 | }
21 | })
22 | }
23 |
24 | func TestCastlingType_IsValidCastlingType(t *testing.T) {
25 | tests := []struct {
26 | castlingType CastlingType
27 | want bool
28 | }{
29 | {castlingType: Kingside, want: true},
30 | {castlingType: Queenside, want: true},
31 | {castlingType: NoCastlingType, want: false},
32 | }
33 | for _, tt := range tests {
34 | t.Run("IsValidCastlingType should return true if castling type is valid, false otherwise", func(t *testing.T) {
35 | valid := IsValidCastlingType(tt.castlingType)
36 | if valid != tt.want {
37 | t.Errorf("IsValidCastlingType(%d) = %v, want %v", tt.castlingType, valid, tt.want)
38 | }
39 | })
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/color.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | import "fmt"
11 |
12 | type Color = int
13 |
14 | const (
15 | White Color = 0
16 | Black Color = 1
17 |
18 | NoColor Color = 2
19 | )
20 |
21 | var (
22 | Colors = [2]Color{White, Black}
23 | )
24 |
25 | func IsValidColor(col Color) bool {
26 | switch col {
27 | case White, Black:
28 | return true
29 | default:
30 | return false
31 | }
32 | }
33 |
34 | func OppositeOf(col Color) Color {
35 | switch col {
36 | case White:
37 | return Black
38 | case Black:
39 | return White
40 | default:
41 | panic(fmt.Sprintf("Invalid color: %v", col))
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/color_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | import (
11 | "testing"
12 | )
13 |
14 | func TestColor(t *testing.T) {
15 | t.Run("Colors array should be valid", func(t *testing.T) {
16 | for col := range Colors {
17 | if Colors[col] != col {
18 | t.Errorf("wanted Colors at index %d to be %v, but got %v", col, col, Colors[col])
19 | }
20 | }
21 | })
22 | }
23 |
24 | func TestColor_IsValidColor(t *testing.T) {
25 | tests := []struct {
26 | color Color
27 | want bool
28 | }{
29 | {color: White, want: true},
30 | {color: Black, want: true},
31 | {color: NoColor, want: false},
32 | }
33 | for _, tt := range tests {
34 | t.Run("IsValidColor should return true if color is valid, false otherwise", func(t *testing.T) {
35 | valid := IsValidColor(tt.color)
36 | if valid != tt.want {
37 | t.Errorf("IsValidColor(%d) = %v, want %v", tt.color, valid, tt.want)
38 | }
39 | })
40 | }
41 | }
42 |
43 | func TestColor_OppositeOf(t *testing.T) {
44 | tests := []struct {
45 | color Color
46 | want Color
47 | }{
48 | {color: White, want: Black},
49 | {color: Black, want: White},
50 | }
51 | for _, tt := range tests {
52 | t.Run("OppositeOf should return the opposite color", func(t *testing.T) {
53 | opposite := OppositeOf(tt.color)
54 | if opposite != tt.want {
55 | t.Errorf("wanted opposite color of %v to be %v, but got %v", tt.color, tt.want, opposite)
56 | }
57 | })
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/depth.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | type Depth = int
11 |
12 | const (
13 | MaxPly Depth = 256
14 | MaxDepth Depth = 64
15 | )
16 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/direction.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | type direction = int
11 |
12 | const (
13 | north direction = 16
14 | east direction = 1
15 | south direction = -16
16 | west direction = -1
17 | northEast direction = north + east
18 | southEast direction = south + east
19 | southWest direction = south + west
20 | northWest direction = north + west
21 | )
22 |
23 | var (
24 | pawnMoveDirections = [2]direction{
25 | north, // WHITE
26 | south, // BLACK
27 | }
28 |
29 | pawnCapturingDirections = [2][2]direction{
30 | {northEast, northWest}, // WHITE
31 | {southEast, southWest}, // BLACK
32 | }
33 |
34 | knightDirections = [8]direction{
35 | north + north + east,
36 | north + north + west,
37 | north + east + east,
38 | north + west + west,
39 | south + south + east,
40 | south + south + west,
41 | south + east + east,
42 | south + west + west,
43 | }
44 |
45 | bishopDirections = [4]direction{
46 | northEast, northWest, southEast, southWest,
47 | }
48 |
49 | rookDirections = [4]direction{
50 | north, east, south, west,
51 | }
52 |
53 | queenDirections = [8]direction{
54 | north, east, south, west,
55 | northEast, northWest, southEast, southWest,
56 | }
57 |
58 | kingDirections = [8]direction{
59 | north, east, south, west,
60 | northEast, northWest, southEast, southWest,
61 | }
62 | )
63 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/file.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | type File = int
11 |
12 | const (
13 | FileA File = 0
14 | FileB File = 1
15 | FileC File = 2
16 | FileD File = 3
17 | FileE File = 4
18 | FileF File = 5
19 | FileG File = 6
20 | FileH File = 7
21 |
22 | NoFile File = 8
23 | )
24 |
25 | var (
26 | Files = [8]File{FileA, FileB, FileC, FileD, FileE, FileF, FileG, FileH}
27 | )
28 |
29 | func IsValidFile(f File) bool {
30 | switch f {
31 | case FileA, FileB, FileC, FileD, FileE, FileF, FileG, FileH:
32 | return true
33 | default:
34 | return false
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/file_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | import (
11 | "testing"
12 | )
13 |
14 | func TestFile(t *testing.T) {
15 | t.Run("Files array should be valid", func(t *testing.T) {
16 | for f := range Files {
17 | if Files[f] != f {
18 | t.Errorf("wanted Files at index %d to be %v, but got %v", f, f, Files[f])
19 | }
20 | }
21 | })
22 | }
23 |
24 | func TestFile_IsValidFile(t *testing.T) {
25 | tests := []struct {
26 | file File
27 | want bool
28 | }{
29 | {file: FileA, want: true},
30 | {file: FileB, want: true},
31 | {file: FileC, want: true},
32 | {file: FileD, want: true},
33 | {file: FileE, want: true},
34 | {file: FileF, want: true},
35 | {file: FileG, want: true},
36 | {file: FileH, want: true},
37 | {file: NoFile, want: false},
38 | }
39 | for _, tt := range tests {
40 | t.Run("IsValidFile should return true if file is valid, false otherwise", func(t *testing.T) {
41 | valid := IsValidFile(tt.file)
42 | if valid != tt.want {
43 | t.Errorf("IsValidFile(%d) = %v, want %v", tt.file, valid, tt.want)
44 | }
45 | })
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/move.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | type Move = int
11 |
12 | const (
13 | // 0 - 2: move type (required)
14 | // 3 - 9: origin square (required)
15 | // 10 - 16: target square (required)
16 | // 17 - 21: origin piece (required)
17 | // 22 - 26: target piece (optional)
18 | // 27 - 29: promotion (optional)
19 | moveTypeShift = 0
20 | moveTypeMask = 0x7 << moveTypeShift
21 | originSquareShift = 3
22 | originSquareMask = 0x7F << originSquareShift
23 | targetSquareShift = 10
24 | targetSquareMask = 0x7F << targetSquareShift
25 | originPieceShift = 17
26 | originPieceMask = 0x1F << originPieceShift
27 | targetPieceShift = 22
28 | targetPieceMask = 0x1F << targetPieceShift
29 | promotionShift = 27
30 | promotionMask = 0x7 << promotionShift
31 |
32 | NoMove Move = (NoMoveType << moveTypeShift) |
33 | (NoSquare << originSquareShift) |
34 | (NoSquare << targetSquareShift) |
35 | (NoPiece << originPieceShift) |
36 | (NoPiece << targetPieceShift) |
37 | (NoPieceType << promotionShift)
38 | )
39 |
40 | func MoveOf(mt MoveType, originSq Square, targetSq Square, originPc Piece, targetPc Piece, promotion PieceType) Move {
41 | return (mt << moveTypeShift) |
42 | (originSq << originSquareShift) |
43 | (targetSq << targetSquareShift) |
44 | (originPc << originPieceShift) |
45 | (targetPc << targetPieceShift) |
46 | (promotion << promotionShift)
47 | }
48 |
49 | func MoveTypeOf(m Move) MoveType {
50 | return (m & moveTypeMask) >> moveTypeShift
51 | }
52 |
53 | func OriginSquareOf(m Move) Square {
54 | return (m & originSquareMask) >> originSquareShift
55 | }
56 |
57 | func TargetSquareOf(m Move) Square {
58 | return (m & targetSquareMask) >> targetSquareShift
59 | }
60 |
61 | func OriginPieceOf(m Move) Piece {
62 | return (m & originPieceMask) >> originPieceShift
63 | }
64 |
65 | func TargetPieceOf(m Move) Piece {
66 | return (m & targetPieceMask) >> targetPieceShift
67 | }
68 |
69 | func PromotionOf(m Move) PieceType {
70 | return (m & promotionMask) >> promotionShift
71 | }
72 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/move_list.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | type MoveList struct {
11 | Size int
12 | Entries [MaxPly]MoveEntry
13 | }
14 |
15 | func (ml *MoveList) reset() {
16 | ml.Size = 0
17 | }
18 |
19 | func (ml *MoveList) add(m Move) {
20 | ml.Entries[ml.Size].Move = m
21 | ml.Size++
22 | }
23 |
24 | func (ml *MoveList) rateByMVVLVA() {
25 | for i := 0; i < ml.Size; i++ {
26 | m := ml.Entries[i].Move
27 | v := 0
28 |
29 | pieceTypeValue := pieceTypeValueOf(PieceTypeOf(OriginPieceOf(m)))
30 | v += kingValue / pieceTypeValue
31 |
32 | targetPc := TargetPieceOf(m)
33 | if targetPc != NoPiece {
34 | v += 10 * pieceTypeValueOf(PieceTypeOf(targetPc))
35 | }
36 |
37 | ml.Entries[i].value = v
38 | }
39 | }
40 |
41 | func (ml *MoveList) sort() {
42 | for i := 0; i < ml.Size; i++ {
43 | entry := ml.Entries[i]
44 |
45 | j := i
46 | for j > 0 && ml.Entries[j-1].value < entry.value {
47 | ml.Entries[j] = ml.Entries[j-1]
48 | j--
49 | }
50 |
51 | ml.Entries[j] = entry
52 | }
53 | }
54 |
55 | type MoveEntry struct {
56 | Move Move
57 | value value
58 | }
59 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/move_list_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | import (
11 | "testing"
12 | )
13 |
14 | func TestMoveList_add(t *testing.T) {
15 | t.Run("add should add a move to the move list", func(t *testing.T) {
16 | ml := &MoveList{}
17 | m := MoveOf(PawnPromotionMove, A7, B8, WhitePawn, BlackQueen, Knight)
18 |
19 | ml.add(m)
20 |
21 | if ml.Size != 1 {
22 | t.Errorf("move list should have size 1, but got %v", ml.Size)
23 | }
24 | if ml.Entries[0].Move != m {
25 | t.Errorf("first move list entry should have move %v, but got %v", m, ml.Entries[0].Move)
26 | }
27 | })
28 | }
29 |
30 | func TestMoveList_rateFromMVVLVA(t *testing.T) {
31 | tests := []struct {
32 | name string
33 | move Move
34 | want value
35 | }{
36 | {
37 | name: "rateByMVVLVA should rate a non-capturing move",
38 | move: MoveOf(NormalMove, D4, D5, WhitePawn, NoPiece, NoPieceType),
39 | want: kingValue / pawnValue,
40 | },
41 | {
42 | name: "rateByMVVLVA should rate a capturing move",
43 | move: MoveOf(NormalMove, D1, G4, WhiteQueen, BlackKnight, NoPieceType),
44 | want: kingValue/queenValue + 10*knightValue,
45 | },
46 | }
47 | for _, tt := range tests {
48 | t.Run(tt.name, func(t *testing.T) {
49 | ml := &MoveList{}
50 | ml.add(tt.move)
51 | ml.rateByMVVLVA()
52 | if ml.Entries[0].value != tt.want {
53 | t.Errorf("rateByMVVLVA() = %v, want %v", ml.Entries[0].value, tt.want)
54 | }
55 | })
56 | }
57 | }
58 |
59 | func TestMoveList_sort(t *testing.T) {
60 | t.Run("sort should sort all moves", func(t *testing.T) {
61 | ml := &MoveList{}
62 | m1 := MoveOf(NormalMove, D1, G4, WhiteQueen, BlackKnight, NoPieceType)
63 | m2 := MoveOf(NormalMove, C1, G5, WhiteBishop, BlackPawn, NoPieceType)
64 | m3 := MoveOf(NormalMove, F1, B5, WhiteBishop, NoPiece, NoPieceType)
65 | m4 := MoveOf(NormalMove, D4, D5, WhitePawn, NoPiece, NoPieceType)
66 | ml.add(m4)
67 | ml.Entries[ml.Size-1].value = 1
68 | ml.add(m3)
69 | ml.Entries[ml.Size-1].value = 2
70 | ml.add(m2)
71 | ml.Entries[ml.Size-1].value = 3
72 | ml.add(m1)
73 | ml.Entries[ml.Size-1].value = 4
74 |
75 | ml.sort()
76 |
77 | if ml.Entries[0].Move != m1 {
78 | t.Errorf("move at index 0 should be %v, but got %v", m1, ml.Entries[0].Move)
79 | }
80 | if ml.Entries[1].Move != m2 {
81 | t.Errorf("move at index 1 should be %v, but got %v", m2, ml.Entries[1].Move)
82 | }
83 | if ml.Entries[2].Move != m3 {
84 | t.Errorf("move at index 2 should be %v, but got %v", m3, ml.Entries[2].Move)
85 | }
86 | if ml.Entries[3].Move != m4 {
87 | t.Errorf("move at index 3 should be %v, but got %v", m4, ml.Entries[3].Move)
88 | }
89 | })
90 | }
91 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/move_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | import "testing"
11 |
12 | func TestMove_moveOf(t *testing.T) {
13 | t.Run("MoveOf should return the move", func(t *testing.T) {
14 | m := MoveOf(PawnPromotionMove, A7, B8, WhitePawn, BlackQueen, Knight)
15 |
16 | mt := MoveTypeOf(m)
17 | if mt != PawnPromotionMove {
18 | t.Errorf("wanted move type of move to be %v, but got %v", PawnPromotionMove, mt)
19 | }
20 | originSq := OriginSquareOf(m)
21 | if originSq != A7 {
22 | t.Errorf("wanted origin square of move to be %v, but got %v", A7, originSq)
23 | }
24 | targetSq := TargetSquareOf(m)
25 | if targetSq != B8 {
26 | t.Errorf("wanted target square of move to be %v, but got %v", B8, targetSq)
27 | }
28 | originPc := OriginPieceOf(m)
29 | if originPc != WhitePawn {
30 | t.Errorf("wanted origin piece of move to be %v, but got %v", WhitePawn, originPc)
31 | }
32 | targetPc := TargetPieceOf(m)
33 | if targetPc != BlackQueen {
34 | t.Errorf("wanted target piece of move to be %v, but got %v", BlackQueen, targetPc)
35 | }
36 | promotion := PromotionOf(m)
37 | if promotion != Knight {
38 | t.Errorf("wanted promotion of move to be %v, but got %v", Knight, promotion)
39 | }
40 | })
41 | }
42 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/move_type.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | type MoveType = int
11 |
12 | const (
13 | NormalMove MoveType = 0
14 | PawnDoubleMove MoveType = 1
15 | PawnPromotionMove MoveType = 2
16 | EnPassantMove MoveType = 3
17 | CastlingMove MoveType = 4
18 |
19 | NoMoveType MoveType = 5
20 | )
21 |
22 | var (
23 | MoveTypes = [5]MoveType{NormalMove, PawnDoubleMove, PawnPromotionMove, EnPassantMove, CastlingMove}
24 | )
25 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/move_type_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | import (
11 | "testing"
12 | )
13 |
14 | func TestMoveType(t *testing.T) {
15 | t.Run("MoveTypes array should be valid", func(t *testing.T) {
16 | for mt := range MoveTypes {
17 | if MoveTypes[mt] != mt {
18 | t.Errorf("wanted move types at index %d to be %v, but got %v", mt, mt, MoveTypes[mt])
19 | }
20 | }
21 | })
22 | }
23 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/piece.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | import "fmt"
11 |
12 | type Piece = int
13 |
14 | const (
15 | WhitePawn Piece = 0
16 | WhiteKnight Piece = 1
17 | WhiteBishop Piece = 2
18 | WhiteRook Piece = 3
19 | WhiteQueen Piece = 4
20 | WhiteKing Piece = 5
21 | BlackPawn Piece = 6
22 | BlackKnight Piece = 7
23 | BlackBishop Piece = 8
24 | BlackRook Piece = 9
25 | BlackQueen Piece = 10
26 | BlackKing Piece = 11
27 |
28 | NoPiece Piece = 12
29 | )
30 |
31 | var (
32 | Pieces = [12]Piece{
33 | WhitePawn, WhiteKnight, WhiteBishop, WhiteRook, WhiteQueen, WhiteKing,
34 | BlackPawn, BlackKnight, BlackBishop, BlackRook, BlackQueen, BlackKing,
35 | }
36 | )
37 |
38 | func IsValidPiece(pc Piece) bool {
39 | switch pc {
40 | case WhitePawn, WhiteKnight, WhiteBishop, WhiteRook, WhiteQueen, WhiteKing, BlackPawn, BlackKnight, BlackBishop, BlackRook, BlackQueen, BlackKing:
41 | return true
42 | default:
43 | return false
44 | }
45 | }
46 |
47 | func PieceOf(col Color, pt PieceType) Piece {
48 | switch col {
49 | case White:
50 | switch pt {
51 | case Pawn:
52 | return WhitePawn
53 | case Knight:
54 | return WhiteKnight
55 | case Bishop:
56 | return WhiteBishop
57 | case Rook:
58 | return WhiteRook
59 | case Queen:
60 | return WhiteQueen
61 | case King:
62 | return WhiteKing
63 | default:
64 | panic(fmt.Sprintf("Invalid piece type: %v", pt))
65 | }
66 | case Black:
67 | switch pt {
68 | case Pawn:
69 | return BlackPawn
70 | case Knight:
71 | return BlackKnight
72 | case Bishop:
73 | return BlackBishop
74 | case Rook:
75 | return BlackRook
76 | case Queen:
77 | return BlackQueen
78 | case King:
79 | return BlackKing
80 | default:
81 | panic(fmt.Sprintf("Invalid piece type: %v", pt))
82 | }
83 | default:
84 | panic(fmt.Sprintf("Invalid color: %v", col))
85 | }
86 | }
87 |
88 | func PieceColorOf(pc Piece) Color {
89 | switch pc {
90 | case WhitePawn, WhiteKnight, WhiteBishop, WhiteRook, WhiteQueen, WhiteKing:
91 | return White
92 | case BlackPawn, BlackKnight, BlackBishop, BlackRook, BlackQueen, BlackKing:
93 | return Black
94 | default:
95 | panic(fmt.Sprintf("Invalid piece: %v", pc))
96 | }
97 | }
98 |
99 | func PieceTypeOf(pc Piece) PieceType {
100 | switch pc {
101 | case WhitePawn, BlackPawn:
102 | return Pawn
103 | case WhiteKnight, BlackKnight:
104 | return Knight
105 | case WhiteBishop, BlackBishop:
106 | return Bishop
107 | case WhiteRook, BlackRook:
108 | return Rook
109 | case WhiteQueen, BlackQueen:
110 | return Queen
111 | case WhiteKing, BlackKing:
112 | return King
113 | default:
114 | panic(fmt.Sprintf("Invalid piece: %v", pc))
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/piece_type.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | import "fmt"
11 |
12 | type PieceType = int
13 |
14 | const (
15 | Pawn PieceType = 0
16 | Knight PieceType = 1
17 | Bishop PieceType = 2
18 | Rook PieceType = 3
19 | Queen PieceType = 4
20 | King PieceType = 5
21 |
22 | NoPieceType PieceType = 6
23 | )
24 |
25 | var (
26 | PieceTypes = [6]PieceType{Pawn, Knight, Bishop, Rook, Queen, King}
27 | )
28 |
29 | func IsValidPieceType(pt PieceType) bool {
30 | switch pt {
31 | case Pawn, Knight, Bishop, Rook, Queen, King:
32 | return true
33 | default:
34 | return false
35 | }
36 | }
37 |
38 | func isSliding(pt PieceType) bool {
39 | switch pt {
40 | case Bishop, Rook, Queen:
41 | return true
42 | case Pawn, Knight, King:
43 | return false
44 | default:
45 | panic(fmt.Sprintf("Invalid piece type: %v", pt))
46 | }
47 | }
48 |
49 | func pieceTypeValueOf(pt PieceType) value {
50 | switch pt {
51 | case Pawn:
52 | return pawnValue
53 | case Knight:
54 | return knightValue
55 | case Bishop:
56 | return bishopValue
57 | case Rook:
58 | return rookValue
59 | case Queen:
60 | return queenValue
61 | case King:
62 | return kingValue
63 | default:
64 | panic(fmt.Sprintf("Invalid piece type: %v", pt))
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/piece_type_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | import (
11 | "testing"
12 | )
13 |
14 | func TestPieceType(t *testing.T) {
15 | t.Run("PieceTypes array should be valid", func(t *testing.T) {
16 | for pt := range PieceTypes {
17 | if PieceTypes[pt] != pt {
18 | t.Errorf("wanted PieceTypes at index %d to be %v, but got %v", pt, pt, PieceTypes[pt])
19 | }
20 | }
21 | })
22 | }
23 |
24 | func TestPieceType_IsValidPieceType(t *testing.T) {
25 | tests := []struct {
26 | pieceType PieceType
27 | want bool
28 | }{
29 | {pieceType: Pawn, want: true},
30 | {pieceType: Knight, want: true},
31 | {pieceType: Bishop, want: true},
32 | {pieceType: Rook, want: true},
33 | {pieceType: Queen, want: true},
34 | {pieceType: King, want: true},
35 | {pieceType: NoPieceType, want: false},
36 | }
37 | for _, tt := range tests {
38 | t.Run("IsValidPieceType should return true if piece type is valid, false otherwise", func(t *testing.T) {
39 | valid := IsValidPieceType(tt.pieceType)
40 | if valid != tt.want {
41 | t.Errorf("IsValidPieceType(%d) = %v, want %v", tt.pieceType, valid, tt.want)
42 | }
43 | })
44 | }
45 | }
46 |
47 | func TestPieceType_isSliding(t *testing.T) {
48 | tests := []struct {
49 | pieceType PieceType
50 | want bool
51 | }{
52 | {pieceType: Pawn, want: false},
53 | {pieceType: Knight, want: false},
54 | {pieceType: Bishop, want: true},
55 | {pieceType: Rook, want: true},
56 | {pieceType: Queen, want: true},
57 | {pieceType: King, want: false},
58 | }
59 | for _, tt := range tests {
60 | t.Run("isSliding should return true when the pieceType is sliding", func(t *testing.T) {
61 | sliding := isSliding(tt.pieceType)
62 | if sliding != tt.want {
63 | t.Errorf("wanted piece type %v to be sliding: %v, but got %v", tt.pieceType, tt.want, sliding)
64 | }
65 | })
66 | }
67 | }
68 |
69 | func TestPieceType_pieceTypeValueOf(t *testing.T) {
70 | tests := []struct {
71 | pieceType PieceType
72 | want value
73 | }{
74 | {pieceType: Pawn, want: pawnValue},
75 | {pieceType: Knight, want: knightValue},
76 | {pieceType: Bishop, want: bishopValue},
77 | {pieceType: Rook, want: rookValue},
78 | {pieceType: Queen, want: queenValue},
79 | {pieceType: King, want: kingValue},
80 | }
81 | for _, tt := range tests {
82 | t.Run("pieceTypeValueOf should return the piece type value", func(t *testing.T) {
83 | v := pieceTypeValueOf(tt.pieceType)
84 | if v != tt.want {
85 | t.Errorf("wanted value of %v to be %v, but got %v", tt.pieceType, tt.want, v)
86 | }
87 | })
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/rank.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | type Rank = int
11 |
12 | const (
13 | Rank1 Rank = 0
14 | Rank2 Rank = 1
15 | Rank3 Rank = 2
16 | Rank4 Rank = 3
17 | Rank5 Rank = 4
18 | Rank6 Rank = 5
19 | Rank7 Rank = 6
20 | Rank8 Rank = 7
21 |
22 | NoRank Rank = 8
23 | )
24 |
25 | var (
26 | Ranks = [8]Rank{Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8}
27 | )
28 |
29 | func IsValidRank(r Rank) bool {
30 | switch r {
31 | case Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8:
32 | return true
33 | default:
34 | return false
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/rank_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | import (
11 | "testing"
12 | )
13 |
14 | func TestRank(t *testing.T) {
15 | t.Run("Ranks array should be valid", func(t *testing.T) {
16 | for r := range Ranks {
17 | if Ranks[r] != r {
18 | t.Errorf("wanted Ranks at index %d to be %v, but got %v", r, r, Ranks[r])
19 | }
20 | }
21 | })
22 | }
23 |
24 | func TestRank_IsValidRank(t *testing.T) {
25 | tests := []struct {
26 | rank Rank
27 | want bool
28 | }{
29 | {rank: Rank1, want: true},
30 | {rank: Rank2, want: true},
31 | {rank: Rank3, want: true},
32 | {rank: Rank4, want: true},
33 | {rank: Rank5, want: true},
34 | {rank: Rank6, want: true},
35 | {rank: Rank7, want: true},
36 | {rank: Rank8, want: true},
37 | {rank: NoRank, want: false},
38 | }
39 | for _, tt := range tests {
40 | t.Run("IsValidRank should return true if rank is valid, false otherwise", func(t *testing.T) {
41 | valid := IsValidRank(tt.rank)
42 | if valid != tt.want {
43 | t.Errorf("IsValidRank(%d) = %v, want %v", tt.rank, valid, tt.want)
44 | }
45 | })
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/square.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | type Square = int
11 |
12 | const (
13 | A1 Square = 0
14 | B1 Square = 1
15 | C1 Square = 2
16 | D1 Square = 3
17 | E1 Square = 4
18 | F1 Square = 5
19 | G1 Square = 6
20 | H1 Square = 7
21 |
22 | A2 Square = 16
23 | B2 Square = 17
24 | C2 Square = 18
25 | D2 Square = 19
26 | E2 Square = 20
27 | F2 Square = 21
28 | G2 Square = 22
29 | H2 Square = 23
30 |
31 | A3 Square = 32
32 | B3 Square = 33
33 | C3 Square = 34
34 | D3 Square = 35
35 | E3 Square = 36
36 | F3 Square = 37
37 | G3 Square = 38
38 | H3 Square = 39
39 |
40 | A4 Square = 48
41 | B4 Square = 49
42 | C4 Square = 50
43 | D4 Square = 51
44 | E4 Square = 52
45 | F4 Square = 53
46 | G4 Square = 54
47 | H4 Square = 55
48 |
49 | A5 Square = 64
50 | B5 Square = 65
51 | C5 Square = 66
52 | D5 Square = 67
53 | E5 Square = 68
54 | F5 Square = 69
55 | G5 Square = 70
56 | H5 Square = 71
57 |
58 | A6 Square = 80
59 | B6 Square = 81
60 | C6 Square = 82
61 | D6 Square = 83
62 | E6 Square = 84
63 | F6 Square = 85
64 | G6 Square = 86
65 | H6 Square = 87
66 |
67 | A7 Square = 96
68 | B7 Square = 97
69 | C7 Square = 98
70 | D7 Square = 99
71 | E7 Square = 100
72 | F7 Square = 101
73 | G7 Square = 102
74 | H7 Square = 103
75 |
76 | A8 Square = 112
77 | B8 Square = 113
78 | C8 Square = 114
79 | D8 Square = 115
80 | E8 Square = 116
81 | F8 Square = 117
82 | G8 Square = 118
83 | H8 Square = 119
84 |
85 | NoSquare Square = 127
86 | )
87 |
88 | const squaresMaxValue Square = 128
89 |
90 | var (
91 | Squares = [64]Square{
92 | A1, B1, C1, D1, E1, F1, G1, H1,
93 | A2, B2, C2, D2, E2, F2, G2, H2,
94 | A3, B3, C3, D3, E3, F3, G3, H3,
95 | A4, B4, C4, D4, E4, F4, G4, H4,
96 | A5, B5, C5, D5, E5, F5, G5, H5,
97 | A6, B6, C6, D6, E6, F6, G6, H6,
98 | A7, B7, C7, D7, E7, F7, G7, H7,
99 | A8, B8, C8, D8, E8, F8, G8, H8,
100 | }
101 | )
102 |
103 | func IsValidSquare(sq Square) bool {
104 | return (sq & 0x88) == 0
105 | }
106 |
107 | func SquareOf(f File, r Rank) Square {
108 | return (r << 4) + f
109 | }
110 |
111 | func FileOf(sq Square) File {
112 | return sq & 0xF
113 | }
114 |
115 | func RankOf(sq Square) Rank {
116 | return sq >> 4
117 | }
118 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/square_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | import (
11 | "testing"
12 | )
13 |
14 | func TestSquare(t *testing.T) {
15 | t.Run("Squares array should be valid", func(t *testing.T) {
16 | for r := range Ranks {
17 | for f := range Files {
18 | index := r*len(Ranks) + f
19 | sq := SquareOf(f, r)
20 | if Squares[index] != sq {
21 | t.Errorf("wanted Squares at index %d to be %v, but got %v", index, sq, Squares[index])
22 | }
23 | if !IsValidSquare(sq) {
24 | t.Errorf("wanted %v to be valid", sq)
25 | }
26 | }
27 | }
28 | })
29 | }
30 |
31 | func TestSquare_SquareOf(t *testing.T) {
32 | t.Run("When creating a square it should save and return file and rank correctly", func(t *testing.T) {
33 | for _, r := range Ranks {
34 | for _, f := range Files {
35 | sq := SquareOf(f, r)
36 | squareFile := FileOf(sq)
37 | if squareFile != f {
38 | t.Errorf("wanted file of square to be %v, but got %v", f, squareFile)
39 | }
40 | squareRank := RankOf(sq)
41 | if squareRank != r {
42 | t.Errorf("wanted rank of square to be %v, but got %v", r, squareRank)
43 | }
44 | }
45 | }
46 | })
47 | }
48 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/engine/value.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package engine
9 |
10 | type value = int
11 |
12 | const (
13 | // Piece values as defined by Larry Kaufman
14 | pawnValue value = 100
15 | knightValue value = 325
16 | bishopValue value = 325
17 | rookValue value = 500
18 | queenValue value = 975
19 | kingValue value = 20000
20 |
21 | infinite value = 200000
22 | checkmate value = 100000
23 | checkmateThreshold value = checkmate - MaxPly
24 | draw value = 0
25 |
26 | noValue value = 300000
27 | )
28 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/perft.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package pulse
9 |
10 | func NewPerft() *Perft {
11 | return &Perft{}
12 | }
13 |
14 | type Perft struct {
15 | }
16 |
17 | func (p *Perft) Run() {
18 | }
19 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/pulse.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package pulse
9 |
10 | import (
11 | "fmt"
12 |
13 | "github.com/fluxroot/pulse/internal/pulse/engine"
14 | "github.com/fluxroot/pulse/internal/pulse/uci"
15 | )
16 |
17 | func NewPulse(sender *uci.DefaultSender) *Pulse {
18 | return &Pulse{
19 | sender: sender,
20 | }
21 | }
22 |
23 | type Pulse struct {
24 | sender *uci.DefaultSender
25 | }
26 |
27 | func (p *Pulse) Initialize() error {
28 | if err := p.Stop(); err != nil {
29 | return fmt.Errorf("stop: %w", err)
30 | }
31 | if err := p.sender.ID("Pulse Go 2.0.0", "Phokham Nonava"); err != nil {
32 | return fmt.Errorf("id: %w", err)
33 | }
34 | if err := p.sender.OK(); err != nil {
35 | return fmt.Errorf("ok: %w", err)
36 | }
37 | return nil
38 | }
39 |
40 | func (p *Pulse) Ready() error {
41 | if err := p.sender.ReadyOK(); err != nil {
42 | return fmt.Errorf("ready ok: %w", err)
43 | }
44 | return nil
45 | }
46 |
47 | func (p *Pulse) SetNameOnlyOption(name string) {
48 | panic("implement me")
49 | }
50 |
51 | func (p *Pulse) SetNameValueOption(name string, value string) {
52 | panic("implement me")
53 | }
54 |
55 | func (p *Pulse) NewGame() error {
56 | panic("implement me")
57 | }
58 |
59 | func (p *Pulse) Position(pos *engine.Position) {
60 | panic("implement me")
61 | }
62 |
63 | func (p *Pulse) Start() error {
64 | panic("implement me")
65 | }
66 |
67 | func (p *Pulse) Stop() error {
68 | panic("implement me")
69 | }
70 |
71 | func (p *Pulse) PonderHit() error {
72 | panic("implement me")
73 | }
74 |
75 | func (p *Pulse) Quit() error {
76 | panic("implement me")
77 | }
78 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/uci/engine.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package uci
9 |
10 | import "github.com/fluxroot/pulse/internal/pulse/engine"
11 |
12 | //go:generate mockgen -source=engine.go -destination=mock/engine.go -package=mock
13 |
14 | type Engine interface {
15 | Initialize() error
16 | Ready() error
17 | SetNameOnlyOption(name string)
18 | SetNameValueOption(name string, value string)
19 | NewGame() error
20 | Position(p *engine.Position)
21 | Start() error
22 | Stop() error
23 | PonderHit() error
24 | Quit() error
25 | }
26 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/uci/reader.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package uci
9 |
10 | import (
11 | "bufio"
12 | "io"
13 | "os"
14 | )
15 |
16 | type Reader interface {
17 | Readln() (string, error)
18 | }
19 |
20 | func NewStdinReader() *StdinReader {
21 | return &StdinReader{
22 | scanner: bufio.NewScanner(os.Stdin),
23 | }
24 | }
25 |
26 | type StdinReader struct {
27 | scanner *bufio.Scanner
28 | }
29 |
30 | func (r *StdinReader) Readln() (string, error) {
31 | if r.scanner.Scan() {
32 | return r.scanner.Text(), nil
33 | } else if r.scanner.Err() != nil {
34 | return "", r.scanner.Err()
35 | } else {
36 | return "", io.EOF
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/uci/sender.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package uci
9 |
10 | import (
11 | "fmt"
12 | )
13 |
14 | type Sender interface {
15 | ID(name string, author string) error
16 | OK() error
17 | ReadyOK() error
18 | Debug(message string) error
19 | }
20 |
21 | func NewDefaultSender(writer Writer) *DefaultSender {
22 | return &DefaultSender{
23 | writer: writer,
24 | debugMode: false,
25 | }
26 | }
27 |
28 | type DefaultSender struct {
29 | writer Writer
30 | debugMode bool
31 | }
32 |
33 | func (s *DefaultSender) ID(name string, author string) error {
34 | if err := s.writer.Writeln(fmt.Sprintf("id name %s", name)); err != nil {
35 | return err
36 | }
37 | if err := s.writer.Writeln(fmt.Sprintf("id author %s", author)); err != nil {
38 | return err
39 | }
40 | return nil
41 | }
42 |
43 | func (s *DefaultSender) OK() error {
44 | if err := s.writer.Writeln("uciok"); err != nil {
45 | return err
46 | }
47 | return nil
48 | }
49 |
50 | func (s *DefaultSender) ReadyOK() error {
51 | if err := s.writer.Writeln("readyok"); err != nil {
52 | return err
53 | }
54 | return nil
55 | }
56 |
57 | func (s *DefaultSender) Debug(message string) error {
58 | if s.debugMode {
59 | if err := s.writer.Writeln(fmt.Sprintf("info string %s", message)); err != nil {
60 | return err
61 | }
62 | }
63 | return nil
64 | }
65 |
--------------------------------------------------------------------------------
/pulse-go/internal/pulse/uci/writer.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package uci
9 |
10 | import (
11 | "bufio"
12 | "fmt"
13 | "os"
14 | )
15 |
16 | type Writer interface {
17 | Writeln(s string) error
18 | }
19 |
20 | func NewStdoutWriter() *StdoutWriter {
21 | return &StdoutWriter{
22 | writer: bufio.NewWriter(os.Stdout),
23 | }
24 | }
25 |
26 | type StdoutWriter struct {
27 | writer *bufio.Writer
28 | }
29 |
30 | func (w *StdoutWriter) Writeln(s string) error {
31 | if _, err := fmt.Fprintln(w.writer, s); err != nil {
32 | return fmt.Errorf("fprintln: %w", err)
33 | }
34 | if err := w.writer.Flush(); err != nil {
35 | return fmt.Errorf("flush: %w", err)
36 | }
37 | return nil
38 | }
39 |
--------------------------------------------------------------------------------
/pulse-go/vendor/go.uber.org/mock/AUTHORS:
--------------------------------------------------------------------------------
1 | # This is the official list of GoMock authors for copyright purposes.
2 | # This file is distinct from the CONTRIBUTORS files.
3 | # See the latter for an explanation.
4 |
5 | # Names should be added to this file as
6 | # Name or Organization
7 | # The email address is not required for organizations.
8 |
9 | # Please keep the list sorted.
10 |
11 | Alex Reece
12 | Google Inc.
13 |
--------------------------------------------------------------------------------
/pulse-go/vendor/go.uber.org/mock/gomock/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | // Package gomock is a mock framework for Go.
16 | //
17 | // Standard usage:
18 | //
19 | // (1) Define an interface that you wish to mock.
20 | // type MyInterface interface {
21 | // SomeMethod(x int64, y string)
22 | // }
23 | // (2) Use mockgen to generate a mock from the interface.
24 | // (3) Use the mock in a test:
25 | // func TestMyThing(t *testing.T) {
26 | // mockCtrl := gomock.NewController(t)
27 | // mockObj := something.NewMockMyInterface(mockCtrl)
28 | // mockObj.EXPECT().SomeMethod(4, "blah")
29 | // // pass mockObj to a real object and play with it.
30 | // }
31 | //
32 | // By default, expected calls are not enforced to run in any particular order.
33 | // Call order dependency can be enforced by use of InOrder and/or Call.After.
34 | // Call.After can create more varied call order dependencies, but InOrder is
35 | // often more convenient.
36 | //
37 | // The following examples create equivalent call order dependencies.
38 | //
39 | // Example of using Call.After to chain expected call order:
40 | //
41 | // firstCall := mockObj.EXPECT().SomeMethod(1, "first")
42 | // secondCall := mockObj.EXPECT().SomeMethod(2, "second").After(firstCall)
43 | // mockObj.EXPECT().SomeMethod(3, "third").After(secondCall)
44 | //
45 | // Example of using InOrder to declare expected call order:
46 | //
47 | // gomock.InOrder(
48 | // mockObj.EXPECT().SomeMethod(1, "first"),
49 | // mockObj.EXPECT().SomeMethod(2, "second"),
50 | // mockObj.EXPECT().SomeMethod(3, "third"),
51 | // )
52 | //
53 | // The standard TestReporter most users will pass to `NewController` is a
54 | // `*testing.T` from the context of the test. Note that this will use the
55 | // standard `t.Error` and `t.Fatal` methods to report what happened in the test.
56 | // In some cases this can leave your testing package in a weird state if global
57 | // state is used since `t.Fatal` is like calling panic in the middle of a
58 | // function. In these cases it is recommended that you pass in your own
59 | // `TestReporter`.
60 | package gomock
61 |
--------------------------------------------------------------------------------
/pulse-go/vendor/go.uber.org/mock/gomock/string.go:
--------------------------------------------------------------------------------
1 | package gomock
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | )
7 |
8 | // getString is a safe way to convert a value to a string for printing results
9 | // If the value is a a mock, getString avoids calling the mocked String() method,
10 | // which avoids potential deadlocks
11 | func getString(x any) string {
12 | if isGeneratedMock(x) {
13 | return fmt.Sprintf("%T", x)
14 | }
15 | if s, ok := x.(fmt.Stringer); ok {
16 | return s.String()
17 | }
18 | return fmt.Sprintf("%v", x)
19 | }
20 |
21 | // isGeneratedMock checks if the given type has a "isgomock" field,
22 | // indicating it is a generated mock.
23 | func isGeneratedMock(x any) bool {
24 | typ := reflect.TypeOf(x)
25 | if typ == nil {
26 | return false
27 | }
28 | if typ.Kind() == reflect.Ptr {
29 | typ = typ.Elem()
30 | }
31 | if typ.Kind() != reflect.Struct {
32 | return false
33 | }
34 | _, isgomock := typ.FieldByName("isgomock")
35 | return isgomock
36 | }
37 |
--------------------------------------------------------------------------------
/pulse-go/vendor/modules.txt:
--------------------------------------------------------------------------------
1 | # go.uber.org/mock v0.5.0
2 | ## explicit; go 1.22
3 | go.uber.org/mock/gomock
4 |
--------------------------------------------------------------------------------
/pulse-java/pulse-java.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | application
3 | }
4 |
5 | dependencies {
6 | implementation(libs.jcpi)
7 | testImplementation(libs.junit)
8 | testImplementation(libs.assertj)
9 | }
10 |
11 | java {
12 | toolchain {
13 | languageVersion.set(JavaLanguageVersion.of(17))
14 | }
15 | }
16 |
17 | tasks.withType {
18 | options.compilerArgs.addAll(listOf("-Xlint:all", "-Werror"))
19 | }
20 |
21 | tasks.test {
22 | useJUnitPlatform()
23 | }
24 |
25 | application {
26 | mainClass.set("com.fluxchess.pulse.java.Main")
27 | executableDir = ""
28 | }
29 |
30 | tasks.named("run") {
31 | standardInput = System.`in`
32 | }
33 |
34 | tasks.withType {
35 | enabled = false
36 | }
37 |
38 | distributions {
39 | main {
40 | contents {
41 | from(rootProject.layout.projectDirectory.dir("src/main/dist"))
42 | from(rootProject.layout.projectDirectory.file("README.md"))
43 | from(rootProject.layout.projectDirectory.file("LICENSE"))
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/pulse-java/src/main/java/com/fluxchess/pulse/java/Bitboard.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java;
8 |
9 | import static java.lang.Long.bitCount;
10 | import static java.lang.Long.numberOfTrailingZeros;
11 |
12 | final class Bitboard {
13 |
14 | private Bitboard() {
15 | }
16 |
17 | static long add(int square, long bitboard) {
18 | return bitboard | 1L << toBitSquare(square);
19 | }
20 |
21 | static long remove(int square, long bitboard) {
22 | return bitboard & ~(1L << toBitSquare(square));
23 | }
24 |
25 | static int next(long bitboard) {
26 | return toX88Square(numberOfTrailingZeros(bitboard));
27 | }
28 |
29 | static long remainder(long bitboard) {
30 | return bitboard & (bitboard - 1);
31 | }
32 |
33 | static int size(long bitboard) {
34 | return bitCount(bitboard);
35 | }
36 |
37 | private static int toX88Square(int square) {
38 | return ((square & ~7) << 1) | (square & 7);
39 | }
40 |
41 | private static int toBitSquare(int square) {
42 | return ((square & ~7) >>> 1) | (square & 7);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/pulse-java/src/main/java/com/fluxchess/pulse/java/Main.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java;
8 |
9 | import java.io.BufferedReader;
10 | import java.io.InputStreamReader;
11 | import java.io.PrintStream;
12 |
13 | public final class Main {
14 |
15 | public static void main(String[] args) {
16 | if (args.length == 0) {
17 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
18 | PrintStream printer = System.out;
19 | Pulse engine = new Pulse(reader, printer);
20 | engine.run();
21 | } else if (args.length == 1 && "perft".equalsIgnoreCase(args[0])) {
22 | new Perft().run();
23 | } else {
24 | printUsage();
25 | System.exit(1);
26 | }
27 | }
28 |
29 | private static void printUsage() {
30 | System.err.println("Usage: pulse-java [perft]");
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/pulse-java/src/main/java/com/fluxchess/pulse/java/MoveList.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java;
8 |
9 | import com.fluxchess.pulse.java.model.Depth;
10 | import com.fluxchess.pulse.java.model.Move;
11 | import com.fluxchess.pulse.java.model.Piece;
12 | import com.fluxchess.pulse.java.model.PieceType;
13 | import com.fluxchess.pulse.java.model.Value;
14 |
15 | import java.lang.reflect.Array;
16 | import java.lang.reflect.InvocationTargetException;
17 |
18 | import static com.fluxchess.pulse.java.model.Move.NOMOVE;
19 |
20 | /**
21 | * This class stores our moves for a specific position. For the root node we
22 | * will populate pv for every root move.
23 | */
24 | final class MoveList {
25 |
26 | private static final int MAX_MOVES = 256;
27 |
28 | final T[] entries;
29 | int size = 0;
30 |
31 | static final class MoveVariation {
32 |
33 | final int[] moves = new int[Depth.MAX_PLY];
34 | int size = 0;
35 | }
36 |
37 | static class MoveEntry {
38 |
39 | int move = NOMOVE;
40 | int value = Value.NOVALUE;
41 | }
42 |
43 | static final class RootEntry extends MoveEntry {
44 |
45 | final MoveVariation pv = new MoveVariation();
46 | }
47 |
48 | MoveList(Class clazz) {
49 | @SuppressWarnings("unchecked") final T[] entries = (T[]) Array.newInstance(clazz, MAX_MOVES);
50 | this.entries = entries;
51 | try {
52 | for (int i = 0; i < entries.length; i++) {
53 | entries[i] = clazz.getDeclaredConstructor().newInstance();
54 | }
55 | } catch (InstantiationException | IllegalAccessException | NoSuchMethodException |
56 | InvocationTargetException e) {
57 | throw new IllegalStateException(e);
58 | }
59 | }
60 |
61 | /**
62 | * Sorts the move list using a stable insertion sort.
63 | */
64 | void sort() {
65 | for (int i = 1; i < size; i++) {
66 | T entry = entries[i];
67 |
68 | int j = i;
69 | while ((j > 0) && (entries[j - 1].value < entry.value)) {
70 | entries[j] = entries[j - 1];
71 | j--;
72 | }
73 |
74 | entries[j] = entry;
75 | }
76 | }
77 |
78 | /**
79 | * Rates the moves in the list according to "Most Valuable Victim - Least Valuable Aggressor".
80 | */
81 | void rateFromMVVLVA() {
82 | for (int i = 0; i < size; i++) {
83 | int move = entries[i].move;
84 | int value = 0;
85 |
86 | int piecetypeValue = PieceType.getValue(Piece.getType(Move.getOriginPiece(move)));
87 | value += PieceType.KING_VALUE / piecetypeValue;
88 |
89 | int target = Move.getTargetPiece(move);
90 | if (Piece.isValid(target)) {
91 | value += 10 * PieceType.getValue(Piece.getType(target));
92 | }
93 |
94 | entries[i].value = value;
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/pulse-java/src/main/java/com/fluxchess/pulse/java/Perft.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java;
8 |
9 | import static com.fluxchess.pulse.java.model.Color.opposite;
10 | import static java.lang.System.currentTimeMillis;
11 | import static java.util.concurrent.TimeUnit.HOURS;
12 | import static java.util.concurrent.TimeUnit.MILLISECONDS;
13 | import static java.util.concurrent.TimeUnit.MINUTES;
14 | import static java.util.concurrent.TimeUnit.SECONDS;
15 |
16 | public final class Perft {
17 |
18 | private static final int MAX_DEPTH = 6;
19 |
20 | private final MoveGenerator[] moveGenerators = new MoveGenerator[MAX_DEPTH];
21 |
22 | public void run() {
23 | Position position = Notation.toPosition(Notation.STANDARDPOSITION);
24 | int depth = MAX_DEPTH;
25 |
26 | for (int i = 0; i < MAX_DEPTH; i++) {
27 | moveGenerators[i] = new MoveGenerator();
28 | }
29 |
30 | System.out.format("Testing %s at depth %d%n", Notation.fromPosition(position), depth);
31 |
32 | long startTime = currentTimeMillis();
33 | long result = miniMax(depth, position, 0);
34 | long endTime = currentTimeMillis();
35 |
36 | long duration = endTime - startTime;
37 |
38 | System.out.format(
39 | "Nodes: %d%nDuration: %02d:%02d:%02d.%03d%n",
40 | result,
41 | MILLISECONDS.toHours(duration),
42 | MILLISECONDS.toMinutes(duration) - HOURS.toMinutes(MILLISECONDS.toHours(duration)),
43 | MILLISECONDS.toSeconds(duration) - MINUTES.toSeconds(MILLISECONDS.toMinutes(duration)),
44 | duration - SECONDS.toMillis(MILLISECONDS.toSeconds(duration))
45 | );
46 |
47 | System.out.format("n/ms: %d%n", result / duration);
48 | }
49 |
50 | private long miniMax(int depth, Position position, int ply) {
51 | if (depth == 0) {
52 | return 1;
53 | }
54 |
55 | int totalNodes = 0;
56 |
57 | boolean isCheck = position.isCheck();
58 | MoveGenerator moveGenerator = moveGenerators[ply];
59 | MoveList moves = moveGenerator.getMoves(position, depth, isCheck);
60 | for (int i = 0; i < moves.size; i++) {
61 | int move = moves.entries[i].move;
62 |
63 | position.makeMove(move);
64 | if (!position.isCheck(opposite(position.activeColor))) {
65 | totalNodes += miniMax(depth - 1, position, ply + 1);
66 | }
67 | position.undoMove(move);
68 | }
69 |
70 | return totalNodes;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/pulse-java/src/main/java/com/fluxchess/pulse/java/Protocol.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java;
8 |
9 | interface Protocol {
10 |
11 | void sendBestMove(int bestMove, int ponderMove);
12 |
13 | void sendStatus(int currentDepth, int currentMaxDepth, long totalNodes, int currentMove, int currentMoveNumber);
14 |
15 | void sendStatus(boolean force, int currentDepth, int currentMaxDepth, long totalNodes, int currentMove, int currentMoveNumber);
16 |
17 | void sendMove(MoveList.RootEntry entry, int currentDepth, int currentMaxDepth, long totalNodes);
18 |
19 | void sendInfo(String message);
20 |
21 | void sendDebug(String message);
22 | }
23 |
--------------------------------------------------------------------------------
/pulse-java/src/main/java/com/fluxchess/pulse/java/model/Castling.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | public final class Castling {
10 |
11 | public static final int WHITE_KINGSIDE = 1; // 1 << 0
12 | public static final int WHITE_QUEENSIDE = 1 << 1;
13 | public static final int BLACK_KINGSIDE = 1 << 2;
14 | public static final int BLACK_QUEENSIDE = 1 << 3;
15 |
16 | public static final int NOCASTLING = 0;
17 |
18 | public static final int VALUES_LENGTH = 16;
19 |
20 | private Castling() {
21 | }
22 |
23 | public static int valueOf(int color, int castlingtype) {
24 | switch (color) {
25 | case Color.WHITE:
26 | switch (castlingtype) {
27 | case CastlingType.KINGSIDE:
28 | return WHITE_KINGSIDE;
29 | case CastlingType.QUEENSIDE:
30 | return WHITE_QUEENSIDE;
31 | default:
32 | throw new IllegalArgumentException();
33 | }
34 | case Color.BLACK:
35 | switch (castlingtype) {
36 | case CastlingType.KINGSIDE:
37 | return BLACK_KINGSIDE;
38 | case CastlingType.QUEENSIDE:
39 | return BLACK_QUEENSIDE;
40 | default:
41 | throw new IllegalArgumentException();
42 | }
43 | default:
44 | throw new IllegalArgumentException();
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/pulse-java/src/main/java/com/fluxchess/pulse/java/model/CastlingType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | public final class CastlingType {
10 |
11 | public static final int KINGSIDE = 0;
12 | public static final int QUEENSIDE = 1;
13 |
14 | public static final int NOCASTLINGTYPE = 2;
15 |
16 | public static final int[] values = {
17 | KINGSIDE, QUEENSIDE
18 | };
19 |
20 | private CastlingType() {
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/pulse-java/src/main/java/com/fluxchess/pulse/java/model/Color.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | public final class Color {
10 |
11 | public static final int WHITE = 0;
12 | public static final int BLACK = 1;
13 |
14 | public static final int NOCOLOR = 2;
15 |
16 | public static final int[] values = {
17 | WHITE, BLACK
18 | };
19 |
20 | private Color() {
21 | }
22 |
23 | public static int opposite(int color) {
24 | switch (color) {
25 | case WHITE:
26 | return BLACK;
27 | case BLACK:
28 | return WHITE;
29 | default:
30 | throw new IllegalArgumentException();
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/pulse-java/src/main/java/com/fluxchess/pulse/java/model/Depth.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | public final class Depth {
10 |
11 | public static final int MAX_PLY = 256;
12 | public static final int MAX_DEPTH = 64;
13 |
14 | private Depth() {
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/pulse-java/src/main/java/com/fluxchess/pulse/java/model/File.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | public final class File {
10 |
11 | public static final int a = 0;
12 | public static final int b = 1;
13 | public static final int c = 2;
14 | public static final int d = 3;
15 | public static final int e = 4;
16 | public static final int f = 5;
17 | public static final int g = 6;
18 | public static final int h = 7;
19 |
20 | public static final int NOFILE = 8;
21 |
22 | public static final int[] values = {
23 | a, b, c, d, e, f, g, h
24 | };
25 |
26 | private File() {
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/pulse-java/src/main/java/com/fluxchess/pulse/java/model/MoveType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | public final class MoveType {
10 |
11 | public static final int MASK = 0x7;
12 |
13 | public static final int NORMAL = 0;
14 | public static final int PAWNDOUBLE = 1;
15 | public static final int PAWNPROMOTION = 2;
16 | public static final int ENPASSANT = 3;
17 | public static final int CASTLING = 4;
18 |
19 | public static final int NOMOVETYPE = 5;
20 |
21 | private MoveType() {
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/pulse-java/src/main/java/com/fluxchess/pulse/java/model/PieceType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | public final class PieceType {
10 |
11 | public static final int MASK = 0x7;
12 |
13 | public static final int PAWN = 0;
14 | public static final int KNIGHT = 1;
15 | public static final int BISHOP = 2;
16 | public static final int ROOK = 3;
17 | public static final int QUEEN = 4;
18 | public static final int KING = 5;
19 |
20 | public static final int NOPIECETYPE = 6;
21 |
22 | public static final int[] values = {
23 | PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING
24 | };
25 |
26 | // Piece values as defined by Larry Kaufman
27 | public static final int PAWN_VALUE = 100;
28 | public static final int KNIGHT_VALUE = 325;
29 | public static final int BISHOP_VALUE = 325;
30 | public static final int ROOK_VALUE = 500;
31 | public static final int QUEEN_VALUE = 975;
32 | public static final int KING_VALUE = 20000;
33 |
34 | private PieceType() {
35 | }
36 |
37 | public static boolean isValidPromotion(int piecetype) {
38 | switch (piecetype) {
39 | case KNIGHT:
40 | case BISHOP:
41 | case ROOK:
42 | case QUEEN:
43 | return true;
44 | default:
45 | return false;
46 | }
47 | }
48 |
49 | public static boolean isSliding(int piecetype) {
50 | switch (piecetype) {
51 | case BISHOP:
52 | case ROOK:
53 | case QUEEN:
54 | return true;
55 | case PAWN:
56 | case KNIGHT:
57 | case KING:
58 | return false;
59 | default:
60 | throw new IllegalArgumentException();
61 | }
62 | }
63 |
64 | public static int getValue(int piecetype) {
65 | switch (piecetype) {
66 | case PAWN:
67 | return PAWN_VALUE;
68 | case KNIGHT:
69 | return KNIGHT_VALUE;
70 | case BISHOP:
71 | return BISHOP_VALUE;
72 | case ROOK:
73 | return ROOK_VALUE;
74 | case QUEEN:
75 | return QUEEN_VALUE;
76 | case KING:
77 | return KING_VALUE;
78 | default:
79 | throw new IllegalArgumentException();
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/pulse-java/src/main/java/com/fluxchess/pulse/java/model/Rank.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | public final class Rank {
10 |
11 | public static final int r1 = 0;
12 | public static final int r2 = 1;
13 | public static final int r3 = 2;
14 | public static final int r4 = 3;
15 | public static final int r5 = 4;
16 | public static final int r6 = 5;
17 | public static final int r7 = 6;
18 | public static final int r8 = 7;
19 |
20 | public static final int NORANK = 8;
21 |
22 | public static final int[] values = {
23 | r1, r2, r3, r4, r5, r6, r7, r8
24 | };
25 |
26 | private Rank() {
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/pulse-java/src/main/java/com/fluxchess/pulse/java/model/Value.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | import static com.fluxchess.pulse.java.model.Depth.MAX_PLY;
10 | import static java.lang.Math.abs;
11 |
12 | public final class Value {
13 |
14 | public static final int INFINITE = 200000;
15 | public static final int CHECKMATE = 100000;
16 | public static final int CHECKMATE_THRESHOLD = CHECKMATE - MAX_PLY;
17 | public static final int DRAW = 0;
18 |
19 | public static final int NOVALUE = 300000;
20 |
21 | private Value() {
22 | }
23 |
24 | public static boolean isCheckmate(int value) {
25 | int absvalue = abs(value);
26 | return absvalue >= CHECKMATE_THRESHOLD && absvalue <= CHECKMATE;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/pulse-java/src/test/java/com/fluxchess/pulse/java/BitboardTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java;
8 |
9 | import com.fluxchess.pulse.java.model.Square;
10 | import org.junit.jupiter.api.BeforeEach;
11 | import org.junit.jupiter.api.Test;
12 |
13 | import java.util.LinkedList;
14 | import java.util.Random;
15 |
16 | import static com.fluxchess.pulse.java.model.Square.a6;
17 | import static org.assertj.core.api.Assertions.assertThat;
18 |
19 | class BitboardTest {
20 |
21 | private LinkedList pool = null;
22 |
23 | @BeforeEach
24 | void setUp() {
25 | Random random = new Random();
26 | pool = new LinkedList<>();
27 |
28 | while (pool.size() < Long.SIZE) {
29 | int value = random.nextInt(Long.SIZE);
30 | if (!pool.contains(Square.values[value])) {
31 | pool.add(Square.values[value]);
32 | }
33 | }
34 | }
35 |
36 | @Test
37 | void shouldAddAllSquaresCorrectly() {
38 | long bitboard = 0;
39 |
40 | for (int x88square : pool) {
41 | bitboard = Bitboard.add(x88square, bitboard);
42 | }
43 |
44 | assertThat(bitboard).isEqualTo(-1L);
45 | }
46 |
47 | @Test
48 | void shouldRemoveAllSquaresCorrectly() {
49 | long bitboard = -1;
50 |
51 | for (int x88square : pool) {
52 | bitboard = Bitboard.remove(x88square, bitboard);
53 | }
54 |
55 | assertThat(bitboard).isEqualTo(0L);
56 | }
57 |
58 | @Test
59 | void shouldReturnTheNextSquare() {
60 | long bitboard = Bitboard.add(a6, 0);
61 |
62 | int square = Bitboard.next(bitboard);
63 |
64 | assertThat(square).isEqualTo(a6);
65 | }
66 |
67 | @Test
68 | void shouldReturnCorrectRemainder() {
69 | long bitboard = 0b1110100;
70 |
71 | long remainder = Bitboard.remainder(bitboard);
72 |
73 | assertThat(remainder).isEqualTo(0b1110000L);
74 | }
75 |
76 | @Test
77 | void shouldReturnCorrectSize() {
78 | long bitboard = 0b111;
79 |
80 | int size = Bitboard.size(bitboard);
81 |
82 | assertThat(size).isEqualTo(3);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/pulse-java/src/test/java/com/fluxchess/pulse/java/EvaluationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java;
8 |
9 | import org.junit.jupiter.api.Test;
10 |
11 | import static org.assertj.core.api.Assertions.assertThat;
12 |
13 | class EvaluationTest {
14 |
15 | @Test
16 | void testEvaluate() {
17 | Position position = Notation.toPosition(Notation.STANDARDPOSITION);
18 | Evaluation evaluation = new Evaluation();
19 |
20 | assertThat(evaluation.evaluate(position)).isEqualTo(Evaluation.TEMPO);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/pulse-java/src/test/java/com/fluxchess/pulse/java/MoveListTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java;
8 |
9 | import org.junit.jupiter.api.Test;
10 |
11 | import static org.assertj.core.api.Assertions.assertThat;
12 |
13 | class MoveListTest {
14 |
15 | @Test
16 | void test() {
17 | MoveList moveList = new MoveList<>(MoveList.MoveEntry.class);
18 |
19 | assertThat(moveList.size).isEqualTo(0);
20 |
21 | moveList.entries[moveList.size++].move = 1;
22 | assertThat(moveList.size).isEqualTo(1);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/pulse-java/src/test/java/com/fluxchess/pulse/java/model/CastlingTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | import org.junit.jupiter.api.Test;
10 |
11 | import static org.assertj.core.api.Assertions.assertThat;
12 |
13 | class CastlingTest {
14 |
15 | @Test
16 | void testValueOf() {
17 | assertThat(Castling.valueOf(Color.WHITE, CastlingType.KINGSIDE)).isEqualTo(Castling.WHITE_KINGSIDE);
18 | assertThat(Castling.valueOf(Color.WHITE, CastlingType.QUEENSIDE)).isEqualTo(Castling.WHITE_QUEENSIDE);
19 | assertThat(Castling.valueOf(Color.BLACK, CastlingType.KINGSIDE)).isEqualTo(Castling.BLACK_KINGSIDE);
20 | assertThat(Castling.valueOf(Color.BLACK, CastlingType.QUEENSIDE)).isEqualTo(Castling.BLACK_QUEENSIDE);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/pulse-java/src/test/java/com/fluxchess/pulse/java/model/CastlingTypeTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | import org.junit.jupiter.api.Test;
10 |
11 | import static org.assertj.core.api.Assertions.assertThat;
12 |
13 | class CastlingTypeTest {
14 |
15 | @Test
16 | void testValues() {
17 | for (int castlingtype : CastlingType.values) {
18 | assertThat(CastlingType.values[castlingtype]).isEqualTo(castlingtype);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/pulse-java/src/test/java/com/fluxchess/pulse/java/model/ColorTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | import org.junit.jupiter.api.Test;
10 |
11 | import static com.fluxchess.pulse.java.model.Color.BLACK;
12 | import static com.fluxchess.pulse.java.model.Color.WHITE;
13 | import static com.fluxchess.pulse.java.model.Color.opposite;
14 | import static org.assertj.core.api.Assertions.assertThat;
15 |
16 | class ColorTest {
17 |
18 | @Test
19 | void testValues() {
20 | for (int color : Color.values) {
21 | assertThat(Color.values[color]).isEqualTo(color);
22 | }
23 | }
24 |
25 | @Test
26 | void testOpposite() {
27 | assertThat(opposite(BLACK)).isEqualTo(WHITE);
28 | assertThat(opposite(WHITE)).isEqualTo(BLACK);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/pulse-java/src/test/java/com/fluxchess/pulse/java/model/FileTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | import org.junit.jupiter.api.Test;
10 |
11 | import static org.assertj.core.api.Assertions.assertThat;
12 |
13 | class FileTest {
14 |
15 | @Test
16 | void testValues() {
17 | for (int file : File.values) {
18 | assertThat(File.values[file]).isEqualTo(file);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/pulse-java/src/test/java/com/fluxchess/pulse/java/model/MoveTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | import org.junit.jupiter.api.Test;
10 |
11 | import static com.fluxchess.pulse.java.model.MoveType.PAWNPROMOTION;
12 | import static com.fluxchess.pulse.java.model.Square.a7;
13 | import static com.fluxchess.pulse.java.model.Square.b7;
14 | import static com.fluxchess.pulse.java.model.Square.b8;
15 | import static com.fluxchess.pulse.java.model.Square.c8;
16 | import static org.assertj.core.api.Assertions.assertThat;
17 |
18 | class MoveTest {
19 |
20 | @Test
21 | void testCreation() {
22 | int move = Move.valueOf(PAWNPROMOTION, a7, b8, Piece.WHITE_PAWN, Piece.BLACK_QUEEN, PieceType.KNIGHT);
23 |
24 | assertThat(Move.getType(move)).isEqualTo(PAWNPROMOTION);
25 | assertThat(Move.getOriginSquare(move)).isEqualTo(a7);
26 | assertThat(Move.getTargetSquare(move)).isEqualTo(b8);
27 | assertThat(Move.getOriginPiece(move)).isEqualTo(Piece.WHITE_PAWN);
28 | assertThat(Move.getTargetPiece(move)).isEqualTo(Piece.BLACK_QUEEN);
29 | assertThat(Move.getPromotion(move)).isEqualTo(PieceType.KNIGHT);
30 | }
31 |
32 | @Test
33 | void testPromotion() {
34 | int move = Move.valueOf(PAWNPROMOTION, b7, c8, Piece.WHITE_PAWN, Piece.BLACK_QUEEN, PieceType.KNIGHT);
35 |
36 | assertThat(Move.getPromotion(move)).isEqualTo(PieceType.KNIGHT);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/pulse-java/src/test/java/com/fluxchess/pulse/java/model/PieceTypeTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | import org.junit.jupiter.api.Test;
10 |
11 | import static org.assertj.core.api.Assertions.assertThat;
12 |
13 | class PieceTypeTest {
14 |
15 | @Test
16 | void testValues() {
17 | for (int piecetype : PieceType.values) {
18 | assertThat(PieceType.values[piecetype]).isEqualTo(piecetype);
19 | }
20 | }
21 |
22 | @Test
23 | void testIsValidPromotion() {
24 | assertThat(PieceType.isValidPromotion(PieceType.KNIGHT)).isEqualTo(true);
25 | assertThat(PieceType.isValidPromotion(PieceType.BISHOP)).isEqualTo(true);
26 | assertThat(PieceType.isValidPromotion(PieceType.ROOK)).isEqualTo(true);
27 | assertThat(PieceType.isValidPromotion(PieceType.QUEEN)).isEqualTo(true);
28 | assertThat(PieceType.isValidPromotion(PieceType.PAWN)).isEqualTo(false);
29 | assertThat(PieceType.isValidPromotion(PieceType.KING)).isEqualTo(false);
30 | assertThat(PieceType.isValidPromotion(PieceType.NOPIECETYPE)).isEqualTo(false);
31 | }
32 |
33 | @Test
34 | void testIsSliding() {
35 | assertThat(PieceType.isSliding(PieceType.BISHOP)).isEqualTo(true);
36 | assertThat(PieceType.isSliding(PieceType.ROOK)).isEqualTo(true);
37 | assertThat(PieceType.isSliding(PieceType.QUEEN)).isEqualTo(true);
38 | assertThat(PieceType.isSliding(PieceType.PAWN)).isEqualTo(false);
39 | assertThat(PieceType.isSliding(PieceType.KNIGHT)).isEqualTo(false);
40 | assertThat(PieceType.isSliding(PieceType.KING)).isEqualTo(false);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/pulse-java/src/test/java/com/fluxchess/pulse/java/model/RankTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | import org.junit.jupiter.api.Test;
10 |
11 | import static org.assertj.core.api.Assertions.assertThat;
12 |
13 | class RankTest {
14 |
15 | @Test
16 | void testValues() {
17 | for (int rank : Rank.values) {
18 | assertThat(Rank.values[rank]).isEqualTo(rank);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/pulse-java/src/test/java/com/fluxchess/pulse/java/model/SquareTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 | package com.fluxchess.pulse.java.model;
8 |
9 | import org.junit.jupiter.api.Test;
10 |
11 | import static org.assertj.core.api.Assertions.assertThat;
12 |
13 | class SquareTest {
14 |
15 | @Test
16 | void testValues() {
17 | for (int rank : Rank.values) {
18 | for (int file : File.values) {
19 | int square = Square.valueOf(file, rank);
20 |
21 | assertThat(Square.getFile(square)).isEqualTo(file);
22 | assertThat(Square.getRank(square)).isEqualTo(rank);
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/pulse-kotlin/pulse-kotlin.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType
2 |
3 | plugins {
4 | alias(libs.plugins.kotlin.multiplatform)
5 | distribution
6 | }
7 |
8 | kotlin {
9 | compilerOptions {
10 | allWarningsAsErrors.set(true)
11 | }
12 | linuxX64 {
13 | binaries {
14 | executable()
15 | }
16 | }
17 | macosX64 {
18 | binaries {
19 | executable()
20 | }
21 | }
22 | mingwX64 {
23 | binaries {
24 | executable()
25 | }
26 | }
27 | sourceSets {
28 | getByName("commonTest") {
29 | dependencies {
30 | implementation(libs.kotlin.test)
31 | }
32 | }
33 | }
34 | }
35 |
36 | val assets = copySpec {
37 | from(rootProject.layout.projectDirectory.dir("src/main/dist"))
38 | from(rootProject.layout.projectDirectory.file("README.md"))
39 | from(rootProject.layout.projectDirectory.file("LICENSE"))
40 | }
41 |
42 | tasks.withType {
43 | compression = Compression.GZIP
44 | archiveExtension.set("tar.gz")
45 | }
46 |
47 | distributions {
48 | create("linux") {
49 | contents {
50 | from(kotlin.linuxX64().binaries.getExecutable(NativeBuildType.RELEASE).linkTaskProvider)
51 | with(assets)
52 | }
53 | }
54 | create("macos") {
55 | contents {
56 | from(kotlin.macosX64().binaries.getExecutable(NativeBuildType.RELEASE).linkTaskProvider)
57 | with(assets)
58 | }
59 | }
60 | create("windows") {
61 | contents {
62 | from(kotlin.mingwX64().binaries.getExecutable(NativeBuildType.RELEASE).linkTaskProvider)
63 | with(assets)
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/Main.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | import com.fluxchess.pulse.kotlin.Perft
9 | import com.fluxchess.pulse.kotlin.Pulse
10 | import com.fluxchess.pulse.kotlin.uci.DefaultReceiver
11 | import com.fluxchess.pulse.kotlin.uci.DefaultSender
12 | import com.fluxchess.pulse.kotlin.uci.StandardInputReader
13 | import com.fluxchess.pulse.kotlin.uci.StandardOutputWriter
14 | import kotlin.system.exitProcess
15 |
16 | fun main(args: Array) = when {
17 | args.isEmpty() -> {
18 | val sender = DefaultSender(StandardOutputWriter())
19 | val engine = Pulse(sender)
20 | val receiver = DefaultReceiver(StandardInputReader(), sender, engine)
21 | receiver.run()
22 | }
23 |
24 | args.size == 1 && "perft".equals(args[0], ignoreCase = true) -> {
25 | Perft().run()
26 | }
27 |
28 | else -> {
29 | printUsage()
30 | exitProcess(1)
31 | }
32 | }
33 |
34 | private fun printUsage() {
35 | println("Usage: pulse-kotlin [perft]")
36 | }
37 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/Perft.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin
9 |
10 | class Perft {
11 | fun run() {
12 | println("Perft running")
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/Pulse.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2023 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin
9 |
10 | import com.fluxchess.pulse.kotlin.engine.Position
11 | import com.fluxchess.pulse.kotlin.uci.Engine
12 | import com.fluxchess.pulse.kotlin.uci.Sender
13 |
14 | class Pulse(
15 | private val sender: Sender,
16 | ) : Engine {
17 | override fun initialize() {
18 | stop()
19 | sender.id("Pulse Kotlin 2.0.0", "Phokham Nonava")
20 | sender.ok()
21 | }
22 |
23 | override fun ready() {
24 | sender.readyOk()
25 | }
26 |
27 | override fun setOption(name: String) {
28 | TODO("Not yet implemented")
29 | }
30 |
31 | override fun setOption(name: String, value: String) {
32 | TODO("Not yet implemented")
33 | }
34 |
35 | override fun newGame() {
36 | TODO("Not yet implemented")
37 | }
38 |
39 | override fun position(position: Position) {
40 | TODO("Not yet implemented")
41 | }
42 |
43 | override fun start() {
44 | TODO("Not yet implemented")
45 | }
46 |
47 | override fun stop() {
48 | TODO("Not yet implemented")
49 | }
50 |
51 | override fun ponderHit() {
52 | TODO("Not yet implemented")
53 | }
54 |
55 | override fun quit() {
56 | TODO("Not yet implemented")
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/engine/Attack.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2025 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | internal fun Position.isCheck(): Boolean {
11 | return isAttacked(next(pieces[activeColor][KING]), oppositeOf(activeColor))
12 | }
13 |
14 | internal fun Position.isCheck(color: Color): Boolean {
15 | return isAttacked(next(pieces[color][KING]), oppositeOf(color))
16 | }
17 |
18 | internal fun Position.isAttacked(targetSquare: Square, attackerColor: Color): Boolean =
19 | isAttackedByPawn(targetSquare, attackerColor)
20 | || isAttackedByNonSlidingPiece(targetSquare, pieceOf(attackerColor, KNIGHT), knightDirections)
21 | || isAttackedBySlidingPiece(
22 | targetSquare,
23 | pieceOf(attackerColor, BISHOP),
24 | pieceOf(attackerColor, QUEEN),
25 | bishopDirections,
26 | )
27 | || isAttackedBySlidingPiece(
28 | targetSquare,
29 | pieceOf(attackerColor, ROOK),
30 | pieceOf(attackerColor, QUEEN),
31 | rookDirections,
32 | )
33 | || isAttackedByNonSlidingPiece(targetSquare, pieceOf(attackerColor, KING), kingDirections)
34 |
35 | private fun Position.isAttackedByPawn(targetSquare: Square, attackerColor: Color): Boolean {
36 | val attackerPawn = pieceOf(attackerColor, PAWN)
37 | for (direction in pawnCapturingDirections[attackerColor]) {
38 | val attackerSquare = targetSquare - direction
39 | if (isValidSquare(attackerSquare) && board[attackerSquare] == attackerPawn) {
40 | return true
41 | }
42 | }
43 | return false
44 | }
45 |
46 | private fun Position.isAttackedByNonSlidingPiece(
47 | targetSquare: Square,
48 | attackerPiece: Piece,
49 | directions: Array,
50 | ): Boolean {
51 | for (direction in directions) {
52 | val attackerSquare = targetSquare + direction
53 | if (isValidSquare(attackerSquare) && board[attackerSquare] == attackerPiece) {
54 | return true
55 | }
56 | }
57 | return false
58 | }
59 |
60 | private fun Position.isAttackedBySlidingPiece(
61 | targetSquare: Square,
62 | attackerPiece: Piece,
63 | attackerQueen: Piece,
64 | directions: Array,
65 | ): Boolean {
66 | for (direction in directions) {
67 | var attackerSquare = targetSquare + direction
68 | while (isValidSquare(attackerSquare)) {
69 | val piece = board[attackerSquare]
70 | if (piece != NO_PIECE) {
71 | if (piece == attackerPiece || piece == attackerQueen) {
72 | return true
73 | }
74 | break
75 | } else {
76 | attackerSquare += direction
77 | }
78 | }
79 | }
80 | return false
81 | }
82 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/engine/Bitboard.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | internal typealias Bitboard = ULong
11 | internal typealias BitboardArray = ULongArray
12 | internal typealias BitSquare = Int
13 |
14 | internal inline fun BitboardArray(size: Int, init: (Int) -> Bitboard): BitboardArray =
15 | ULongArray(size) { index -> init(index) }
16 |
17 | internal fun addSquare(square: Square, bitboard: Bitboard): Bitboard =
18 | bitboard or (1uL shl toBitSquare(square))
19 |
20 | internal fun removeSquare(square: Square, bitboard: Bitboard): Bitboard =
21 | bitboard and (1uL shl toBitSquare(square)).inv()
22 |
23 | internal fun next(bitboard: Bitboard): Square =
24 | toX88Square(bitboard.countTrailingZeroBits())
25 |
26 | internal fun remainder(bitboard: Bitboard): Bitboard =
27 | bitboard and (bitboard - 1u)
28 |
29 | private fun toBitSquare(square: Square): BitSquare =
30 | ((square and 7.inv()) shr 1) or (square and 7)
31 |
32 | private fun toX88Square(square: BitSquare): Square =
33 | ((square and 7.inv()) shl 1) or (square and 7)
34 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/engine/Castling.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | typealias Castling = Int
11 |
12 | const val WHITE_KINGSIDE: Castling = 1 // 1 shl 0
13 | const val WHITE_QUEENSIDE: Castling = 1 shl 1
14 | const val BLACK_KINGSIDE: Castling = 1 shl 2
15 | const val BLACK_QUEENSIDE: Castling = 1 shl 3
16 |
17 | const val NO_CASTLING: Castling = 0
18 |
19 | val castlings = intArrayOf(
20 | WHITE_KINGSIDE, WHITE_QUEENSIDE,
21 | BLACK_KINGSIDE, BLACK_QUEENSIDE,
22 | )
23 |
24 | fun isValidCastling(castling: Castling): Boolean = when (castling) {
25 | WHITE_KINGSIDE, WHITE_QUEENSIDE, BLACK_KINGSIDE, BLACK_QUEENSIDE -> true
26 | else -> false
27 | }
28 |
29 | fun castlingOf(color: Color, castlingType: CastlingType): Castling = when (color) {
30 | WHITE -> when (castlingType) {
31 | KINGSIDE -> WHITE_KINGSIDE
32 | QUEENSIDE -> WHITE_QUEENSIDE
33 | else -> error("Invalid castling type: $castlingType")
34 | }
35 |
36 | BLACK -> when (castlingType) {
37 | KINGSIDE -> BLACK_KINGSIDE
38 | QUEENSIDE -> BLACK_QUEENSIDE
39 | else -> error("Invalid castling type: $castlingType")
40 | }
41 |
42 | else -> error("Invalid color: $color")
43 | }
44 |
45 | fun castlingColorOf(castling: Castling): Color = when (castling) {
46 | WHITE_KINGSIDE, WHITE_QUEENSIDE -> WHITE
47 | BLACK_KINGSIDE, BLACK_QUEENSIDE -> BLACK
48 | else -> error("Invalid castling: $castling")
49 | }
50 |
51 | fun castlingTypeOf(castling: Castling): Color = when (castling) {
52 | WHITE_KINGSIDE, BLACK_KINGSIDE -> KINGSIDE
53 | WHITE_QUEENSIDE, BLACK_QUEENSIDE -> QUEENSIDE
54 | else -> error("Invalid castling: $castling")
55 | }
56 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/engine/CastlingType.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | typealias CastlingType = Int
11 |
12 | const val KINGSIDE: CastlingType = 0
13 | const val QUEENSIDE: CastlingType = 1
14 |
15 | const val NO_CASTLING_TYPE: CastlingType = 2
16 |
17 | val castlingTypes = intArrayOf(KINGSIDE, QUEENSIDE)
18 |
19 | fun isValidCastlingType(castlingType: CastlingType): Boolean = when (castlingType) {
20 | KINGSIDE, QUEENSIDE -> true
21 | else -> false
22 | }
23 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/engine/Color.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | typealias Color = Int
11 |
12 | const val WHITE: Color = 0
13 | const val BLACK: Color = 1
14 |
15 | const val NO_COLOR: Color = 2
16 |
17 | val colors = intArrayOf(WHITE, BLACK)
18 |
19 | fun isValidColor(color: Color) = when (color) {
20 | WHITE, BLACK -> true
21 | else -> false
22 | }
23 |
24 | fun oppositeOf(color: Color): Color =
25 | when (color) {
26 | WHITE -> BLACK
27 | BLACK -> WHITE
28 | else -> error("Invalid color: $color")
29 | }
30 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/engine/Depth.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | typealias Depth = Int
11 |
12 | const val MAX_PLY: Depth = 256
13 | const val MAX_DEPTH: Depth = 64
14 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/engine/Direction.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | internal typealias Direction = Int
11 |
12 | internal const val NORTH: Direction = 16
13 | internal const val EAST: Direction = 1
14 | internal const val SOUTH: Direction = -16
15 | internal const val WEST: Direction = -1
16 | internal const val NORTH_EAST: Direction = NORTH + EAST
17 | internal const val SOUTH_EAST: Direction = SOUTH + EAST
18 | internal const val SOUTH_WEST: Direction = SOUTH + WEST
19 | internal const val NORTH_WEST: Direction = NORTH + WEST
20 |
21 | internal val pawnMoveDirections: Array = arrayOf(
22 | NORTH, // WHITE
23 | SOUTH, // BLACK
24 | )
25 |
26 | internal val pawnCapturingDirections: Array> = arrayOf(
27 | arrayOf(NORTH_EAST, NORTH_WEST), // WHITE
28 | arrayOf(SOUTH_EAST, SOUTH_WEST), // BLACK
29 | )
30 |
31 | internal val knightDirections: Array = arrayOf(
32 | NORTH + NORTH + EAST,
33 | NORTH + NORTH + WEST,
34 | NORTH + EAST + EAST,
35 | NORTH + WEST + WEST,
36 | SOUTH + SOUTH + EAST,
37 | SOUTH + SOUTH + WEST,
38 | SOUTH + EAST + EAST,
39 | SOUTH + WEST + WEST,
40 | )
41 |
42 | internal val bishopDirections: Array = arrayOf(
43 | NORTH_EAST, NORTH_WEST, SOUTH_EAST, SOUTH_WEST,
44 | )
45 |
46 | internal val rookDirections: Array = arrayOf(
47 | NORTH, EAST, SOUTH, WEST,
48 | )
49 |
50 | internal val queenDirections: Array = arrayOf(
51 | NORTH, EAST, SOUTH, WEST,
52 | NORTH_EAST, NORTH_WEST, SOUTH_EAST, SOUTH_WEST,
53 | )
54 |
55 | internal val kingDirections: Array = arrayOf(
56 | NORTH, EAST, SOUTH, WEST,
57 | NORTH_EAST, NORTH_WEST, SOUTH_EAST, SOUTH_WEST,
58 | )
59 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/engine/File.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | typealias File = Int
11 |
12 | const val FILE_A: File = 0
13 | const val FILE_B: File = 1
14 | const val FILE_C: File = 2
15 | const val FILE_D: File = 3
16 | const val FILE_E: File = 4
17 | const val FILE_F: File = 5
18 | const val FILE_G: File = 6
19 | const val FILE_H: File = 7
20 |
21 | const val NO_FILE: File = 8
22 |
23 | val files = intArrayOf(FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H)
24 |
25 | fun isValidFile(file: File): Boolean = when (file) {
26 | FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H -> true
27 | else -> false
28 | }
29 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/engine/Move.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | typealias Move = Int
11 |
12 | // 0 - 2: move type (required)
13 | // 3 - 9: origin square (required)
14 | // 10 - 16: target square (required)
15 | // 17 - 21: origin piece (required)
16 | // 22 - 26: target piece (optional)
17 | // 27 - 29: promotion (optional)
18 | internal const val MOVE_TYPE_SHIFT = 0
19 | internal const val MOVE_TYPE_MASK = 0x7 shl MOVE_TYPE_SHIFT
20 | internal const val ORIGIN_SQUARE_SHIFT = 3
21 | internal const val ORIGIN_SQUARE_MASK = 0x7F shl ORIGIN_SQUARE_SHIFT
22 | internal const val TARGET_SQUARE_SHIFT = 10
23 | internal const val TARGET_SQUARE_MASK = 0x7F shl TARGET_SQUARE_SHIFT
24 | internal const val ORIGIN_PIECE_SHIFT = 17
25 | internal const val ORIGIN_PIECE_MASK = 0x1F shl ORIGIN_PIECE_SHIFT
26 | internal const val TARGET_PIECE_SHIFT = 22
27 | internal const val TARGET_PIECE_MASK = 0x1F shl TARGET_PIECE_SHIFT
28 | internal const val PROMOTION_SHIFT = 27
29 | internal const val PROMOTION_MASK = 0x7 shl PROMOTION_SHIFT
30 |
31 | const val NO_MOVE: Move = (NO_MOVE_TYPE shl MOVE_TYPE_SHIFT) or
32 | (NO_SQUARE shl ORIGIN_SQUARE_SHIFT) or
33 | (NO_SQUARE shl TARGET_SQUARE_SHIFT) or
34 | (NO_PIECE shl ORIGIN_PIECE_SHIFT) or
35 | (NO_PIECE shl TARGET_PIECE_SHIFT) or
36 | (NO_PIECE_TYPE shl PROMOTION_SHIFT)
37 |
38 | fun moveOf(
39 | moveType: MoveType,
40 | originSquare: Square,
41 | targetSquare: Square,
42 | originPiece: Piece,
43 | targetPiece: Piece,
44 | promotion: PieceType,
45 | ): Move = (moveType shl MOVE_TYPE_SHIFT) or
46 | (originSquare shl ORIGIN_SQUARE_SHIFT) or
47 | (targetSquare shl TARGET_SQUARE_SHIFT) or
48 | (originPiece shl ORIGIN_PIECE_SHIFT) or
49 | (targetPiece shl TARGET_PIECE_SHIFT) or
50 | (promotion shl PROMOTION_SHIFT)
51 |
52 | fun moveTypeOf(move: Move): MoveType = (move and MOVE_TYPE_MASK) ushr MOVE_TYPE_SHIFT
53 |
54 | fun originSquareOf(move: Move): Square = (move and ORIGIN_SQUARE_MASK) ushr ORIGIN_SQUARE_SHIFT
55 |
56 | fun targetSquareOf(move: Move): Square = (move and TARGET_SQUARE_MASK) ushr TARGET_SQUARE_SHIFT
57 |
58 | fun originPieceOf(move: Move): Piece = (move and ORIGIN_PIECE_MASK) ushr ORIGIN_PIECE_SHIFT
59 |
60 | fun targetPieceOf(move: Move): Piece = (move and TARGET_PIECE_MASK) ushr TARGET_PIECE_SHIFT
61 |
62 | fun promotionOf(move: Move): PieceType = (move and PROMOTION_MASK) ushr PROMOTION_SHIFT
63 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/engine/MoveList.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | class MoveList {
11 | var size: Int = 0
12 | val entries: Array = Array(MAX_PLY) { MoveEntry() }
13 |
14 | internal fun reset() {
15 | size = 0
16 | }
17 |
18 | internal fun add(move: Move) {
19 | entries[size++].move = move
20 | }
21 |
22 | internal fun rateByMVVLVA() {
23 | for (i in 0 until size) {
24 | val move = entries[i].move
25 | var value = 0
26 |
27 | val pieceTypeValue = pieceTypeValueOf(pieceTypeOf(originPieceOf(move)))
28 | value += KING_VALUE / pieceTypeValue
29 |
30 | val targetPiece = targetPieceOf(move)
31 | if (targetPiece != NO_PIECE) {
32 | value += 10 * pieceTypeValueOf(pieceTypeOf(targetPiece))
33 | }
34 |
35 | entries[i].value = value
36 | }
37 | }
38 |
39 | internal fun sort() {
40 | for (i in 0 until size) {
41 | val entry = entries[i]
42 |
43 | var j = i
44 | while (j > 0 && entries[j - 1].value < entry.value) {
45 | entries[j] = entries[j - 1]
46 | j--
47 | }
48 |
49 | entries[j] = entry
50 | }
51 | }
52 | }
53 |
54 | class MoveEntry {
55 | var move: Move = NO_MOVE
56 | internal var value: Value = NO_VALUE
57 | }
58 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/engine/MoveType.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | typealias MoveType = Int
11 |
12 | const val NORMAL_MOVE: MoveType = 0
13 | const val PAWN_DOUBLE_MOVE: MoveType = 1
14 | const val PAWN_PROMOTION_MOVE: MoveType = 2
15 | const val EN_PASSANT_MOVE: MoveType = 3
16 | const val CASTLING_MOVE: MoveType = 4
17 |
18 | const val NO_MOVE_TYPE: MoveType = 5
19 |
20 | val moveTypes = intArrayOf(NORMAL_MOVE, PAWN_DOUBLE_MOVE, PAWN_PROMOTION_MOVE, EN_PASSANT_MOVE, CASTLING_MOVE)
21 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/engine/Piece.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | typealias Piece = Int
11 |
12 | const val WHITE_PAWN: Piece = 0
13 | const val WHITE_KNIGHT: Piece = 1
14 | const val WHITE_BISHOP: Piece = 2
15 | const val WHITE_ROOK: Piece = 3
16 | const val WHITE_QUEEN: Piece = 4
17 | const val WHITE_KING: Piece = 5
18 | const val BLACK_PAWN: Piece = 6
19 | const val BLACK_KNIGHT: Piece = 7
20 | const val BLACK_BISHOP: Piece = 8
21 | const val BLACK_ROOK: Piece = 9
22 | const val BLACK_QUEEN: Piece = 10
23 | const val BLACK_KING: Piece = 11
24 |
25 | const val NO_PIECE: Piece = 12
26 |
27 | val pieces = intArrayOf(
28 | WHITE_PAWN, WHITE_KNIGHT, WHITE_BISHOP, WHITE_ROOK, WHITE_QUEEN, WHITE_KING,
29 | BLACK_PAWN, BLACK_KNIGHT, BLACK_BISHOP, BLACK_ROOK, BLACK_QUEEN, BLACK_KING,
30 | )
31 |
32 | fun isValidPiece(piece: Piece): Boolean = when (piece) {
33 | WHITE_PAWN, WHITE_KNIGHT, WHITE_BISHOP, WHITE_ROOK, WHITE_QUEEN, WHITE_KING,
34 | BLACK_PAWN, BLACK_KNIGHT, BLACK_BISHOP, BLACK_ROOK, BLACK_QUEEN, BLACK_KING,
35 | -> true
36 |
37 | else -> false
38 | }
39 |
40 | fun pieceOf(color: Color, pieceType: PieceType): Piece = when (color) {
41 | WHITE -> when (pieceType) {
42 | PAWN -> WHITE_PAWN
43 | KNIGHT -> WHITE_KNIGHT
44 | BISHOP -> WHITE_BISHOP
45 | ROOK -> WHITE_ROOK
46 | QUEEN -> WHITE_QUEEN
47 | KING -> WHITE_KING
48 | else -> error("Invalid piece type: $pieceType")
49 | }
50 |
51 | BLACK -> when (pieceType) {
52 | PAWN -> BLACK_PAWN
53 | KNIGHT -> BLACK_KNIGHT
54 | BISHOP -> BLACK_BISHOP
55 | ROOK -> BLACK_ROOK
56 | QUEEN -> BLACK_QUEEN
57 | KING -> BLACK_KING
58 | else -> error("Invalid piece type: $pieceType")
59 | }
60 |
61 | else -> error("Invalid color: $color")
62 | }
63 |
64 | fun pieceColorOf(piece: Piece): Color = when (piece) {
65 | WHITE_PAWN, WHITE_KNIGHT, WHITE_BISHOP, WHITE_ROOK, WHITE_QUEEN, WHITE_KING -> WHITE
66 | BLACK_PAWN, BLACK_KNIGHT, BLACK_BISHOP, BLACK_ROOK, BLACK_QUEEN, BLACK_KING -> BLACK
67 | else -> error("Invalid piece: $piece")
68 | }
69 |
70 | fun pieceTypeOf(piece: Piece): PieceType = when (piece) {
71 | WHITE_PAWN, BLACK_PAWN -> PAWN
72 | WHITE_KNIGHT, BLACK_KNIGHT -> KNIGHT
73 | WHITE_BISHOP, BLACK_BISHOP -> BISHOP
74 | WHITE_ROOK, BLACK_ROOK -> ROOK
75 | WHITE_QUEEN, BLACK_QUEEN -> QUEEN
76 | WHITE_KING, BLACK_KING -> KING
77 | else -> error("Invalid piece: $piece")
78 | }
79 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/engine/PieceType.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | typealias PieceType = Int
11 |
12 | const val PAWN: PieceType = 0
13 | const val KNIGHT: PieceType = 1
14 | const val BISHOP: PieceType = 2
15 | const val ROOK: PieceType = 3
16 | const val QUEEN: PieceType = 4
17 | const val KING: PieceType = 5
18 |
19 | const val NO_PIECE_TYPE: PieceType = 6
20 |
21 | val pieceTypes = intArrayOf(PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING)
22 |
23 | fun isValidPieceType(pieceType: PieceType): Boolean = when (pieceType) {
24 | PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING -> true
25 | else -> false
26 | }
27 |
28 | internal fun isSliding(pieceType: PieceType): Boolean = when (pieceType) {
29 | BISHOP, ROOK, QUEEN -> true
30 | PAWN, KNIGHT, KING -> false
31 | else -> error("Invalid piece type: $pieceType")
32 | }
33 |
34 | internal fun pieceTypeValueOf(pieceType: PieceType): Value = when (pieceType) {
35 | PAWN -> PAWN_VALUE
36 | KNIGHT -> KNIGHT_VALUE
37 | BISHOP -> BISHOP_VALUE
38 | ROOK -> ROOK_VALUE
39 | QUEEN -> QUEEN_VALUE
40 | KING -> KING_VALUE
41 | else -> error("Invalid piece type: $pieceType")
42 | }
43 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/engine/Rank.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | typealias Rank = Int
11 |
12 | const val RANK_1: Rank = 0
13 | const val RANK_2: Rank = 1
14 | const val RANK_3: Rank = 2
15 | const val RANK_4: Rank = 3
16 | const val RANK_5: Rank = 4
17 | const val RANK_6: Rank = 5
18 | const val RANK_7: Rank = 6
19 | const val RANK_8: Rank = 7
20 |
21 | const val NO_RANK: Rank = 8
22 |
23 | val ranks = intArrayOf(RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8)
24 |
25 | fun isValidRank(rank: Rank): Boolean = when (rank) {
26 | RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8 -> true
27 | else -> false
28 | }
29 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/engine/Square.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | typealias Square = Int
11 |
12 | const val A1: Square = 0
13 | const val B1: Square = 1
14 | const val C1: Square = 2
15 | const val D1: Square = 3
16 | const val E1: Square = 4
17 | const val F1: Square = 5
18 | const val G1: Square = 6
19 | const val H1: Square = 7
20 |
21 | const val A2: Square = 16
22 | const val B2: Square = 17
23 | const val C2: Square = 18
24 | const val D2: Square = 19
25 | const val E2: Square = 20
26 | const val F2: Square = 21
27 | const val G2: Square = 22
28 | const val H2: Square = 23
29 |
30 | const val A3: Square = 32
31 | const val B3: Square = 33
32 | const val C3: Square = 34
33 | const val D3: Square = 35
34 | const val E3: Square = 36
35 | const val F3: Square = 37
36 | const val G3: Square = 38
37 | const val H3: Square = 39
38 |
39 | const val A4: Square = 48
40 | const val B4: Square = 49
41 | const val C4: Square = 50
42 | const val D4: Square = 51
43 | const val E4: Square = 52
44 | const val F4: Square = 53
45 | const val G4: Square = 54
46 | const val H4: Square = 55
47 |
48 | const val A5: Square = 64
49 | const val B5: Square = 65
50 | const val C5: Square = 66
51 | const val D5: Square = 67
52 | const val E5: Square = 68
53 | const val F5: Square = 69
54 | const val G5: Square = 70
55 | const val H5: Square = 71
56 |
57 | const val A6: Square = 80
58 | const val B6: Square = 81
59 | const val C6: Square = 82
60 | const val D6: Square = 83
61 | const val E6: Square = 84
62 | const val F6: Square = 85
63 | const val G6: Square = 86
64 | const val H6: Square = 87
65 |
66 | const val A7: Square = 96
67 | const val B7: Square = 97
68 | const val C7: Square = 98
69 | const val D7: Square = 99
70 | const val E7: Square = 100
71 | const val F7: Square = 101
72 | const val G7: Square = 102
73 | const val H7: Square = 103
74 |
75 | const val A8: Square = 112
76 | const val B8: Square = 113
77 | const val C8: Square = 114
78 | const val D8: Square = 115
79 | const val E8: Square = 116
80 | const val F8: Square = 117
81 | const val G8: Square = 118
82 | const val H8: Square = 119
83 |
84 | const val NO_SQUARE: Square = 127
85 |
86 | internal const val SQUARES_MAX_VALUE: Square = 128
87 |
88 | val squares = intArrayOf(
89 | A1, B1, C1, D1, E1, F1, G1, H1,
90 | A2, B2, C2, D2, E2, F2, G2, H2,
91 | A3, B3, C3, D3, E3, F3, G3, H3,
92 | A4, B4, C4, D4, E4, F4, G4, H4,
93 | A5, B5, C5, D5, E5, F5, G5, H5,
94 | A6, B6, C6, D6, E6, F6, G6, H6,
95 | A7, B7, C7, D7, E7, F7, G7, H7,
96 | A8, B8, C8, D8, E8, F8, G8, H8,
97 | )
98 |
99 | fun isValidSquare(square: Square): Boolean = (square and 0x88) == 0
100 |
101 | fun squareOf(file: File, rank: Rank): Square = (rank shl 4) + file
102 |
103 | fun fileOf(square: Square): File = square and 0xF
104 |
105 | fun rankOf(square: Square): Rank = square ushr 4
106 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/engine/Value.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | internal typealias Value = Int
11 |
12 | // Piece values as defined by Larry Kaufman
13 | internal const val PAWN_VALUE: Value = 100
14 | internal const val KNIGHT_VALUE: Value = 325
15 | internal const val BISHOP_VALUE: Value = 325
16 | internal const val ROOK_VALUE: Value = 500
17 | internal const val QUEEN_VALUE: Value = 975
18 | internal const val KING_VALUE: Value = 20000
19 |
20 | internal const val INFINITE: Value = 200000
21 | internal const val CHECKMATE: Value = 100000
22 | internal const val CHECKMATE_THRESHOLD: Value = CHECKMATE - MAX_PLY
23 | internal const val DRAW: Value = 0
24 |
25 | internal const val NO_VALUE: Value = 300000
26 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/uci/Engine.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.uci
9 |
10 | import com.fluxchess.pulse.kotlin.engine.Position
11 |
12 | interface Engine {
13 | fun initialize()
14 | fun ready()
15 | fun setOption(name: String)
16 | fun setOption(name: String, value: String)
17 | fun newGame()
18 | fun position(position: Position)
19 | fun start()
20 | fun stop()
21 | fun ponderHit()
22 | fun quit()
23 | }
24 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/uci/Reader.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.uci
9 |
10 | interface Reader {
11 | fun readln(): String?
12 | }
13 |
14 | class StandardInputReader : Reader {
15 | override fun readln(): String? {
16 | return readlnOrNull()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/uci/Sender.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.uci
9 |
10 | interface Sender {
11 | fun id(name: String, author: String)
12 | fun ok()
13 | fun readyOk()
14 | fun debug(message: String)
15 | }
16 |
17 | class DefaultSender(
18 | private val writer: Writer,
19 | ) : Sender {
20 | var debugMode: Boolean = false
21 |
22 | override fun id(name: String, author: String) {
23 | writer.writeln("id name $name")
24 | writer.writeln("id author $author")
25 | }
26 |
27 | override fun ok() {
28 | writer.writeln("uciok")
29 | }
30 |
31 | override fun readyOk() {
32 | writer.writeln("readyok")
33 | }
34 |
35 | override fun debug(message: String) {
36 | if (debugMode) {
37 | writer.writeln("info string $message")
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonMain/kotlin/com/fluxchess/pulse/kotlin/uci/Writer.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.uci
9 |
10 | interface Writer {
11 | fun writeln(s: String)
12 | }
13 |
14 | class StandardOutputWriter : Writer {
15 | override fun writeln(s: String) {
16 | println(s)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonTest/kotlin/com/fluxchess/pulse/kotlin/engine/BitboardTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | import kotlin.test.Test
11 | import kotlin.test.assertEquals
12 |
13 | class BitboardTest {
14 | @Test
15 | fun `It should add all squares`() {
16 | var bitboard: Bitboard = 0uL
17 | shuffledSquares().forEach {
18 | bitboard = addSquare(it, bitboard)
19 | }
20 | assertEquals(ULong.MAX_VALUE, bitboard)
21 | }
22 |
23 | @Test
24 | fun `It should remove all squares`() {
25 | var bitboard: Bitboard = ULong.MAX_VALUE
26 | shuffledSquares().forEach {
27 | bitboard = removeSquare(it, bitboard)
28 | }
29 | assertEquals(0uL, bitboard)
30 | }
31 |
32 | @Test
33 | fun `It should return the next square`() {
34 | val bitboard: Bitboard = addSquare(A6, 0uL)
35 | val square = next(bitboard)
36 | assertEquals(A6, square)
37 | }
38 |
39 | @Test
40 | fun `It should return the remainder`() {
41 | val bitboard: Bitboard = 0b1110100uL
42 | val remainder = remainder(bitboard)
43 | assertEquals(0b1110000uL, remainder)
44 | }
45 | }
46 |
47 | private fun shuffledSquares(): List {
48 | return squares.indices.shuffled().map { squares[it] }
49 | }
50 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonTest/kotlin/com/fluxchess/pulse/kotlin/engine/CastlingTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | import kotlin.test.Test
11 | import kotlin.test.assertEquals
12 | import kotlin.test.assertFalse
13 | import kotlin.test.assertTrue
14 |
15 | class CastlingTest {
16 | @Test
17 | fun `isValidCastling should return true if castling is valid false otherwise`() {
18 | assertTrue(isValidCastling(WHITE_KINGSIDE))
19 | assertTrue(isValidCastling(WHITE_QUEENSIDE))
20 | assertTrue(isValidCastling(BLACK_KINGSIDE))
21 | assertTrue(isValidCastling(BLACK_QUEENSIDE))
22 |
23 | assertFalse(isValidCastling(NO_CASTLING))
24 | }
25 |
26 | @Test
27 | fun `castlingOf should return the castling`() {
28 | assertEquals(WHITE_KINGSIDE, castlingOf(WHITE, KINGSIDE))
29 | assertEquals(WHITE_QUEENSIDE, castlingOf(WHITE, QUEENSIDE))
30 | assertEquals(BLACK_KINGSIDE, castlingOf(BLACK, KINGSIDE))
31 | assertEquals(BLACK_QUEENSIDE, castlingOf(BLACK, QUEENSIDE))
32 | }
33 |
34 | @Test
35 | fun `castlingColorOf should return the castling color`() {
36 | assertEquals(WHITE, castlingColorOf(WHITE_KINGSIDE))
37 | assertEquals(WHITE, castlingColorOf(WHITE_QUEENSIDE))
38 | assertEquals(BLACK, castlingColorOf(BLACK_KINGSIDE))
39 | assertEquals(BLACK, castlingColorOf(BLACK_QUEENSIDE))
40 | }
41 |
42 | @Test
43 | fun `castlingTypeOf should return the castling type`() {
44 | assertEquals(KINGSIDE, castlingTypeOf(WHITE_KINGSIDE))
45 | assertEquals(QUEENSIDE, castlingTypeOf(WHITE_QUEENSIDE))
46 | assertEquals(KINGSIDE, castlingTypeOf(BLACK_KINGSIDE))
47 | assertEquals(QUEENSIDE, castlingTypeOf(BLACK_QUEENSIDE))
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonTest/kotlin/com/fluxchess/pulse/kotlin/engine/CastlingTypeTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | import kotlin.test.Test
11 | import kotlin.test.assertEquals
12 | import kotlin.test.assertFalse
13 | import kotlin.test.assertTrue
14 |
15 | class CastlingTypeTest {
16 | @Test
17 | fun `castlingTypes array should be valid`() {
18 | for (castlingType in castlingTypes.indices) {
19 | assertEquals(castlingTypes[castlingType], castlingType)
20 | }
21 | }
22 |
23 | @Test
24 | fun `isValidCastlingType should return true if castlingType is valid false otherwise`() {
25 | assertTrue(isValidCastlingType(KINGSIDE))
26 | assertTrue(isValidCastlingType(QUEENSIDE))
27 |
28 | assertFalse(isValidCastlingType(NO_CASTLING_TYPE))
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonTest/kotlin/com/fluxchess/pulse/kotlin/engine/ColorTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | import kotlin.test.Test
11 | import kotlin.test.assertEquals
12 | import kotlin.test.assertFalse
13 | import kotlin.test.assertTrue
14 |
15 | class ColorTest {
16 | @Test
17 | fun `Colors array should be valid`() {
18 | for (color in colors.indices) {
19 | assertEquals(colors[color], color)
20 | }
21 | }
22 |
23 | @Test
24 | fun `isValidColor should return true if color is valid false otherwise`() {
25 | assertTrue(isValidColor(WHITE))
26 | assertTrue(isValidColor(BLACK))
27 |
28 | assertFalse(isValidColor(NO_COLOR))
29 | }
30 |
31 | @Test
32 | fun `oppositeOf should return the opposite color`() {
33 | assertEquals(BLACK, oppositeOf(WHITE))
34 | assertEquals(WHITE, oppositeOf(BLACK))
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonTest/kotlin/com/fluxchess/pulse/kotlin/engine/FileTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | import kotlin.test.Test
11 | import kotlin.test.assertEquals
12 | import kotlin.test.assertFalse
13 | import kotlin.test.assertTrue
14 |
15 | class FileTest {
16 | @Test
17 | fun `files array should be valid`() {
18 | for (file in files.indices) {
19 | assertEquals(files[file], file)
20 | }
21 | }
22 |
23 | @Test
24 | fun `isValidFile should return true if file is valid false otherwise`() {
25 | assertTrue(isValidFile(FILE_A))
26 | assertTrue(isValidFile(FILE_B))
27 | assertTrue(isValidFile(FILE_C))
28 | assertTrue(isValidFile(FILE_D))
29 | assertTrue(isValidFile(FILE_E))
30 | assertTrue(isValidFile(FILE_F))
31 | assertTrue(isValidFile(FILE_G))
32 | assertTrue(isValidFile(FILE_H))
33 |
34 | assertFalse(isValidFile(NO_FILE))
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonTest/kotlin/com/fluxchess/pulse/kotlin/engine/MoveListTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | import kotlin.test.Test
11 | import kotlin.test.assertEquals
12 |
13 | class MoveListTest {
14 |
15 | @Test
16 | fun `add should add a move to the move list`() {
17 | val moveList = MoveList()
18 | val move = moveOf(PAWN_PROMOTION_MOVE, A7, B8, WHITE_PAWN, BLACK_QUEEN, KNIGHT)
19 |
20 | moveList.add(move)
21 |
22 | assertEquals(1, moveList.size)
23 | assertEquals(move, moveList.entries[0].move)
24 | }
25 |
26 | @Test
27 | fun `rateByMVVLVA should rate a non-capturing move`() {
28 | val moveList = MoveList()
29 | moveList.add(moveOf(NORMAL_MOVE, D4, D5, WHITE_PAWN, NO_PIECE, NO_PIECE_TYPE))
30 |
31 | moveList.rateByMVVLVA()
32 |
33 | assertEquals(KING_VALUE / PAWN_VALUE, moveList.entries[0].value)
34 | }
35 |
36 | @Test
37 | fun `rateByMVVLVA should rate a capturing move`() {
38 | val moveList = MoveList()
39 | moveList.add(moveOf(NORMAL_MOVE, D1, G4, WHITE_QUEEN, BLACK_KNIGHT, NO_PIECE_TYPE))
40 |
41 | moveList.rateByMVVLVA()
42 |
43 | assertEquals(KING_VALUE / QUEEN_VALUE + 10 * KNIGHT_VALUE, moveList.entries[0].value)
44 | }
45 |
46 | @Test
47 | fun `sort should sort all moves`() {
48 | val moveList = MoveList()
49 | val move1 = moveOf(NORMAL_MOVE, D1, G4, WHITE_QUEEN, BLACK_KNIGHT, NO_PIECE_TYPE)
50 | val move2 = moveOf(NORMAL_MOVE, C1, G5, WHITE_BISHOP, BLACK_PAWN, NO_PIECE_TYPE)
51 | val move3 = moveOf(NORMAL_MOVE, F1, B5, WHITE_BISHOP, NO_PIECE, NO_PIECE_TYPE)
52 | val move4 = moveOf(NORMAL_MOVE, D4, D5, WHITE_PAWN, NO_PIECE, NO_PIECE_TYPE)
53 | moveList.add(move4)
54 | moveList.entries[moveList.size - 1].value = 1
55 | moveList.add(move3)
56 | moveList.entries[moveList.size - 1].value = 2
57 | moveList.add(move2)
58 | moveList.entries[moveList.size - 1].value = 3
59 | moveList.add(move1)
60 | moveList.entries[moveList.size - 1].value = 4
61 |
62 | moveList.sort()
63 |
64 | assertEquals(move1, moveList.entries[0].move)
65 | assertEquals(move2, moveList.entries[1].move)
66 | assertEquals(move3, moveList.entries[2].move)
67 | assertEquals(move4, moveList.entries[3].move)
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonTest/kotlin/com/fluxchess/pulse/kotlin/engine/MoveTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | import kotlin.test.Test
11 | import kotlin.test.assertEquals
12 |
13 | class MoveTest {
14 | @Test
15 | fun `moveOf should return the move`() {
16 | val move = moveOf(PAWN_PROMOTION_MOVE, A7, B8, WHITE_PAWN, BLACK_QUEEN, KNIGHT)
17 |
18 | assertEquals(PAWN_PROMOTION_MOVE, moveTypeOf(move))
19 | assertEquals(A7, originSquareOf(move))
20 | assertEquals(B8, targetSquareOf(move))
21 | assertEquals(WHITE_PAWN, originPieceOf(move))
22 | assertEquals(BLACK_QUEEN, targetPieceOf(move))
23 | assertEquals(KNIGHT, promotionOf(move))
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonTest/kotlin/com/fluxchess/pulse/kotlin/engine/MoveTypeTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | import kotlin.test.Test
11 | import kotlin.test.assertEquals
12 |
13 | class MoveTypeTest {
14 | @Test
15 | fun `MoveTypes array should be valid`() {
16 | for (moveType in moveTypes.indices) {
17 | assertEquals(moveTypes[moveType], moveType)
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonTest/kotlin/com/fluxchess/pulse/kotlin/engine/PieceTypeTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | import kotlin.test.Test
11 | import kotlin.test.assertEquals
12 | import kotlin.test.assertFalse
13 | import kotlin.test.assertTrue
14 |
15 | class PieceTypeTest {
16 | @Test
17 | fun `PieceTypes array should be valid`() {
18 | for (pieceType in pieceTypes.indices) {
19 | assertEquals(pieceTypes[pieceType], pieceType)
20 | }
21 | }
22 |
23 | @Test
24 | fun `isValidPieceType should return true if pieceType is valid false otherwise`() {
25 | assertTrue(isValidPieceType(PAWN))
26 | assertTrue(isValidPieceType(KNIGHT))
27 | assertTrue(isValidPieceType(BISHOP))
28 | assertTrue(isValidPieceType(ROOK))
29 | assertTrue(isValidPieceType(QUEEN))
30 | assertTrue(isValidPieceType(KING))
31 |
32 | assertFalse(isValidPieceType(NO_PIECE_TYPE))
33 | }
34 |
35 | @Test
36 | fun `isSliding should return true when the pieceType is sliding`() {
37 | assertTrue(isSliding(BISHOP))
38 | assertTrue(isSliding(ROOK))
39 | assertTrue(isSliding(QUEEN))
40 | assertFalse(isSliding(PAWN))
41 | assertFalse(isSliding(KNIGHT))
42 | assertFalse(isSliding(KING))
43 | }
44 |
45 | @Test
46 | fun `pieceTypeValueOf should return the piece type value`() {
47 | assertEquals(PAWN_VALUE, pieceTypeValueOf(PAWN))
48 | assertEquals(KNIGHT_VALUE, pieceTypeValueOf(KNIGHT))
49 | assertEquals(BISHOP_VALUE, pieceTypeValueOf(BISHOP))
50 | assertEquals(ROOK_VALUE, pieceTypeValueOf(ROOK))
51 | assertEquals(QUEEN_VALUE, pieceTypeValueOf(QUEEN))
52 | assertEquals(KING_VALUE, pieceTypeValueOf(KING))
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonTest/kotlin/com/fluxchess/pulse/kotlin/engine/RankTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | import kotlin.test.Test
11 | import kotlin.test.assertEquals
12 | import kotlin.test.assertFalse
13 | import kotlin.test.assertTrue
14 |
15 | class RankTest {
16 | @Test
17 | fun `ranks array should be valid`() {
18 | for (rank in ranks.indices) {
19 | assertEquals(ranks[rank], rank)
20 | }
21 | }
22 |
23 | @Test
24 | fun `isValidRank should return true if rank is valid false otherwise`() {
25 | assertTrue(isValidRank(RANK_1))
26 | assertTrue(isValidRank(RANK_2))
27 | assertTrue(isValidRank(RANK_3))
28 | assertTrue(isValidRank(RANK_4))
29 | assertTrue(isValidRank(RANK_5))
30 | assertTrue(isValidRank(RANK_6))
31 | assertTrue(isValidRank(RANK_7))
32 | assertTrue(isValidRank(RANK_8))
33 |
34 | assertFalse(isValidRank(NO_RANK))
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonTest/kotlin/com/fluxchess/pulse/kotlin/engine/SquareTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.engine
9 |
10 | import kotlin.test.Test
11 | import kotlin.test.assertEquals
12 | import kotlin.test.assertTrue
13 |
14 | class SquareTest {
15 | @Test
16 | fun `squares array should be valid`() {
17 | for (rank in ranks.indices) {
18 | for (file in files.indices) {
19 | assertEquals(squares[rank * ranks.size + file], squareOf(file, rank))
20 | assertTrue(isValidSquare(squareOf(file, rank)))
21 | }
22 | }
23 | }
24 |
25 | @Test
26 | fun `When creating a square it should save and return file and rank correctly`() {
27 | ranks.forEach { rank ->
28 | files.forEach { file ->
29 | val square = squareOf(file, rank)
30 | assertEquals(file, fileOf(square))
31 | assertEquals(rank, rankOf(square))
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/pulse-kotlin/src/commonTest/kotlin/com/fluxchess/pulse/kotlin/uci/DefaultSenderTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2024 Phokham Nonava
3 | *
4 | * Use of this source code is governed by the MIT license that can be
5 | * found in the LICENSE file.
6 | */
7 |
8 | package com.fluxchess.pulse.kotlin.uci
9 |
10 | import kotlin.test.Test
11 | import kotlin.test.assertEquals
12 |
13 | class DefaultSenderTest {
14 | @Test
15 | fun `When 'id' is called with name and author it should print a valid string`() {
16 | val testWriter = TestWriter()
17 | val sender = DefaultSender(testWriter)
18 | sender.id("some-name", "some-author")
19 | assertEquals(
20 | """
21 | |id name some-name
22 | |id author some-author
23 | |""".trimMargin(),
24 | testWriter.result,
25 | )
26 | }
27 |
28 | @Test
29 | fun `When 'ok' is called it should print 'uciok'`() {
30 | val testWriter = TestWriter()
31 | val sender = DefaultSender(testWriter)
32 | sender.ok()
33 | assertEquals(
34 | """
35 | |uciok
36 | |""".trimMargin(),
37 | testWriter.result,
38 | )
39 | }
40 |
41 | @Test
42 | fun `When 'readyOk' is called it should print 'readyok'`() {
43 | val testWriter = TestWriter()
44 | val sender = DefaultSender(testWriter)
45 | sender.readyOk()
46 | assertEquals(
47 | """
48 | |readyok
49 | |""".trimMargin(),
50 | testWriter.result,
51 | )
52 | }
53 |
54 | @Test
55 | fun `When 'debug' is called and debugMode is false it should print nothing`() {
56 | val testWriter = TestWriter()
57 | val sender = DefaultSender(testWriter)
58 | sender.debugMode = false
59 | sender.debug("some message")
60 | assertEquals(
61 | "",
62 | testWriter.result,
63 | )
64 | }
65 |
66 | @Test
67 | fun `When 'debug' is called and debugMode is true it should print the message`() {
68 | val testWriter = TestWriter()
69 | val sender = DefaultSender(testWriter)
70 | sender.debugMode = true
71 | sender.debug("some message")
72 | assertEquals(
73 | """
74 | |info string some message
75 | |""".trimMargin(),
76 | testWriter.result,
77 | )
78 | }
79 | }
80 |
81 | class TestWriter : Writer {
82 | var result: String = ""
83 | override fun writeln(s: String) {
84 | this.result += "$s\n"
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "pulse"
2 |
3 | val modules = listOf(
4 | "pulse-java",
5 | "pulse-kotlin",
6 | )
7 |
8 | modules.forEach {
9 | include(it)
10 | project(":$it").buildFileName = "$it.gradle.kts"
11 | }
12 |
13 | dependencyResolutionManagement {
14 | repositories {
15 | mavenCentral()
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/dist/logo.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluxroot/pulse/7a06b87b61e90adaeb362e72f0659131141092e9/src/main/dist/logo.bmp
--------------------------------------------------------------------------------