├── .gitattributes ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── gen_version.py └── split.py ├── example ├── CMakeLists.txt └── main.cpp ├── include ├── cache.h ├── kernels.cuh ├── lcg.h ├── noise.h ├── util.h └── version.h.template ├── install.bat ├── install.sh ├── libseedfindingConfig.cmake.in ├── test_cpp ├── CMakeLists.txt └── test.cpp └── test_cuda ├── CMakeLists.txt └── test.cu /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Explicitly declare text files you want to always be normalized and converted 5 | # to native line endings on checkout. 6 | *.c text 7 | *.h text 8 | 9 | # Declare files that will always have CRLF line endings on checkout. 10 | *.sh text eol=lf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeLists.txt.user 2 | CMakeCache.txt 3 | CMakeFiles 4 | CMakeScripts 5 | Testing 6 | Makefile 7 | cmake_install.cmake 8 | install_manifest.txt 9 | compile_commands.json 10 | CTestTestfile.cmake 11 | _deps 12 | 13 | .idea/ 14 | 15 | build/ 16 | cmake-build-debug 17 | *.cbp 18 | version.h 19 | example/main 20 | 21 | *.vcxproj 22 | 23 | *.filters 24 | 25 | *.sln 26 | 27 | libseedfindingConfig.cmake 28 | 29 | libseedfindingConfigVersion.cmake 30 | 31 | test_cpp/ 32 | 33 | x64/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | Build options: 3 | * BUILD_SHARED_LIBS (default off) builds as a shared library (if LIBSEEDFINDING_COMPILE is ON) 4 | * LIBSEEDFINDING_COMPILE (default off) 5 | * LIBSEEDFINDING_USE_CUDA (default off) 6 | * LIBSEEDFINDING_UPDATE_VERSION (default on) 7 | 8 | ------------------------------------------------------------------------------- 9 | 10 | After installation with Cmake, a find_package(libseedfinding) is available. 11 | This creates a libseedfinding::libseedfinding target (if found). 12 | It can be linked like so: 13 | 14 | target_link_libraries(your_exe libseedfinding::libseedfinding) 15 | 16 | The following will build & install for later use. 17 | 18 | Linux/macOS: 19 | 20 | mkdir -p build 21 | cd build 22 | cmake -DCMAKE_BUILD_TYPE=Release .. 23 | sudo cmake --build . --target install 24 | 25 | Windows: 26 | 27 | mkdir build 28 | cd build 29 | cmake .. 30 | runas /user:Administrator "cmake --build . --config Release --target install" 31 | 32 | ------------------------------------------------------------------------------- 33 | 34 | These variables are available after you run find_package(libseedfinding) 35 | * LIBSEEDFINDING_HEADER_PATH - this is the full path to the installed header (e.g. /usr/include/libseedfinding.h). 36 | * LIBSEEDFINDING_IS_USING_CUDA - a bool for if Cuda support is enabled. 37 | * LIBSEEDFINDING_IS_COMPILED - a bool for if the library is compiled, or otherwise header-only. 38 | * LIBSEEDFINDING_INCLUDE_DIR - the root path to libseedfinding's header (e.g. /usr/include). 39 | * LIBSEEDFINDING_LIBRARY - the full path to the library if compiled (e.g. /usr/lib/liblibseedfinding.so). 40 | * LIBSEEDFINDING_VERSION or libseedfinding_VERSION - the project's version string. 41 | * LIBSEEDFINDING_FOUND - a bool for if the target was found. 42 | 43 | Want to use precompiled headers (Cmake feature since v3.16)? 44 | It's as simple as doing the following (before linking): 45 | 46 | target_precompile_headers(libseedfinding::libseedfinding INTERFACE "${LIBSEEDFINDING_HEADER_PATH}") 47 | 48 | ------------------------------------------------------------------------------- 49 | 50 | FindPython3 requires Cmake v3.12 51 | ARCH_INDEPENDENT option of write_basic_package_version_file() requires Cmake v3.14 52 | ]] 53 | cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR) 54 | set(CMAKE_VERBOSE_MAKEFILE ON) 55 | 56 | ######################## OPTIONS YOU CAN TURN OFF AND ON ################################ 57 | # Let you use cuda capabilities 58 | option(LIBSEEDFINDING_USE_CUDA "If ON the cuda kernel will be compiled" OFF) 59 | # Let you compile the program as a regular library instead of header-only 60 | option(LIBSEEDFINDING_COMPILE "If ON, uses a Python script to split the header into a compilable header & source file (requires Python v3)." OFF) 61 | # Defaults to static library 62 | option(BUILD_SHARED_LIBS "Build the library as a shared library instead of static. Has no effect if using header-only." OFF) 63 | # populate the version.h 64 | option(LIBSEEDFINDING_UPDATE_VERSION "Download the version manifest from mojang and write it to version.h" ON) 65 | ########################################################################################## 66 | 67 | 68 | # On systems without Git installed, there were errors since execute_process seemed to not throw an error without it? 69 | # Also to not check out the version.h its needed 70 | find_package(Git QUIET) 71 | if (Git_FOUND) 72 | # Gets the latest tag as a string like "v0.6.6" 73 | # Can silently fail if git isn't on the system 74 | execute_process(COMMAND ${GIT_EXECUTABLE} ls-remote https://github.com/Earthcomputer/libseedfinding tags/* 75 | OUTPUT_VARIABLE _raw_version_string 76 | ERROR_VARIABLE _git_tag_error 77 | ) 78 | string(REGEX MATCHALL "refs/tags/[ab]([0-9]+\\.?)+" _libseedfinding_tag_list "${_raw_version_string}") 79 | list(LENGTH _libseedfinding_tag_list list_tag_len) 80 | math(EXPR last_tag_index "${list_tag_len} - 1") 81 | list(GET _libseedfinding_tag_list ${last_tag_index} last_tag) 82 | string(REGEX REPLACE "refs/tags/" "" _raw_version_string ${last_tag}) 83 | endif () 84 | 85 | # execute_process can fail silenty, so check for an error 86 | # if there was an error, just use the user agent as a version 87 | if (_git_tag_error OR NOT Git_FOUND) 88 | message(WARNING "libseedfinding failed to find the latest Git tag, falling back to using the version in the header file.") 89 | # This is so the we can only bother to update the header 90 | file(STRINGS include/util.h _raw_version_string REGEX ".*version.*([0-9]+.?)+") 91 | endif () 92 | # Needed since git tags have "v" prefixing them. 93 | # Also used if the fallback to user agent string is being used. 94 | string(REGEX MATCH "([0-9]+\\.?)+" _libseedfinding_version "${_raw_version_string}") 95 | message("Version is ${_libseedfinding_version}") 96 | 97 | if (LIBSEEDFINDING_USE_CUDA) 98 | message("CUDA has been enabled, next message will tell you if it is supported") 99 | project(libseedfinding VERSION ${_libseedfinding_version} LANGUAGES CXX CUDA) 100 | set(CMAKE_CUDA_STANDARD 14) 101 | include(CheckLanguage) 102 | check_language(CUDA) 103 | if (CMAKE_CUDA_COMPILER) 104 | enable_language(CUDA) 105 | message(STATUS "CUDA support") 106 | set(LIBSEEDFINDING_IS_USING_CUDA TRUE) 107 | else () 108 | message(STATUS "No CUDA support") 109 | endif () 110 | else () 111 | project(libseedfinding VERSION ${_libseedfinding_version} LANGUAGES CXX) 112 | set(CMAKE_CXX_STANDARD 14) 113 | endif () 114 | 115 | # Just setting this variable here for people building in-tree 116 | if (LIBSEEDFINDING_COMPILE) 117 | message("LibSeedFinding will be compiled") 118 | set(LIBSEEDFINDING_IS_COMPILED TRUE) 119 | endif () 120 | 121 | 122 | if (BUILD_SHARED_LIBS AND WIN32 AND LIBSEEDFINDING_COMPILE) 123 | # Necessary for Windows if building shared libs 124 | # See https://stackoverflow.com/a/40743080 125 | set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) 126 | endif () 127 | 128 | if (LIBSEEDFINDING_UPDATE_VERSION) 129 | find_package(Python COMPONENTS Interpreter Development) 130 | if (Python_FOUND) 131 | execute_process(COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gen_version.py ${CMAKE_CURRENT_SOURCE_DIR}) 132 | else () 133 | error("You are missing Python the version.h was not generated") 134 | endif () 135 | endif () 136 | 137 | 138 | # Used for default, common dirs that the end-user can change (if needed) 139 | # like CMAKE_INSTALL_INCLUDEDIR or CMAKE_INSTALL_DATADIR 140 | include(GNUInstallDirs) 141 | 142 | if (LIBSEEDFINDING_COMPILE) 143 | configure_file(cmake/split.py "${CMAKE_CURRENT_BINARY_DIR}/split.py" 144 | COPYONLY 145 | ) 146 | # Needs to be in the same dir as the python script 147 | file(GLOB SEEDFINDING_HEADERS "include/*.h") 148 | file(COPY ${SEEDFINDING_HEADERS} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 149 | 150 | # Used outside of this if-else 151 | set(_INTERFACE_OR_PUBLIC PUBLIC) 152 | # Brings in the Python3_EXECUTABLE path we can use. 153 | find_package(Python3 REQUIRED) 154 | # Actually split the file 155 | # Keeps the output in the build dir to not pollute the main dir 156 | execute_process(COMMAND ${Python3_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/split.py" 157 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 158 | ERROR_VARIABLE _libseedfinding_split_error) 159 | if (_libseedfinding_split_error) 160 | message(FATAL_ERROR "Failed when trying to split Cpp-libseedfinding with the Python script.\n${_libseedfinding_split_error}") 161 | endif () 162 | 163 | # split.py puts output in "out" 164 | set(_libseedfinding_build_includedir "${CMAKE_CURRENT_BINARY_DIR}/out") 165 | # This will automatically be either static or shared based on the value of BUILD_SHARED_LIBS 166 | add_library(${PROJECT_NAME} "${_libseedfinding_build_includedir}/libseedfinding.cc") 167 | target_sources(${PROJECT_NAME} 168 | PUBLIC 169 | $ 170 | $) 171 | else () 172 | set(_INTERFACE_OR_PUBLIC INTERFACE) 173 | add_library(${PROJECT_NAME} INTERFACE) 174 | set(_libseedfinding_build_includedir "${CMAKE_CURRENT_SOURCE_DIR}") 175 | endif () 176 | 177 | ######################### LIST of all header files to include ############################ 178 | list(APPEND LIBSEEDFINDING_HEADERS 179 | "lcg.h" 180 | "noise.h" 181 | "util.h" 182 | "version.h" 183 | "cache.h" 184 | ) 185 | if (LIBSEEDFINDING_IS_USING_CUDA) 186 | list(APPEND LIBSEEDFINDING_HEADERS 187 | "kernels.cuh") 188 | endif() 189 | ########################################################################################## 190 | 191 | foreach(header ${LIBSEEDFINDING_HEADERS}) 192 | list(APPEND libseedfinding_headers "${_libseedfinding_build_includedir}/include/${header}") 193 | endforeach(header) 194 | 195 | message("The selected headers are: ${LIBSEEDFINDING_HEADERS}.") 196 | 197 | 198 | 199 | # Lets you address the target with libseedfinding::libseedfinding 200 | # Only useful if building in-tree, versus using it from an installation. 201 | add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) 202 | 203 | # Might be missing some, but this list is somewhat comprehensive 204 | target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} 205 | cxx_std_14 206 | cxx_nullptr 207 | cxx_lambdas 208 | cxx_override 209 | cxx_defaulted_functions 210 | cxx_attribute_deprecated 211 | cxx_auto_type 212 | cxx_decltype 213 | cxx_deleted_functions 214 | cxx_range_for 215 | cxx_sizeof_member) 216 | 217 | target_include_directories(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} 218 | $ 219 | $) 220 | 221 | # here we could set what libraries we depend on 222 | target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}) 223 | 224 | # Set the definitions to enable optional features 225 | target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} $<$:"CPPLIBSEEDFINDING_CUDA_SUPPORT">) 226 | 227 | # Cmake's find_package search path is different based on the system 228 | # See https://cmake.org/cmake/help/latest/command/find_package.html for the list 229 | if (CMAKE_SYSTEM_NAME STREQUAL "Windows") 230 | set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_PREFIX}/cmake/${PROJECT_NAME}") 231 | else () 232 | # On Non-Windows, it should be /usr/lib/cmake//Config.cmake 233 | # NOTE: This may or may not work for macOS... 234 | set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") 235 | endif () 236 | 237 | include(CMakePackageConfigHelpers) 238 | 239 | # Configures the meta-file libseedfindingConfig.cmake.in to replace variables with paths/values/etc. 240 | configure_package_config_file("${PROJECT_NAME}Config.cmake.in" 241 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 242 | INSTALL_DESTINATION "${_TARGET_INSTALL_CMAKEDIR}" 243 | # Passes the includedir install path 244 | PATH_VARS CMAKE_INSTALL_FULL_INCLUDEDIR 245 | # There aren't any components, so don't use the macro 246 | NO_CHECK_REQUIRED_COMPONENTS_MACRO) 247 | 248 | if (LIBSEEDFINDING_COMPILE) 249 | write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake" 250 | # Example: if you find_package(libseedfinding 0.5.4) 251 | # then anything >= 0.5 and <= 1.0 is accepted 252 | COMPATIBILITY SameMajorVersion) 253 | else () 254 | write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake" 255 | # Example: if you find_pacrkage(libseedfinding 0.5.4) 256 | # then anything >= 0.5 and <= 1.0 is accepted 257 | COMPATIBILITY SameMajorVersion 258 | # Tells Cmake that it's a header-only lib 259 | # Mildly useful for end-users :) 260 | ARCH_INDEPENDENT) 261 | endif () 262 | 263 | # Creates the export libseedfindingTargets.cmake 264 | # This is strictly what holds compilation requirements 265 | # and linkage information (doesn't find deps though). 266 | install(TARGETS ${PROJECT_NAME} 267 | EXPORT libseedfindingTargets 268 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 269 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) 270 | 271 | foreach(header ${libseedfinding_headers}) 272 | install(FILES 273 | ${header} 274 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 275 | message("File ${header} will be installed if you choose to") 276 | endforeach(header) 277 | 278 | install(FILES 279 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 280 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" 281 | DESTINATION ${_TARGET_INSTALL_CMAKEDIR}) 282 | 283 | # NOTE: This path changes depending on if it's on Windows or Linux 284 | install(EXPORT libseedfindingTargets 285 | # Puts the targets into the libseedfinding namespace 286 | # So this makes libseedfinding::libseedfinding linkable after doing find_package(libseedfinding) 287 | NAMESPACE ${PROJECT_NAME}:: 288 | DESTINATION ${_TARGET_INSTALL_CMAKEDIR}) 289 | 290 | if (LIBSEEDFINDING_IS_USING_CUDA) 291 | add_subdirectory(test_cuda) 292 | else () 293 | add_subdirectory(test_cpp) 294 | endif () 295 | 296 | if (MSVC) 297 | add_custom_target(install_${PROJECT_NAME} 298 | runas /user:Administrator "${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --config Release --target install" 299 | DEPENDS ${PROJECT_NAME} 300 | COMMENT "Installing ${PROJECT_NAME}" 301 | SOURCES ${libseedfinding_headers} 302 | USES_TERMINAL) 303 | else () 304 | add_custom_target(install_${PROJECT_NAME} 305 | sudo "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --target install 306 | DEPENDS ${PROJECT_NAME} 307 | COMMENT "Installing ${PROJECT_NAME}" 308 | SOURCES ${libseedfinding_headers} 309 | USES_TERMINAL) 310 | endif () -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright The Minecraft Seed Finding Team 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LibSeedFinding 2 | 3 | # Grand public usage 4 | 5 | It's recommended to use the example repository here: https://github.com/hube12/seedfinding_example. 6 | 7 | It's solely a cmake file that configure and add the library as a local library so the executable can be link against. 8 | 9 | The main part are the cmake/libseedfinding.cmake and the following lines : 10 | 11 | ```cmake 12 | ##################### Configure the library ############################## 13 | # find the external project and include it 14 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 15 | include(seedfinding) 16 | # create the library and bind the cmake script to it, the library is available everywhere now 17 | add_library(libseedfinding INTERFACE) 18 | add_dependencies(libseedfinding seedfinding) 19 | target_include_directories(libseedfinding INTERFACE ${libseedfinding_INCLUDE_DIR}) 20 | ########################################################################## 21 | ``` 22 | 23 | # Install in the system 24 | 25 | After installation (you can use the install.bat as administrator, or the install.sh with sudo privileges) with Cmake, 26 | a find_package(libseedfinding) is available. 27 | 28 | This creates a libseedfinding::libseedfinding target (if found). 29 | 30 | It can be linked like so: 31 | 32 | `target_link_libraries(your_exe libseedfinding::libseedfinding)` 33 | 34 | The following will build & install for later use. (you can also do `cmake . && make install_libseedfinding`) 35 | 36 | Linux/macOS: 37 | ``` 38 | mkdir -p build 39 | cd build 40 | cmake -DCMAKE_BUILD_TYPE=Release .. 41 | sudo cmake --build . --target install 42 | ``` 43 | Windows: 44 | ``` 45 | mkdir build 46 | cd build 47 | cmake .. 48 | runas /user:Administrator "cmake --build . --config Release --target install" 49 | ``` 50 | 51 | # Usage 52 | 53 | See example/ after installing 54 | 55 | # Development 56 | 57 | Create new .h in include and add it to the CmakeLists.txt after the line 177: 58 | 59 | ```cmake 60 | ######################### LIST of all header files to include ############################ 61 | list(APPEND LIBSEEDFINDING_HEADERS 62 | "lcg.h" 63 | "simplex.h" 64 | "util.h" 65 | "version.h") 66 | if (LIBSEEDFINDING_IS_USING_CUDA) 67 | list(APPEND LIBSEEDFINDING_HEADERS 68 | "kernels.cuh") 69 | endif() 70 | ########################################################################################## 71 | ``` 72 | 73 | 74 | # Bugs and untested 75 | 76 | So far the standalone static library is not tested, see split.py 77 | -------------------------------------------------------------------------------- /cmake/gen_version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding=utf-8 -*- pyversions=2.6+,3.3+ 3 | import json 4 | try: #python3 5 | from urllib.request import urlopen 6 | from urllib.error import HTTPError, URLError 7 | except: #python2 8 | from urllib2 import urlopen 9 | from urllib2 import HTTPError, URLError 10 | 11 | import os 12 | import sys 13 | 14 | if len(sys.argv) != 2: 15 | print("missing path as argument") 16 | sys.exit(-1) 17 | path = sys.argv[1] 18 | 19 | 20 | def download_file(url, filename): 21 | try: 22 | print('Downloading '+filename+'...') 23 | f = urlopen(url) 24 | with open(filename, 'wb+') as local_file: 25 | local_file.write(f.read()) 26 | except HTTPError as e: 27 | print('HTTP Error') 28 | print(e) 29 | except URLError as e: 30 | print('URL Error') 31 | print(e) 32 | 33 | 34 | download_file("https://launchermeta.mojang.com/mc/game/version_manifest.json", "version_manifest.json") 35 | if not os.path.isfile('version_manifest.json'): 36 | exit(-1) 37 | print("Download of version_manifest.json was a success") 38 | text = [] 39 | with open("version_manifest.json") as file: 40 | versions = json.load(file)["versions"] 41 | versions.reverse() 42 | typf = lambda typ: "SNAPSHOT" if typ == "snapshot" else "RELEASE" if typ == "release" else "OLD_ALPHA" \ 43 | if typ == "old_alpha" else "OLD_BETA" if typ == "old_beta" else "NONE" 44 | for i, version in enumerate(versions): 45 | text.append("const Version MC_" + version.get("id").replace(".", "_").replace("-", "_").replace(" ", "_") + 46 | " = {" + str(i) + ", " + typf(version.get("type")) + ', "' + version.get("releaseTime") + '"}; ') 47 | 48 | os.remove("version_manifest.json") 49 | with open(path + '/include/version.h.template') as file: 50 | with open(path + '/include/version.h', 'w') as out: 51 | for line in file: 52 | out.write(line.replace("/////////////////////////GENERATION////////////////////////////////", (os.linesep + "\t").join(text))) 53 | -------------------------------------------------------------------------------- /cmake/split.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | border = '// ----------------------------------------------------------------------------' 5 | 6 | PythonVersion = sys.version_info[0]; 7 | # find all .h first 8 | with open('lcg.h') as f: 9 | lines = f.readlines() 10 | inImplementation = False 11 | 12 | if PythonVersion < 3: 13 | os.makedirs('out') 14 | else: 15 | os.makedirs('out', exist_ok=True) 16 | 17 | with open('out/libseedfinding.h', 'w') as fh: 18 | with open('out/libseedfinding.cc', 'w') as fc: 19 | fc.write('#include "libseedfinding.h"\n') 20 | fc.write('namespace libseedfinding {\n') 21 | for line in lines: 22 | isBorderLine = border in line 23 | if isBorderLine: 24 | inImplementation = not inImplementation 25 | else: 26 | if inImplementation: 27 | fc.write(line.replace('inline ', '')) 28 | pass 29 | else: 30 | fh.write(line) 31 | pass 32 | fc.write('} // namespace libseedfinding\n') 33 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16 FATAL_ERROR) 2 | project(seedfinding_example LANGUAGES CXX) 3 | find_package(libseedfinding) 4 | add_executable(main main.cpp) 5 | # improve compilation speed only in cmake 3.16+ 6 | target_precompile_headers(libseedfinding::libseedfinding INTERFACE "${LIBSEEDFINDING_HEADER_PATH}") 7 | target_link_libraries(main libseedfinding::libseedfinding) -------------------------------------------------------------------------------- /example/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) The Minecraft Seed Finding Team 2 | // 3 | // MIT License 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | int main() { 12 | std::cout << lcg::dfz2seed(47) << std::endl; 13 | std::cout << version::MC_1_16.id << std::endl; 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /include/cache.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) The Minecraft Seed Finding Team 2 | // 3 | // MIT License 4 | 5 | #ifndef LIBSEEDFINDING_CACHE_H 6 | #define LIBSEEDFINDING_CACHE_H 7 | namespace cache{ 8 | // make a int cache for size 16 32 48 64 80 96 112 128 9 | template struct Cache{ 10 | K map; 11 | }; 12 | } 13 | #endif //LIBSEEDFINDING_CACHE_H 14 | -------------------------------------------------------------------------------- /include/kernels.cuh: -------------------------------------------------------------------------------- 1 | // Copyright (c) The Minecraft Seed Finding Team 2 | // 3 | // MIT License 4 | 5 | #ifndef LIBSEEDFINDING_KERNELS_H 6 | #define LIBSEEDFINDING_KERNELS_H 7 | 8 | #ifndef __CUDACC__ 9 | #error "kernels.cuh can only be used with CUDA" 10 | #endif 11 | 12 | #include 13 | #include "lcg.h" 14 | 15 | /** 16 | * Boilerplate code for different types of kernels. 17 | */ 18 | namespace kernels { 19 | /// A function which takes a seed and returns a true or false value. 20 | typedef bool(*seed_predicate)(lcg::Random); 21 | 22 | /// A format for a seed to be in. 23 | enum class seed_format { 24 | /// Specifies that the value is equal to the seed of the Random. 25 | SEED, 26 | /// Specifies that the value is equal to the DFZ form of the seed of the Random. See lcg::dfz2seed for details. 27 | DFZ 28 | }; 29 | 30 | /** 31 | * This kernel can be used for the first step in most brute forces. It runs over the entire seed-space and adds to 32 | * the output buffer where the given predicate matches. A single call to this kernel will only do a fraction of the 33 | * seed-space, to search the full seed-space, this function must be called in a loop with different offset values. 34 | * 35 | * @tparam PREDICATE The function used to test_cuda whether a seed should be added to the buffer. This function will 36 | * always receive seeds in seed_format::SEED form, regardless of the value of INPUT_ORDER. 37 | * Must be a __device__ function. 38 | * @tparam INPUT_ORDER The order in which input seeds are brute-forced. Defaults to seed_format::SEED, which is the 39 | * most efficient in most cases, however some optimized functions rely on seed_format::DFZ. 40 | * These functions state this in their documentation. 41 | * @tparam OUTPUT_FORMAT The format of seeds added to the output buffer. Defaults to seed_format::SEED (regardless 42 | * of input order). 43 | * @tparam count_t The type of the output size of the buffer. Defaults to uint32_t. 44 | * @param offset The start point of the bruteforce. This kernel will bruteforce gridDim * blockDim seeds starting 45 | * from this offset. 46 | * @param count The kernel will start adding seeds to buffer at this index. After the kernel has finished, this 47 | * value will store the index of the next value to be inserted into buffer. That is, if count starts 48 | * off as 0, then count will end up as the number of matching seeds added to the buffer. 49 | * @param buffer The buffer to add matching seeds to. 50 | */ 51 | template 52 | __global__ void bruteforce(uint64_t offset, count_t* count, uint64_t* buffer) { 53 | uint64_t value = offset + blockIdx.x * blockDim.x + threadIdx.x; 54 | lcg::Random seed = INPUT_ORDER == seed_format::SEED ? value : lcg::dfz2seed_inline(value); 55 | 56 | if (PREDICATE(seed)) { 57 | count_t index = atomicAdd(count, 1); 58 | if (OUTPUT_FORMAT == seed_format::SEED) { 59 | buffer[index] = seed; 60 | } else if (INPUT_ORDER == seed_format::DFZ) { 61 | buffer[index] = value; 62 | } else { 63 | buffer[index] = lcg::seed2dfz_inline(seed); 64 | } 65 | } 66 | } 67 | 68 | /** 69 | * This kernel can be used as a secondary step to filter down a list of seeds. Seeds are read from bothput, tested, 70 | * and if the predicate returns false, 0 is written to bothput. If INPUT_FORMAT is different from OUTPUT_FORMAT, the 71 | * seeds in bothput are also converted and written back. Note that this kernel assumes that seed 0 does not match. 72 | * If this is a concern, seed 0 should be tested separately on the host. 73 | * 74 | * @tparam PREDICATE The function used to test_cuda whether a seed should be added to the buffer. This function will 75 | * always receive seeds in seed_format::SEED form, regardless of the value of INPUT_ORDER. 76 | * Must be a __device__ function. 77 | * @tparam INPUT_FORMAT The format of the input seeds in bothput. If not equal to seed_format::SEED, seeds in 78 | * bothput will be converted to seed_format::SEED before being passed to PREDICATE. 79 | * @tparam OUTPUT_FORMAT The format of the seeds in bothput afterwards. Defaults to seed_format::SEED. 80 | * @tparam count_t The type of count. 81 | * @param count The number of seeds in bothput. 82 | * @param bothput The seed buffer. 83 | */ 84 | template 85 | __global__ void filter(count_t count, uint64_t* bothput) { 86 | count_t index = blockIdx.x * blockDim.x + threadIdx.x; 87 | if (index > count) { 88 | return; 89 | } 90 | uint64_t value = bothput[index]; 91 | if (value == 0) { 92 | return; 93 | } 94 | lcg::Random seed = INPUT_FORMAT == seed_format::SEED ? value : lcg::dfz2seed_inline(value); 95 | 96 | if (PREDICATE(seed)) { 97 | if (INPUT_FORMAT == seed_format::SEED && OUTPUT_FORMAT == seed_format::DFZ) { 98 | bothput[index] = lcg::seed2dfz_inline(seed); 99 | } else if (INPUT_FORMAT == seed_format::DFZ && OUTPUT_FORMAT == seed_format::SEED) { 100 | bothput[index] = seed; 101 | } 102 | } else { 103 | bothput[index] = 0; 104 | } 105 | } 106 | 107 | /** 108 | * Copies seeds from input into output, skipping 0 values. This should only be used if the input buffer is both 109 | * large and sparse. In many cases, a simple loop on the host is more efficient due to the overhead of copying 110 | * memory to and from the device. 111 | * 112 | * @tparam input_count_t The type of input_count. 113 | * @tparam output_count_t The type of output_count. 114 | * @param input_count The number of seeds in the input buffer. 115 | * @param input The input buffer. 116 | * @param output_count The number of seeds in the output buffer will be written to this value. Should be initialized 117 | * to 0 before calling this kernel. 118 | * @param output The output buffer. 119 | */ 120 | template 121 | __global__ void compact(input_count_t input_count, uint64_t* input, output_count_t* output_count, uint64_t* output) { 122 | input_count_t index = blockIdx.x * blockDim.x + threadIdx.x; 123 | if (index > input_count) { 124 | return; 125 | } 126 | uint64_t value = input[index]; 127 | if (value != 0) { 128 | output_count_t output_index = atomicAdd(output_count, 1); 129 | output[output_index] = value; 130 | } 131 | } 132 | } 133 | 134 | #endif //LIBSEEDFINDING_KERNELS_H 135 | -------------------------------------------------------------------------------- /include/lcg.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) The Minecraft Seed Finding Team 2 | // 3 | // MIT License 4 | 5 | #ifndef LIBSEEDFINDING_LCG_H 6 | #define LIBSEEDFINDING_LCG_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include "util.h" 12 | 13 | #if CPP_VER < 201402L 14 | #error lcg.h requires C++ 14 15 | #endif 16 | 17 | 18 | /** 19 | * Contains an implementation of the Java LCG and utility functions surrounding it. 20 | * Many functions here are constexpr, meaning that they can be evaluated at compile-time if their inputs are 21 | * also statically known. Otherwise, they are usually inlined by the compiler instead. 22 | */ 23 | namespace lcg { 24 | typedef uint64_t Random; 25 | 26 | const uint64_t MULTIPLIER = 0x5deece66dLL; 27 | const uint64_t ADDEND = 0xbLL; 28 | const uint64_t MASK = 0xffffffffffffL; 29 | 30 | /// The minimum distance that two nextFloat values can be from each other. May be used to iterate over all valid floats. 31 | static DEVICEABLE_CONST float FLOAT_UNIT = 1.0f / static_cast(1L << 24); 32 | /// The minimum distance that two nextDouble values can be from each other. May be used to iterate over all valid doubles. 33 | static DEVICEABLE_CONST double DOUBLE_UNIT = 1.0 / static_cast(1LL << 53); 34 | 35 | // Declared here for forward reference 36 | template 37 | DEVICEABLE constexpr typename std::enable_if_t<(0 <= B && B <= 32), int32_t> next(Random &rand); 38 | 39 | /** 40 | * Contains internal functions. These are unstable, do not use them for any reason! 41 | * If you think you need something from in here, first look for alternatives, else consider adding something to the public API for it. 42 | * The internal functions are included in the header file so that the compiler can optimize using their implementations. 43 | * Many of these functions are force-inlined. This is to ensure the public force-inlined functions are fully inlined properly. 44 | */ 45 | namespace internal { 46 | // for returning multiple values 47 | struct LCG { 48 | uint64_t multiplier; 49 | uint64_t addend; 50 | }; 51 | 52 | DEVICEABLE FORCEINLINE constexpr LCG combine(uint64_t calls) { 53 | uint64_t multiplier = 1; 54 | uint64_t addend = 0; 55 | 56 | uint64_t intermediate_multiplier = MULTIPLIER; 57 | uint64_t intermediate_addend = ADDEND; 58 | 59 | for (uint64_t k = calls; k != 0; k >>= 1) { 60 | if ((k & 1) != 0) { 61 | multiplier *= intermediate_multiplier; 62 | addend = intermediate_multiplier * addend + intermediate_addend; 63 | } 64 | 65 | intermediate_addend = (intermediate_multiplier + 1) * intermediate_addend; 66 | intermediate_multiplier *= intermediate_multiplier; 67 | } 68 | 69 | multiplier &= MASK; 70 | addend &= MASK; 71 | 72 | return {multiplier, addend}; 73 | } 74 | 75 | DEVICEABLE FORCEINLINE constexpr LCG combine(int64_t calls) { 76 | return combine(static_cast(calls)); 77 | } 78 | 79 | DEVICEABLE FORCEINLINE constexpr uint64_t gcd(uint64_t a, uint64_t b) { 80 | if (b == 0) { 81 | return a; 82 | } 83 | while (true) { 84 | a %= b; 85 | if (a == 0) { 86 | return b; 87 | } 88 | b %= a; 89 | if (b == 0) { 90 | return a; 91 | } 92 | } 93 | } 94 | 95 | // Returns modulo inverse of a with 96 | // respect to m using extended Euclid 97 | // Algorithm Assumption: a and m are 98 | // coprimes, i.e., gcd(a, m) = 1 99 | // stolen code, now handles the case where gcd(a,m) != 1 100 | DEVICEABLE FORCEINLINE constexpr uint64_t euclidean_helper(uint64_t a, uint64_t m) { 101 | uint64_t y = 0, x = 1; 102 | if (m == 1) { 103 | return 0; 104 | } 105 | uint64_t gcd_ = gcd(a, m); 106 | while (a > gcd_) { 107 | uint64_t q = a / m; 108 | uint64_t t = m; 109 | m = a % m; 110 | a = t; 111 | t = y; 112 | y = x - q * y; 113 | x = t; 114 | } 115 | return x; 116 | } 117 | 118 | DEVICEABLE FORCEINLINE constexpr uint64_t theta(uint64_t num) { 119 | if (num % 4 == 3) { 120 | num = (1LL << 50) - num; 121 | } 122 | 123 | // xhat = num 124 | uint64_t xhat_lo = num; 125 | uint64_t xhat_hi = 0; 126 | 127 | // raise xhat to the power of 2^49 by squaring it 49 times 128 | for (int i = 0; i < 49; i++) { 129 | // https://www.codeproject.com/Tips/618570/UInt-Multiplication-Squaring 130 | uint64_t r1 = xhat_lo & 0xffffffffLL; 131 | uint64_t t = r1 * r1; 132 | uint64_t w3 = t & 0xffffffffLL; 133 | uint64_t k = t >> 32; 134 | uint64_t r = xhat_lo >> 32; 135 | uint64_t m = r * r1; 136 | t = m + k; 137 | uint64_t w2 = t & 0xffffffffLL; 138 | uint64_t w1 = t >> 32; 139 | t = m + w2; 140 | k = t >> 32; 141 | uint64_t new_hi = r * r + w1 + k; 142 | uint64_t new_lo = (t << 32) + w3; 143 | new_hi += (xhat_hi * xhat_lo) << 1; 144 | xhat_lo = new_lo; 145 | xhat_hi = new_hi; 146 | } 147 | xhat_hi &= (1LL << (99 - 64)) - 1; 148 | 149 | // xhat-- 150 | if (xhat_lo == 0) xhat_hi--; 151 | xhat_lo--; 152 | 153 | // xhat >>= 51 154 | xhat_lo = (xhat_lo >> 51) | (xhat_hi << (64 - 51)); 155 | 156 | // xhat &= MASK 157 | xhat_lo &= MASK; 158 | return xhat_lo; 159 | } 160 | 161 | DEVICEABLE constexpr int32_t dynamic_next_int_power_of_2(Random &rand, int32_t n) { 162 | return static_cast((static_cast(next<31>(rand)) * static_cast(n)) >> 31); 163 | } 164 | } 165 | 166 | 167 | /// Unsigned equivalent of combined_lcg. 168 | template 169 | struct ucombined_lcg { 170 | /// The multiplier of the LCG that advances by N. 171 | static const uint64_t multiplier = internal::combine(N).multiplier; 172 | /// The addend of the LCG that advances by N. 173 | static const uint64_t addend = internal::combine(N).addend; 174 | }; 175 | 176 | /** 177 | * Contains the multiplier and addend of the LCG equivalent to advancing the Java LCG by N. 178 | * N may be negative to signal a backwards advance. 179 | */ 180 | template 181 | struct combined_lcg { 182 | /// The multiplier of the LCG that advances by N. 183 | static const uint64_t multiplier = ucombined_lcg(N)>::multiplier; 184 | /// The addend of the LCG that advances by N. 185 | static const uint64_t addend = ucombined_lcg(N)>::addend; 186 | }; 187 | 188 | /// Advances the Random by an unsigned N steps, which defaults to 1. Runs in O(1) time because of compile-time optimizations. 189 | template 190 | DEVICEABLE constexpr void uadvance(Random &rand) { 191 | rand = (rand * ucombined_lcg::multiplier + ucombined_lcg::addend) & MASK; 192 | } 193 | 194 | /** 195 | * Advances the Random by N steps, which defaults to 1. Runs in O(1) time because of compile-time optimizations. 196 | * N may be negative to signal a backwards advance. 197 | */ 198 | template 199 | DEVICEABLE constexpr void advance(Random &rand) { 200 | uadvance(N)>(rand); 201 | } 202 | 203 | /// Force-inlined version of dynamic_advance. Do not use unless profiling tells you that the compiler is not inlining anyway! 204 | DEVICEABLE FORCEINLINE constexpr void dynamic_advance_inline(Random &rand, uint64_t n) { 205 | #define ADVANCE_BIT(N) if (n < (1LL << N)) return;\ 206 | if (n & (1LL << N)) uadvance<1LL << N>(rand); 207 | ADVANCE_BIT(0) 208 | ADVANCE_BIT(1) 209 | ADVANCE_BIT(2) 210 | ADVANCE_BIT(3) 211 | ADVANCE_BIT(4) 212 | ADVANCE_BIT(5) 213 | ADVANCE_BIT(6) 214 | ADVANCE_BIT(7) 215 | ADVANCE_BIT(8) 216 | ADVANCE_BIT(9) 217 | ADVANCE_BIT(10) 218 | ADVANCE_BIT(11) 219 | ADVANCE_BIT(12) 220 | ADVANCE_BIT(13) 221 | ADVANCE_BIT(14) 222 | ADVANCE_BIT(15) 223 | ADVANCE_BIT(16) 224 | ADVANCE_BIT(17) 225 | ADVANCE_BIT(18) 226 | ADVANCE_BIT(19) 227 | ADVANCE_BIT(20) 228 | ADVANCE_BIT(21) 229 | ADVANCE_BIT(22) 230 | ADVANCE_BIT(23) 231 | ADVANCE_BIT(24) 232 | ADVANCE_BIT(25) 233 | ADVANCE_BIT(26) 234 | ADVANCE_BIT(27) 235 | ADVANCE_BIT(28) 236 | ADVANCE_BIT(29) 237 | ADVANCE_BIT(30) 238 | ADVANCE_BIT(31) 239 | ADVANCE_BIT(32) 240 | ADVANCE_BIT(33) 241 | ADVANCE_BIT(34) 242 | ADVANCE_BIT(35) 243 | ADVANCE_BIT(36) 244 | ADVANCE_BIT(37) 245 | ADVANCE_BIT(38) 246 | ADVANCE_BIT(39) 247 | ADVANCE_BIT(40) 248 | ADVANCE_BIT(41) 249 | ADVANCE_BIT(42) 250 | ADVANCE_BIT(43) 251 | ADVANCE_BIT(44) 252 | ADVANCE_BIT(45) 253 | ADVANCE_BIT(46) 254 | ADVANCE_BIT(47) 255 | #undef ADVANCE_BIT 256 | } 257 | 258 | /// Advances the Random by an unsigned n steps. Used when n is not known at compile-time. Runs in O(log(n)) time. 259 | DEVICEABLE constexpr void dynamic_advance(Random &rand, uint64_t n) { 260 | dynamic_advance_inline(rand, n); 261 | } 262 | 263 | /// Force-inlined version of dynamic_advance. Do not use unless profiling tells you that the compiler is not inlining anyway! 264 | DEVICEABLE FORCEINLINE constexpr void dynamic_advance_inline(Random &rand, int64_t n) { 265 | dynamic_advance_inline(rand, static_cast(n)); 266 | } 267 | 268 | /** 269 | * Advances the Random by n steps. Used when n is not known at compile-time. Runs in O(log(n)) time. 270 | * n may be negative to signal a backwards advance. 271 | */ 272 | DEVICEABLE constexpr void dynamic_advance(Random &rand, int64_t n) { 273 | dynamic_advance_inline(rand, n); 274 | } 275 | 276 | /// Force-inlined version of dfz2seed. Do not use unless profiling tells you that the compiler is not inlining anyway! 277 | DEVICEABLE FORCEINLINE constexpr Random dfz2seed_inline(uint64_t dfz) { 278 | Random seed = 0; 279 | dynamic_advance_inline(seed, dfz); 280 | return seed; 281 | } 282 | 283 | /** 284 | * Converts a Distance From Zero (DFZ) value to a Random seed. 285 | * DFZ is a representation of a seed which is the number of LCG calls required to get from seed 0 to that seed. 286 | * To get Random outputs from a DFZ value it must first be converted to a seed, which is done in O(log(dfz)). 287 | * In various situations, especially far GPU parallelization, it may be useful to represent seeds this way. 288 | */ 289 | DEVICEABLE constexpr Random dfz2seed(uint64_t dfz) { 290 | return dfz2seed_inline(dfz); 291 | } 292 | 293 | /// Force-inlined version of seed2dfz. Do not use unless profiling tells you that the compiler is not inlining anyway! 294 | DEVICEABLE FORCEINLINE constexpr uint64_t seed2dfz_inline(Random seed) { 295 | uint64_t a = 25214903917LL; 296 | uint64_t b = (((seed * (MULTIPLIER - 1)) * 179120439724963LL) + 1) & ((1LL << 50) - 1); 297 | uint64_t abar = internal::theta(a); 298 | uint64_t bbar = internal::theta(b); 299 | uint64_t gcd_ = internal::gcd(abar, (1LL << 48)); 300 | return (bbar * internal::euclidean_helper(abar, (1LL << 48)) & 0x3FFFFFFFFFFFLL) / gcd_; //+ i*(1L << 48)/gcd; 301 | } 302 | 303 | /** 304 | * Converts a Random seed to DFZ form. See dfz2seed for a description of DFZ form. 305 | * This function should be called reservedly, as although it is O(1), it is relatively slow. 306 | */ 307 | DEVICEABLE constexpr uint64_t seed2dfz(Random seed) { 308 | return seed2dfz_inline(seed); 309 | } 310 | 311 | /// Advances the LCG and gets the upper B bits from it. 312 | template 313 | DEVICEABLE constexpr typename std::enable_if_t<(0 <= B && B <= 32), int32_t> next(Random &rand) { 314 | advance(rand); 315 | return static_cast(rand >> (48 - B)); 316 | } 317 | 318 | /// Does an unbounded nextInt call and returns the result. 319 | DEVICEABLE constexpr int32_t next_int_unbounded(Random &rand) { 320 | return next<32>(rand); 321 | } 322 | 323 | /// Does a bounded nextInt call with bound N. 324 | template 325 | DEVICEABLE constexpr typename std::enable_if_t<(N > 0) && ((N & -N) == N), int32_t> next_int(Random &rand) { 326 | return static_cast((static_cast(next<31>(rand)) * static_cast(N)) >> 31); 327 | } 328 | 329 | template 330 | DEVICEABLE constexpr typename std::enable_if_t<(N > 0) && ((N & -N) != N), int32_t> next_int(Random &rand) { 331 | int32_t bits = next<31>(rand); 332 | int32_t val = bits % N; 333 | while (bits - val + (N - 1) < 0) { 334 | bits = next<31>(rand); 335 | val = bits % N; 336 | } 337 | return val; 338 | } 339 | 340 | /** 341 | * Does a bounded nextInt call with bound N. If N is not a power of 2, then it makes the assumption that the loop 342 | * does not iterate more than once. The probability of this being correct depends on N, but for small N this 343 | * function is extremely likely to have the same effect as next_int. 344 | */ 345 | template 346 | DEVICEABLE constexpr typename std::enable_if_t<(N > 0), int32_t> next_int_fast(Random &rand) { 347 | if ((N & -N) == N) { 348 | return next_int(rand); 349 | } else { 350 | return next<31>(rand) % N; 351 | } 352 | } 353 | 354 | /// Does a bounded nextInt call with bound n, used when n is not known in advance. 355 | DEVICEABLE constexpr int32_t dynamic_next_int(Random &rand, int32_t n) { 356 | if ((n & -n) == n) { 357 | return internal::dynamic_next_int_power_of_2(rand, n); 358 | } else { 359 | int32_t bits = next<31>(rand); 360 | int32_t val = bits % n; 361 | while (bits - val + (n - 1) < 0) { 362 | bits = next<31>(rand); 363 | val = bits % n; 364 | } 365 | return val; 366 | } 367 | } 368 | 369 | /** 370 | * Does a bounded nextInt call with bound n, using the "fast" approach, used when n is not known in advance. 371 | * See next_int_fast for a description of the fast approach. 372 | */ 373 | DEVICEABLE constexpr int32_t dynamic_next_int_fast(Random &rand, int32_t n) { 374 | if ((n & -n) == n) { 375 | return internal::dynamic_next_int_power_of_2(rand, n); 376 | } else { 377 | return next<31>(rand) % n; 378 | } 379 | } 380 | 381 | /// Does a nextLong call. 382 | DEVICEABLE constexpr int64_t next_long(Random &rand) { 383 | // separate out calls due to unspecified evaluation order in C++ 384 | int32_t hi = next<32>(rand); 385 | int32_t lo = next<32>(rand); 386 | return (static_cast(hi) << 32) + static_cast(lo); 387 | } 388 | 389 | /// Does an unsigned nextLong call. 390 | DEVICEABLE constexpr uint64_t next_ulong(Random &rand) { 391 | return static_cast(next_long(rand)); 392 | } 393 | 394 | /// Does a nextBoolean call. 395 | DEVICEABLE constexpr bool next_bool(Random &rand) { 396 | return next<1>(rand) != 0; 397 | } 398 | 399 | /// Does a nextFloat call. 400 | DEVICEABLE std::enable_if_t::is_iec559, float> next_float(Random &rand) { 401 | return static_cast(next<24>(rand)) * FLOAT_UNIT; 402 | } 403 | 404 | /// Does a nextDouble call. 405 | DEVICEABLE std::enable_if_t::is_iec559, double> next_double(Random &rand) { 406 | // separate out calls due to unspecified evaluation order in C++ 407 | int32_t hi = next<26>(rand); 408 | int32_t lo = next<27>(rand); 409 | return static_cast((static_cast(hi) << 27) + static_cast(lo)) * DOUBLE_UNIT; 410 | } 411 | } 412 | 413 | #endif //LIBSEEDFINDING_LCG_H 414 | -------------------------------------------------------------------------------- /include/noise.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) The Minecraft Seed Finding Team 2 | // 3 | // MIT License 4 | 5 | #ifndef SEEDFINDING_SIMPLEX_H 6 | #define SEEDFINDING_SIMPLEX_H 7 | 8 | #include "lcg.h" 9 | #include "cache.h" 10 | // constant definition for simplex 11 | #define F2 0.3660254037844386 12 | #define G2 0.21132486540518713 13 | #define F3 0.3333333333333333 14 | #define G3 0.16666666666666666 15 | 16 | namespace noise { 17 | struct Noise { 18 | double xo; 19 | double yo; 20 | double zo; 21 | uint8_t permutations[256]; 22 | }; 23 | 24 | static inline void initOctaves(Noise octaves[], lcg::Random random, int nbOctaves) { 25 | for (int i = 0; i < nbOctaves; ++i) { 26 | octaves[i].xo = lcg::next_double(random) * 256.0; 27 | octaves[i].yo = lcg::next_double(random) * 256.0; 28 | octaves[i].zo = lcg::next_double(random) * 256.0; 29 | uint8_t *permutations = octaves[i].permutations; 30 | uint8_t j = 0; 31 | do { 32 | permutations[j] = j; 33 | } while (j++ != 255); 34 | uint8_t index = 0; 35 | do { 36 | uint32_t randomIndex = lcg::dynamic_next_int(random, 256u - index) + index; 37 | if (randomIndex != index) { 38 | // swap 39 | permutations[index] ^= permutations[randomIndex]; 40 | permutations[randomIndex] ^= permutations[index]; 41 | permutations[index] ^= permutations[randomIndex]; 42 | } 43 | } while (index++ != 255); 44 | } 45 | } 46 | } 47 | namespace simplex { 48 | struct Simplex{ 49 | noise::Noise noise; 50 | cache::Cache< 51 | } 52 | DEVICEABLE_CONST int grad2[12][2] = {{1, 1,}, 53 | {-1, 1,}, 54 | {1, -1,}, 55 | {-1, -1,}, 56 | {1, 0,}, 57 | {-1, 0,}, 58 | {1, 0,}, 59 | {-1, 0,}, 60 | {0, 1,}, 61 | {0, -1,}, 62 | {0, 1,}, 63 | {0, -1,}}; 64 | 65 | static inline void simplexNoise(double **buffer, double chunkX, double chunkZ, int x, int z, double offsetX, double offsetZ, double octaveFactor, PermutationTable permutationTable) { 66 | int k = 0; 67 | uint8_t *permutations = permutationTable.permutations; 68 | for (int X = 0; X < x; X++) { 69 | double XCoords = (chunkX + (double) X) * offsetX + permutationTable.xo; 70 | for (int Z = 0; Z < z; Z++) { 71 | double ZCoords = (chunkZ + (double) Z) * offsetZ + permutationTable.yo; 72 | // Skew the input space to determine which simplex cell we're in 73 | double hairyFactor = (XCoords + ZCoords) * F2; 74 | auto tempX = static_cast(XCoords + hairyFactor); 75 | auto tempZ = static_cast(ZCoords + hairyFactor); 76 | int32_t xHairy = (XCoords + hairyFactor < tempX) ? (tempX - 1) : (tempX); 77 | int32_t zHairy = (ZCoords + hairyFactor < tempZ) ? (tempZ - 1) : (tempZ); 78 | double d11 = (double) (xHairy + zHairy) * G2; 79 | double X0 = (double) xHairy - d11; // Unskew the cell origin back to (x,y) space 80 | double Y0 = (double) zHairy - d11; 81 | double x0 = XCoords - X0; // The x,y distances from the cell origin 82 | double y0 = ZCoords - Y0; 83 | // For the 2D case, the simplex shape is an equilateral triangle. 84 | // Determine which simplex we are in. 85 | int offsetSecondCornerX, offsetSecondCornerZ; // Offsets for second (middle) corner of simplex in (i,j) coords 86 | 87 | if (x0 > y0) { // lower triangle, XY order: (0,0)->(1,0)->(1,1) 88 | offsetSecondCornerX = 1; 89 | offsetSecondCornerZ = 0; 90 | } else { // upper triangle, YX order: (0,0)->(0,1)->(1,1) 91 | offsetSecondCornerX = 0; 92 | offsetSecondCornerZ = 1; 93 | } 94 | 95 | double x1 = (x0 - (double) offsetSecondCornerX) + G2; // Offsets for middle corner in (x,y) unskewed coords 96 | double y1 = (y0 - (double) offsetSecondCornerZ) + G2; 97 | double x2 = (x0 - 1.0) + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords 98 | double y2 = (y0 - 1.0) + 2.0 * G2; 99 | 100 | // Work out the hashed gradient indices of the three simplex corners 101 | uint32_t ii = (uint32_t) xHairy & 0xffu; 102 | uint32_t jj = (uint32_t) zHairy & 0xffu; 103 | uint8_t gi0 = permutations[ii + permutations[jj]] % 12u; 104 | uint8_t gi1 = permutations[ii + offsetSecondCornerX + permutations[jj + offsetSecondCornerZ]] % 12u; 105 | uint8_t gi2 = permutations[ii + 1 + permutations[jj + 1]] % 12u; 106 | 107 | // Calculate the contribution from the three corners 108 | double t0 = 0.5 - x0 * x0 - y0 * y0; 109 | double n0; 110 | if (t0 < 0.0) { 111 | n0 = 0.0; 112 | } else { 113 | t0 *= t0; 114 | n0 = t0 * t0 * ((double) grad2[gi0][0] * x0 + (double) grad2[gi0][1] * y0); // (x,y) of grad2 used for 2D gradient 115 | } 116 | double t1 = 0.5 - x1 * x1 - y1 * y1; 117 | double n1; 118 | if (t1 < 0.0) { 119 | n1 = 0.0; 120 | } else { 121 | t1 *= t1; 122 | n1 = t1 * t1 * ((double) grad2[gi1][0] * x1 + (double) grad2[gi1][1] * y1); 123 | } 124 | double t2 = 0.5 - x2 * x2 - y2 * y2; 125 | double n2; 126 | if (t2 < 0.0) { 127 | n2 = 0.0; 128 | } else { 129 | t2 *= t2; 130 | n2 = t2 * t2 * ((double) grad2[gi2][0] * x2 + (double) grad2[gi2][1] * y2); 131 | } 132 | // Add contributions from each corner to get the final noise value. 133 | // The result is scaled to return values in the interval [-1,1]. 134 | (*buffer)[k] = (*buffer)[k] + 70.0 * (n0 + n1 + n2) * octaveFactor; 135 | k++; 136 | 137 | } 138 | 139 | } 140 | } 141 | } 142 | #endif //SEEDFINDING_SIMPLEX_H 143 | -------------------------------------------------------------------------------- /include/util.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) The Minecraft Seed Finding Team 2 | // version 1.0.1 3 | // MIT License 4 | 5 | #ifndef LIBSEEDFINDING_UTIL_H 6 | #define LIBSEEDFINDING_UTIL_H 7 | 8 | #ifdef _MSVC_LANG 9 | #define CPP_VER _MSVC_LANG 10 | #else 11 | #define CPP_VER __cplusplus 12 | #endif 13 | 14 | #if defined(__CUDACC__) 15 | #define FORCEINLINE __forceinline__ 16 | #elif defined(__GNUC__) 17 | #define FORCEINLINE __attribute__((always_inline)) 18 | #elif defined(_MSC_VER) 19 | #define FORCEINLINE __forceinline 20 | #else 21 | #warning Using unknown compiler, various optimizations may not be enabled 22 | #define FORCEINLINE 23 | #endif 24 | 25 | #ifdef __CUDACC__ 26 | #define DEVICEABLE __host__ __device__ 27 | #else 28 | #define DEVICEABLE 29 | #endif 30 | 31 | #ifdef __CUDA_ARCH__ 32 | #define DEVICE_FORCEINLINE FORCEINLINE 33 | #define HOST_FORCEINLINE 34 | #define DEVICEABLE_CONST __constant__ __device__ 35 | #else 36 | #define DEVICE_FORCEINLINE 37 | #define HOST_FORCEINLINE FORCEINLINE 38 | #define DEVICEABLE_CONST const 39 | #endif 40 | 41 | #endif //LIBSEEDFINDING_UTIL_H 42 | -------------------------------------------------------------------------------- /include/version.h.template: -------------------------------------------------------------------------------- 1 | // Copyright (c) The Minecraft Seed Finding Team 2 | // version 1.0.1 3 | // MIT License 4 | 5 | #ifndef SEEDFINDING_VERSION_H 6 | #define SEEDFINDING_VERSION_H 7 | #include 8 | #include "util.h" 9 | 10 | namespace version { 11 | enum Type { 12 | RELEASE, 13 | SNAPSHOT, 14 | OLD_ALPHA, 15 | OLD_BETA 16 | }; 17 | struct Version { 18 | int id; 19 | Type type; 20 | std::string releaseTime; 21 | }; 22 | 23 | DEVICEABLE inline bool operator==(const version::Version version1, const version::Version version2) { 24 | return version1.id == version2.id; 25 | } 26 | 27 | DEVICEABLE inline bool operator>(const version::Version version1, const version::Version version2) { 28 | return version1.id > version2.id; 29 | } 30 | 31 | DEVICEABLE inline bool operator<(const version::Version version1, const version::Version version2) { 32 | return version1.id < version2.id; 33 | } 34 | 35 | DEVICEABLE inline bool operator>=(const version::Version version1, const version::Version version2) { 36 | return version1.id >= version2.id; 37 | } 38 | 39 | DEVICEABLE inline bool operator<=(const version::Version version1, const version::Version version2) { 40 | return version1.id <= version2.id; 41 | } 42 | 43 | DEVICEABLE inline bool operator!=(const version::Version version1, const version::Version version2) { 44 | return version1.id != version2.id; 45 | } 46 | 47 | DEVICEABLE inline bool is_release(const version::Version version) { 48 | return version.type == RELEASE; 49 | } 50 | 51 | DEVICEABLE inline bool is_snapshot(const version::Version version) { 52 | return version.type == SNAPSHOT; 53 | } 54 | 55 | DEVICEABLE inline bool is_beta(const version::Version version) { 56 | return version.type == OLD_BETA; 57 | } 58 | 59 | DEVICEABLE inline bool is_alpha(const version::Version version) { 60 | return version.type == OLD_ALPHA; 61 | } 62 | 63 | /////////////////////////GENERATION//////////////////////////////// 64 | 65 | 66 | } 67 | #endif //SEEDFINDING_VERSION_H 68 | -------------------------------------------------------------------------------- /install.bat: -------------------------------------------------------------------------------- 1 | mkdir build 2 | cd build 3 | cmake .. 4 | cmake --build . --config Release --target install -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | mkdir -p build 3 | # shellcheck disable=SC2164 4 | cd build 5 | cmake -DCMAKE_BUILD_TYPE=Release .. 6 | sudo cmake --build . --target install -------------------------------------------------------------------------------- /libseedfindingConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # Generates a macro to auto-configure everything 2 | @PACKAGE_INIT@ 3 | 4 | # Setting these here so they're accessible after install. 5 | # Might be useful for some users to check which settings were used. 6 | set(LIBSEEDFINDING_IS_USING_CUDA @LIBSEEDFINDING_IS_USING_CUDA@) 7 | set(LIBSEEDFINDING_IS_COMPILED @LIBSEEDFINDING_COMPILE@) 8 | set(LIBSEEDFINDING_VERSION @PROJECT_VERSION@) 9 | 10 | include(CMakeFindDependencyMacro) 11 | 12 | # We add find_dependency calls here to not make the end-user have to call them. 13 | if(@LIBSEEDFINDING_IS_USING_CUDA@) 14 | enable_language(CUDA) 15 | find_dependency(CUDA REQUIRED) 16 | endif() 17 | 18 | # Mildly useful for end-users 19 | # Not really recommended to be used though 20 | set_and_check(LIBSEEDFINDING_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@") 21 | # Lets the end-user find the header path with the header appended 22 | # This is helpful if you're using Cmake's pre-compiled header feature 23 | foreach(header @LIBSEEDFINDING_HEADERS@) 24 | set_and_check(LIBSEEDFINDING_HEADER_PATH "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@/${header}") 25 | endforeach(header) 26 | # Brings in the target library 27 | include("${CMAKE_CURRENT_LIST_DIR}/libseedfindingTargets.cmake") 28 | 29 | # Ouputs a "found lcg /usr/include/lcg.h ..." message when using find_package(LIBSEEDFINDING) 30 | include(FindPackageMessage) 31 | if(TARGET libseedfinding::libseedfinding) 32 | set(LIBSEEDFINDING_FOUND TRUE) 33 | 34 | # Since the compiled version has a lib, show that in the message 35 | if(@LIBSEEDFINDING_COMPILE@) 36 | # The list of configurations is most likely just 1 unless they installed a debug & release 37 | get_target_property(_libseedfinding_configs libseedfinding::libseedfinding "IMPORTED_CONFIGURATIONS") 38 | # Need to loop since the "IMPORTED_LOCATION" property isn't want we want. 39 | # Instead, we need to find the IMPORTED_LOCATION_RELEASE or IMPORTED_LOCATION_DEBUG which has the lib path. 40 | foreach(_libseedfinding_conf "${_libseedfinding_configs}") 41 | # Grab the path to the lib and sets it to LIBSEEDFINDING_LIBRARY 42 | get_target_property(LIBSEEDFINDING_LIBRARY libseedfinding::libseedfinding "IMPORTED_LOCATION_${_libseedfinding_conf}") 43 | # Check if we found it 44 | if(LIBSEEDFINDING_LIBRARY) 45 | break() 46 | endif() 47 | endforeach() 48 | 49 | unset(_libseedfinding_configs) 50 | unset(_libseedfinding_conf) 51 | 52 | find_package_message(libseedfinding "Found libseedfinding: ${LIBSEEDFINDING_LIBRARY} (found version \"${LIBSEEDFINDING_VERSION}\")" "[${LIBSEEDFINDING_LIBRARY}][${LIBSEEDFINDING_HEADER_PATH}]") 53 | else() 54 | find_package_message(libseedfinding "Found libseedfinding: ${LIBSEEDFINDING_HEADER_PATH} (found version \"${LIBSEEDFINDING_VERSION}\")" "[${LIBSEEDFINDING_HEADER_PATH}]") 55 | endif() 56 | endif() 57 | -------------------------------------------------------------------------------- /test_cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(test test.cpp) -------------------------------------------------------------------------------- /test_cpp/test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) The Minecraft Seed Finding Team 2 | // 3 | // MIT License 4 | 5 | #include "../include/lcg.h" 6 | #include "../include/version.h" 7 | #include 8 | 9 | #define ASSERT(expr) if (!(expr)) std::cerr << "Assertion failed: " #expr " at line " __FILE__ ":" << __LINE__ << std::endl; 10 | #define Abs(x) ((x) < 0 ? -(x) : (x)) 11 | #define Max(a, b) ((a) > (b) ? (a) : (b)) 12 | 13 | void test_lcg() { 14 | ASSERT(lcg::dfz2seed(47) == 0x2FC0CFC54419LL); 15 | ASSERT(lcg::seed2dfz(0x2FC0CFC54419LL) == 47); 16 | } 17 | 18 | double RelDif(double a, double b) { 19 | double c = Abs(a); 20 | double d = Abs(b); 21 | d = Max(c, d); 22 | return d == 0.0 ? 0.0 : Abs(a - b) / d; 23 | } 24 | 25 | void test_random() { 26 | lcg::Random random = 1; 27 | ASSERT(RelDif(lcg::next_double(random), 8.95818e-05) <= 0.000001); 28 | } 29 | 30 | void test_version() { 31 | ASSERT(version::MC_1_16.id == 530); 32 | } 33 | 34 | void test_version_cmp() { 35 | ASSERT(version::MC_1_16 > version::MC_1_15); 36 | } 37 | 38 | int main() { 39 | test_lcg(); 40 | test_random(); 41 | test_version(); 42 | test_version_cmp(); 43 | std::cout << "All tests have been run" << std::endl; 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /test_cuda/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(test test.cu) -------------------------------------------------------------------------------- /test_cuda/test.cu: -------------------------------------------------------------------------------- 1 | // Copyright (c) The Minecraft Seed Finding Team 2 | // 3 | // MIT License 4 | 5 | #include "../include/lcg.h" 6 | #include "../include/version.h" 7 | #include 8 | 9 | #define ASSERT(expr) if (!(expr)) std::cerr << "Assertion failed: " #expr " at line " __FILE__ ":" << __LINE__ << std::endl; 10 | #define Abs(x) ((x) < 0 ? -(x) : (x)) 11 | #define Max(a, b) ((a) > (b) ? (a) : (b)) 12 | 13 | void test_lcg() { 14 | ASSERT(lcg::dfz2seed(47) == 0x2FC0CFC54419LL); 15 | ASSERT(lcg::seed2dfz(0x2FC0CFC54419LL) == 47); 16 | } 17 | 18 | double RelDif(double a, double b) { 19 | double c = Abs(a); 20 | double d = Abs(b); 21 | d = Max(c, d); 22 | return d == 0.0 ? 0.0 : Abs(a - b) / d; 23 | } 24 | 25 | void test_random() { 26 | lcg::Random random = 1; 27 | ASSERT(RelDif(lcg::next_double(random), 8.95818e-05) <= 0.000001); 28 | } 29 | 30 | void test_version() { 31 | ASSERT(version::MC_1_16.id == 530); 32 | } 33 | 34 | void test_version_cmp() { 35 | ASSERT(version::MC_1_16 > version::MC_1_15); 36 | } 37 | 38 | int main() { 39 | test_lcg(); 40 | test_random(); 41 | test_version(); 42 | test_version_cmp(); 43 | std::cout << "All tests have been run" << std::endl; 44 | return 0; 45 | } 46 | --------------------------------------------------------------------------------