├── .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 | 5 | -------------------------------------------------------------------------------- /.idea/copyright/MIT.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | 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 | 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 | 11 | 16 | 18 | true 19 | true 20 | false 21 | 22 | 23 | -------------------------------------------------------------------------------- /.run/Run Pulse Kotlin Linux.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /.run/Run Pulse Kotlin Windows.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /.run/Run Pulse Kotlin macOS.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 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 --------------------------------------------------------------------------------