diff --git a/.github/workflows/cpp-linter.yml b/.github/workflows/cpp-linter.yml index f576c835..a26fdee5 100644 --- a/.github/workflows/cpp-linter.yml +++ b/.github/workflows/cpp-linter.yml @@ -48,7 +48,7 @@ jobs: tidy-checks: "" step-summary: true file-annotations: true - ignore: subprojects|build|android|assets|recordings|docs|toolchains|platforms|src/thirdparty + ignore: subprojects|build|android|assets|recordings|docs|toolchains|platforms|wrapper|src/libs/core/hash-library - name: Fail CI run if linter checks failed if: steps.linter.outputs.checks-failed != 0 diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml index 716db255..a0e81d00 100644 --- a/.github/workflows/flatpak.yml +++ b/.github/workflows/flatpak.yml @@ -21,3 +21,4 @@ jobs: with: bundle: oopetris.flatpak manifest-path: com.github.mgerhold.OOPetris.yml + verbose: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/meson.yml b/.github/workflows/meson.yml index ade23f75..b3b9f5d9 100644 --- a/.github/workflows/meson.yml +++ b/.github/workflows/meson.yml @@ -177,7 +177,7 @@ jobs: if: matrix.config.os == 'macos' run: | brew update - brew install ninja sdl2 sdl2_ttf sdl2_mixer sdl2_image + brew install sdl2 sdl2_ttf sdl2_mixer sdl2_image - name: Configure run: meson setup build -Dbuildtype=${{ matrix.config.buildtype }} -Ddefault_library=${{ matrix.config.library_type }} -Dclang_libcpp=${{ ( ( matrix.config.os == 'ubuntu' && matrix.config.use-clang == true && matrix.config.use-clang_stdlib ) || matrix.config.os == 'macos' ) && 'enabled' || 'disabled' }} @@ -185,24 +185,8 @@ jobs: - name: Build run: meson compile -C build - - name: Upload artifacts - Linux + - name: Upload artifacts uses: actions/upload-artifact@v4 - if: matrix.config.os == 'ubuntu' - with: - name: ${{ matrix.config.name }} Executable - path: build/oopetris - - - name: Upload artifacts - MacOS - uses: actions/upload-artifact@v4 - if: matrix.config.os == 'macos' - with: - name: ${{ matrix.config.name }} Executable - path: build/oopetris - # TODO: create a proper installer: https://mesonbuild.com/Creating-OSX-packages.html - - - name: Upload artifacts - Windows - uses: actions/upload-artifact@v4 - if: matrix.config.os == 'windows' with: name: ${{ matrix.config.name }} Executable - path: build/oopetris.exe + path: build/src/executables/oopetris* diff --git a/.github/workflows/wrapper.yml b/.github/workflows/wrapper.yml new file mode 100644 index 00000000..5f5bf15d --- /dev/null +++ b/.github/workflows/wrapper.yml @@ -0,0 +1,124 @@ +name: Wrapper CI + +on: + push: + branches: ["main"] + pull_request: + workflow_dispatch: + +jobs: + build: + name: ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }}-${{ matrix.config.os-version }} + + strategy: + fail-fast: false + matrix: + config: + - name: Windows + os: windows + os-version: 2022 + + - name: Linux + os: ubuntu + os-version: 24.04 + + - name: MacOS + os: macos + os-version: 13 + + - name: MacOS (Arm64) + os: macos + os-version: 14 + + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: "0" + + - name: Setup MSVC (Windows) + if: matrix.config.os == 'windows' + uses: TheMrMilchmann/setup-msvc-dev@v3 + with: + arch: x64 + toolset: 14.39 + + - name: Setup GCC (Linux) + if: matrix.config.os == 'ubuntu' + uses: egor-tensin/setup-gcc@v1 + with: + version: 14 + platform: x64 + + - name: Setup Clang (MacOS) + if: matrix.config.os == 'macos' + run: | + brew update + brew install llvm@18 + echo "$(brew --prefix)/opt/llvm/bin" >> $GITHUB_PATH + echo "LDFLAGS=-L$(brew --prefix)/opt/llvm/lib -L$(brew --prefix)/opt/llvm/lib/c++ -Wl,-rpath,$(brew --prefix)/opt/llvm/lib/c++" >> "$GITHUB_ENV" + echo "CPPFLAGS=-I$(brew --prefix)/opt/llvm/include" >> "$GITHUB_ENV" + echo "CC=clang" >> "$GITHUB_ENV" + echo "CXX=clang++" >> "$GITHUB_ENV" + echo "OBJC=clang" >> "$GITHUB_ENV" + echo "CC_LD=lld" >> "$GITHUB_ENV" + echo "CXX_LD=lld" >> "$GITHUB_ENV" + echo "OBJC_LD=lld" >> "$GITHUB_ENV" + + - name: Setup meson (MacOS) + if: matrix.config.os == 'macos' + run: | + brew update + brew install meson + + # NOTE: meson has no dependencies, so --break-system-packages doesn't really break anything! + - name: Setup meson + if: matrix.config.os != 'macos' + run: | + pip install meson --break-system-packages + + - name: Install dependencies (Linux) + if: matrix.config.os == 'ubuntu' + run: | + sudo apt-get update + sudo apt-get install ninja-build -y + sudo pip install meson --break-system-packages + + - name: Fix pkg-config (Windows) + if: matrix.config.os == 'windows' + run: | + Remove-Item -Path C:\Strawberry\ -Recurse + choco install pkgconfiglite + echo "PKG_CONFIG_PATH=C:/lib/pkgconfig" | Out-File -FilePath $env:GITHUB_ENV -Append + + - name: Configure + run: meson setup build -Dbuildtype=release -Ddefault_library=static -Dclang_libcpp=${{ matrix.config.os == 'macos' && 'enabled' || 'disabled' }} -Donly_build_libs=true ${{ matrix.config.os == 'windows' && '-Db_vscrt=static_from_buildtype' || '' }} + + - name: Build and install Libs + if: matrix.config.os != 'ubuntu' + run: meson install -C build + + - name: Build and install Libs (Linux) + if: matrix.config.os == 'ubuntu' + run: sudo meson install -C build + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Build package + run: | + cd wrapper/javascript + npm install -D + npm run build --verbose + npm run test + npm pack + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.config.name }} Node.js Wrapper + path: wrapper/javascript/oopetris*.tgz + \ No newline at end of file diff --git a/meson.build b/meson.build index 5d93863e..cf65a850 100644 --- a/meson.build +++ b/meson.build @@ -14,9 +14,6 @@ project( version: '0.5.6', ) -oopetris_author = 'Coder2k' -oopetris_name = 'OOPetris' - subdir('tools/options') subdir('tools/dependencies') @@ -25,87 +22,7 @@ subdir('src') subdir('tools/install') -if meson.is_cross_build() and host_machine.system() == 'android' - - library( - 'oopetris', - main_files, - dependencies: [liboopetris_graphics_dep, graphic_application_deps], - override_options: { - 'warning_level': '3', - 'werror': true, - }, - ) - -elif meson.is_cross_build() and host_machine.system() == 'switch' - switch_options = [ - app_name, - main_files, - [liboopetris_graphics_dep, graphic_application_deps], - ] - subdir('platforms/switch') -elif meson.is_cross_build() and host_machine.system() == '3ds' - _3ds_options = [ - app_name, - main_files, - [liboopetris_graphics_dep, graphic_application_deps], - ] - subdir('platforms/3ds') -else - - if host_machine.system() == 'windows' - subdir('platforms/windows') - endif - - oopetris_exe = executable( - 'oopetris', - main_files, - dependencies: [liboopetris_graphics_dep, graphic_application_deps], - override_options: { - 'warning_level': '3', - 'werror': true, - }, - install: true, - win_subsystem: 'windows', - ) - - oopetris_recordings_utility_exe = executable( - 'oopetris_recordings_utility', - recordings_main_files, - dependencies: liboopetris_recordings_dep, - override_options: { - 'warning_level': '3', - 'werror': true, - }, - install: true, - win_subsystem: 'console', - ) - - if build_installer - if host_machine.system() == 'windows' - - makensis = find_program('makensis') - - nsis_script = meson.project_source_root() / 'tools' / 'installer' / 'setup.nsi' - - run_target( - 'windows_installer', - command: [ - makensis, - '-DVERSION=' + meson.project_version(), - '-DNAME=' + oopetris_name, - '-DAUTHOR=' + oopetris_author, - '-DPROJECT_SOURCE_DIR=' + meson.project_source_root(), - '-DPROJECT_BUILD_DIR=' + meson.project_build_root(), - nsis_script, - ], - depends: [oopetris_exe, oopetris_recordings_utility_exe], - ) - - endif - endif - -endif +subdir('src/executables') if get_option('tests') subdir('tests') diff --git a/meson.options b/meson.options index b7888d6d..5593a8ea 100644 --- a/meson.options +++ b/meson.options @@ -18,3 +18,11 @@ option( value: false, description: 'whether or not tests should be built', ) + +option( + 'only_build_libs', + type: 'boolean', + value: false, + description: 'if you only want to build the libs, enable this', +) + diff --git a/platforms/android/app/build.gradle b/platforms/android/app/build.gradle index 9f8c3279..f7b96c63 100644 --- a/platforms/android/app/build.gradle +++ b/platforms/android/app/build.gradle @@ -72,6 +72,13 @@ static boolean isValidVersion(String version) { return true; } +/** + * Internal helper function + */ +static List getSupportedABIs() { + return ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"]; +} + /** * Read the Android ABI from user input. * supported ANDROID_ABIs are 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64' @@ -80,7 +87,7 @@ static boolean isValidVersion(String version) { List getAndroidABIs() { String property = project.findProperty('ANDROID_ABI') - List supportedABIs = ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"]; + List supportedABIs = getSupportedABIs() List AbiFilters = new ArrayList() if (property == null) { @@ -193,8 +200,37 @@ String getVersion() { } +/** + * Detect if we need to build an universal apk + * this first checks, if this is manually request by the cli args, than it checks, if all supported ABIs are given, if that is the case, enable it too + * @return Boolean + */ +Boolean shouldBuildUniversalApk(List abisToUse) { + String property = project.findProperty('BUILD_UNIVERSAL_APK') + + + if (property != null) { + return true + } + + List supportedABIs = getSupportedABIs() + + // return true, if all abis, we support are specified + for (abi in supportedABIs) { + if (!abisToUse.contains(abi)) { + return false; + } + } + + return true; + + +} + + List abisToUse = getAndroidABIs() String versionString = getVersion() +Boolean buildUniversalApk = shouldBuildUniversalApk(abisToUse) System.out.printf("DEBUG: Using abis: %s%n", abisToUse.join(", ")) System.out.printf("DEBUG: Using version: %s%n", versionString) @@ -271,7 +307,7 @@ android { // Specifies a list of ABIs for Gradle to create APKs for. include(*abisToUse) // Specifies that you don't want to also generate a universal APK that includes all ABIs. - universalApk false + universalApk(buildUniversalApk) } } diff --git a/platforms/android/app/jni/Android.mk b/platforms/android/app/jni/Android.mk index 38fd350d..9e12052d 100644 --- a/platforms/android/app/jni/Android.mk +++ b/platforms/android/app/jni/Android.mk @@ -84,14 +84,14 @@ include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := liboopetris_core -LIB_PATH := $(BUILD_PATH)/src +LIB_PATH := $(BUILD_PATH)/src/libs/core LOCAL_SRC_FILES := $(LIB_PATH)/liboopetris_core.so include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := liboopetris_recordings -LIB_PATH := $(BUILD_PATH)/src +LIB_PATH := $(BUILD_PATH)/src/libs/recordings LOCAL_SRC_FILES := $(LIB_PATH)/liboopetris_recordings.so include $(PREBUILT_SHARED_LIBRARY) @@ -105,7 +105,8 @@ include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := liboopetris -LOCAL_SRC_FILES := $(BUILD_PATH)/liboopetris.so +LIB_PATH := $(BUILD_PATH)/src/executables +LOCAL_SRC_FILES := $(LIB_PATH)/liboopetris.so include $(PREBUILT_SHARED_LIBRARY) diff --git a/platforms/android/build.gradle b/platforms/android/build.gradle index 70aed58f..0c4c2c36 100644 --- a/platforms/android/build.gradle +++ b/platforms/android/build.gradle @@ -6,7 +6,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:8.4.0' + classpath 'com.android.tools.build:gradle:8.4.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/src/discord/core.cpp b/src/discord/core.cpp index 5b0c5c4f..72dcd3b4 100644 --- a/src/discord/core.cpp +++ b/src/discord/core.cpp @@ -1,9 +1,8 @@ +#include +#include #include "core.hpp" -#include "helper/magic_enum_wrapper.hpp" -#include "helper/utils.hpp" -#include "types.h" #include #include diff --git a/src/discord/core.hpp b/src/discord/core.hpp index ffad0d68..c7b3db05 100644 --- a/src/discord/core.hpp +++ b/src/discord/core.hpp @@ -2,7 +2,7 @@ #pragma once -#include "helper/expected.hpp" +#include #include #ifdef _WIN32 diff --git a/src/application.cpp b/src/executables/game/application.cpp similarity index 93% rename from src/application.cpp rename to src/executables/game/application.cpp index f6f20c0e..d0fee17d 100644 --- a/src/application.cpp +++ b/src/executables/game/application.cpp @@ -1,8 +1,10 @@ +#include +#include +#include + #include "application.hpp" -#include "helper/errors.hpp" -#include "helper/magic_enum_wrapper.hpp" +#include "helper/graphic_utils.hpp" #include "helper/message_box.hpp" -#include "helper/sleep.hpp" #include "input/input.hpp" #include "manager/music_manager.hpp" #include "scenes/loading_screen/loading_screen.hpp" @@ -35,8 +37,8 @@ namespace { } // namespace -Application::Application(std::shared_ptr&& window, const std::vector& arguments) try - : m_command_line_arguments{ arguments }, +Application::Application(std::shared_ptr&& window, CommandLineArguments&& arguments) try + : m_command_line_arguments{ std::move(arguments) }, m_window{ std::move(window) }, m_renderer{ *m_window, m_command_line_arguments.target_fps.has_value() ? Renderer::VSync::Disabled : Renderer::VSync::Enabled }, @@ -117,6 +119,11 @@ void Application::handle_event(const SDL_Event& event) { m_is_running = false; } + // special event for android and IOS + if (event.type == SDL_APP_TERMINATING) { + m_is_running = false; + } + auto handled = false; for (const auto& scene : std::ranges::views::reverse(m_scene_stack)) { @@ -299,7 +306,6 @@ void Application::initialize() { : 0s; auto start_execution_time = std::chrono::steady_clock::now(); - bool finished_loading = false; // this is a duplicate of below in some cases, but it's just for the loading screen and can't be factored out easily @@ -322,6 +328,15 @@ void Application::initialize() { if (event.type == SDL_QUIT) { m_is_running = false; } + + // special event for android and IOS + if (event.type == SDL_APP_TERMINATING) { + m_is_running = false; + } + } + + if (not m_is_running) { + break; } loading_screen.update(); @@ -353,13 +368,14 @@ void Application::initialize() { const auto duration = std::chrono::milliseconds(SDL_GetTicks64() - start_time); - // we can reach this via SDL_QUIT or (not console::inMainLoop()) - if (not finished_loading) { + // we can reach this via SDL_QUIT, SDL_APP_TERMINATING or (not console::inMainLoop()) + if (not finished_loading or not m_is_running) { spdlog::debug("Aborted loading after {}", duration); - // just exit immediately, without cleaning up, since than we would have to cancel the loading thread somehow, which is way rto complicated, let the OS clean up our mess we create her xD - std::exit(0); + // just exit immediately, without cleaning up, since than we would have to cancel the loading thread somehow, which is way to complicated, let the OS clean up our mess we created here xD + + utils::exit(0); } @@ -389,11 +405,11 @@ void Application::load_resources() { #if defined(_HAVE_DISCORD_SDK) -[[nodiscard]] helper::optional& Application::discord_instance() { +[[nodiscard]] std::optional& Application::discord_instance() { return m_discord_instance; } -[[nodiscard]] const helper::optional& Application::discord_instance() const { +[[nodiscard]] const std::optional& Application::discord_instance() const { return m_discord_instance; } diff --git a/src/application.hpp b/src/executables/game/application.hpp similarity index 87% rename from src/application.hpp rename to src/executables/game/application.hpp index 676e910c..69b1a981 100644 --- a/src/application.hpp +++ b/src/executables/game/application.hpp @@ -1,9 +1,9 @@ #pragma once +#include + #include "graphics/renderer.hpp" #include "graphics/window.hpp" -#include "helper/command_line_arguments.hpp" -#include "helper/types.hpp" #include "input/input.hpp" #include "manager/event_dispatcher.hpp" #include "manager/event_listener.hpp" @@ -25,7 +25,7 @@ struct Application final : public EventListener, public ServiceProvider { CommandLineArguments m_command_line_arguments; std::shared_ptr m_window; Renderer m_renderer; - helper::optional m_target_framerate; + std::optional m_target_framerate; // these fields are initalized asynchronously in a separate thread std::unique_ptr m_music_manager; @@ -39,7 +39,7 @@ struct Application final : public EventListener, public ServiceProvider { #endif #if defined(_HAVE_DISCORD_SDK) - helper::optional m_discord_instance{ helper::nullopt }; + std::optional m_discord_instance{ std::nullopt }; #endif protected: @@ -49,7 +49,7 @@ struct Application final : public EventListener, public ServiceProvider { std::vector> m_scene_stack; public: - Application(std::shared_ptr&& window, const std::vector& arguments); + Application(std::shared_ptr&& window, CommandLineArguments&& arguments); Application(const Application&) = delete; Application& operator=(const Application&) = delete; @@ -130,8 +130,8 @@ struct Application final : public EventListener, public ServiceProvider { #if defined(_HAVE_DISCORD_SDK) - [[nodiscard]] helper::optional& discord_instance() override; - [[nodiscard]] const helper::optional& discord_instance() const override; + [[nodiscard]] std::optional& discord_instance() override; + [[nodiscard]] const std::optional& discord_instance() const override; #endif diff --git a/src/executables/game/main.cpp b/src/executables/game/main.cpp new file mode 100644 index 00000000..a58daae8 --- /dev/null +++ b/src/executables/game/main.cpp @@ -0,0 +1,148 @@ + + +#include "./parser.hpp" + +#include +#include + +#include "application.hpp" +#include "helper/constants.hpp" +#include "helper/graphic_utils.hpp" +#include "helper/message_box.hpp" + +#include +#include +#include +#include +#include + +#if defined(__ANDROID__) +#include +#endif + +#if defined(__CONSOLE__) +#include "helper/console_helpers.hpp" +#endif + + +#include +#include +#include + + +namespace { + void initialize_spdlog() { + + const auto logs_path = utils::get_root_folder() / "logs"; + if (not std::filesystem::exists(logs_path)) { + std::filesystem::create_directory(logs_path); + } + + std::vector sinks; +#if defined(__ANDROID__) + sinks.push_back(std::make_shared()); +#elif defined(__CONSOLE__) + sinks.push_back(std::make_shared()); +#else + sinks.push_back(std::make_shared()); +#endif + sinks.push_back(std::make_shared( + fmt::format("{}/oopetris.log", logs_path.string()), 1024 * 1024 * 10, 5, true + )); + auto combined_logger = std::make_shared("combined_logger", begin(sinks), end(sinks)); + spdlog::set_default_logger(combined_logger); + +#if !defined(NDEBUG) + spdlog::set_level(spdlog::level::debug); +#else + spdlog::set_level(spdlog::level::err); +#endif + } + + + int main_no_sdl_replace(int argc, char** argv) noexcept { + + std::shared_ptr window{ nullptr }; + + try { + + initialize_spdlog(); + + std::vector arguments_vector{}; + arguments_vector.reserve(argc); + for (auto i = 0; i < argc; ++i) { + arguments_vector.emplace_back(argv[i]); //NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + + if (arguments_vector.empty()) { + arguments_vector.emplace_back("oopetris"); + } + + auto parsed_arguments = helper::parse_args(arguments_vector); + + if (not parsed_arguments.has_value()) { + + spdlog::error("error parsing command line arguments: {}", parsed_arguments.error()); + return EXIT_FAILURE; + } + + auto arguments = std::move(parsed_arguments.value()); + + [[maybe_unused]] constexpr auto window_name = constants::program_name.c_str(); + + + try { +#if defined(__ANDROID__) or defined(__CONSOLE__) + window = std::make_shared(window_name, WindowPosition::Centered); +#else + [[maybe_unused]] static constexpr int width = 1280; + [[maybe_unused]] static constexpr int height = 720; + window = std::make_shared(window_name, WindowPosition::Centered, width, height); +#endif + } catch (const helper::GeneralError& general_error) { + spdlog::error("Couldn't initialize window: {}", general_error.message()); + } + + if (window == nullptr) { + helper::MessageBox::show_simple( + helper::MessageBox::Type::Error, "Initialization Error", "failed to create SDL window", nullptr + ); + return EXIT_FAILURE; + } + + Application app{ std::move(window), std::move(arguments) }; + + app.run(); + return EXIT_SUCCESS; + + } catch (const helper::GeneralError& general_error) { + spdlog::error("{}", general_error.message()); + + + if (window == nullptr) { + helper::MessageBox::show_simple( + helper::MessageBox::Type::Error, "Initialization Error", general_error.message(), nullptr + ); + } else { + window->show_simple(helper::MessageBox::Type::Error, "Initialization Error", general_error.message()); + } + + + return EXIT_FAILURE; + } catch (const utils::ExitException& exit_exception) { + spdlog::debug("Requested exit with status code {}", exit_exception.status_code()); + return exit_exception.status_code(); + } catch (const std::exception& error) { + // this is the last resort, so using std::cerr and no sdl show_simple messagebox! + std::cerr << error.what(); + return EXIT_FAILURE; + } + } + + +} // namespace + + +int main(int argc, char** argv) { + return main_no_sdl_replace(argc, argv); +} diff --git a/src/executables/game/meson.build b/src/executables/game/meson.build new file mode 100644 index 00000000..90b4c0b2 --- /dev/null +++ b/src/executables/game/meson.build @@ -0,0 +1,7 @@ +main_files += files( + 'application.cpp', + 'application.hpp', + 'main.cpp', + 'parser.cpp', + 'parser.hpp', +) diff --git a/src/executables/game/parser.cpp b/src/executables/game/parser.cpp new file mode 100644 index 00000000..591a5e31 --- /dev/null +++ b/src/executables/game/parser.cpp @@ -0,0 +1,63 @@ + +#include "parser.hpp" + +#include + +#include "game/command_line_arguments.hpp" + +#include "helper/constants.hpp" +#include "helper/graphic_utils.hpp" + + +#include +#include + + +helper::expected helper::parse_args(const std::vector& arguments) { + argparse::ArgumentParser parser{ constants::program_name, constants::version, argparse::default_arguments::all }; + parser.add_argument("-r", "--recording").help("the path of a recorded game used for replay"); + parser.add_argument("-f", "--target-fps").help("the number of simulation steps per second").scan<'i', u32>(); + parser.add_argument("-l", "--level") + .help("the starting level of the game") + .scan<'i', CommandLineArguments::Level>() + .default_value(CommandLineArguments::default_starting_level); + parser.add_argument("-s", "--silent").help("disable audio output").default_value(false).implicit_value(true); + try { + parser.parse_args(arguments); + + CommandLineArguments result{ std::nullopt, std::nullopt }; + + + if (auto path = parser.present("--recording")) { + spdlog::info("recording is present"); + result.recording_path = utils::get_root_folder() / *path; + } + + const auto fps = parser.present("--target-fps"); + if (fps.has_value()) { + if (fps.value() >= 1) { + result.target_fps = fps; + } else { + spdlog::error("invalid value for target fps ({}), using default value instead (VSYNC)", fps.value()); + } + } + + const auto level = parser.get("--level"); + if (level <= 30) { + result.starting_level = level; + } else { + spdlog::error( + "invalid value for starting level ({}), using default value instead ({})", level, + CommandLineArguments::default_starting_level + ); + result.starting_level = CommandLineArguments::default_starting_level; + } + + result.silent = parser.get("--silent"); + + return result; + + } catch (const std::exception& error) { + return helper::unexpected{ error.what() }; + } +} diff --git a/src/executables/game/parser.hpp b/src/executables/game/parser.hpp new file mode 100644 index 00000000..790b35eb --- /dev/null +++ b/src/executables/game/parser.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "game/command_line_arguments.hpp" + + +#include +#include + + +namespace helper { + helper::expected parse_args(const std::vector& arguments); +} // namespace helper diff --git a/src/executables/meson.build b/src/executables/meson.build new file mode 100644 index 00000000..fb9b687c --- /dev/null +++ b/src/executables/meson.build @@ -0,0 +1,94 @@ +if build_application + + main_files = [] + + subdir('game') + + if meson.is_cross_build() and host_machine.system() == 'android' + + library( + 'oopetris', + main_files, + dependencies: [liboopetris_graphics_dep, graphic_application_deps], + override_options: { + 'warning_level': '3', + 'werror': true, + }, + ) + + elif meson.is_cross_build() and host_machine.system() == 'switch' + switch_options = [ + app_name, + main_files, + [liboopetris_graphics_dep, graphic_application_deps], + ] + subdir('platforms/switch') + elif meson.is_cross_build() and host_machine.system() == '3ds' + _3ds_options = [ + app_name, + main_files, + [liboopetris_graphics_dep, graphic_application_deps], + ] + subdir('platforms/3ds') + else + + if host_machine.system() == 'windows' + subdir('platforms/windows') + endif + + oopetris_exe = executable( + 'oopetris', + main_files, + dependencies: [liboopetris_graphics_dep, graphic_application_deps], + override_options: { + 'warning_level': '3', + 'werror': true, + }, + install: true, + win_subsystem: 'windows', + ) + + recordings_main_files = [] + + subdir('utility') + + oopetris_recordings_utility_exe = executable( + 'oopetris_recordings_utility', + recordings_main_files, + dependencies: [liboopetris_recordings_dep, recordings_application_deps], + override_options: { + 'warning_level': '3', + 'werror': true, + }, + install: true, + win_subsystem: 'console', + ) + + if build_installer + if host_machine.system() == 'windows' + + makensis = find_program('makensis') + + nsis_script = meson.project_source_root() / 'tools' / 'installer' / 'setup.nsi' + + run_target( + 'windows_installer', + command: [ + makensis, + '-DVERSION=' + meson.project_version(), + '-DNAME=' + oopetris_name, + '-DAUTHOR=' + oopetris_author, + '-DPROJECT_SOURCE_DIR=' + + meson.project_source_root(), + '-DPROJECT_BUILD_DIR=' + meson.project_build_root(), + nsis_script, + ], + depends: [oopetris_exe, oopetris_recordings_utility_exe], + ) + + endif + endif + + endif + +endif diff --git a/platforms/3ds/meson.build b/src/executables/platforms/3ds/meson.build similarity index 95% rename from platforms/3ds/meson.build rename to src/executables/platforms/3ds/meson.build index 8b05e307..f8524f36 100644 --- a/platforms/3ds/meson.build +++ b/src/executables/platforms/3ds/meson.build @@ -1,11 +1,9 @@ - ## get the options object and "unpack" it _3ds_exe_name = _3ds_options[0] _3ds_src_files = _3ds_options[1] _3ds_deps = _3ds_options[2] - # libraries _3ds_dependencies = [ @@ -29,7 +27,6 @@ _3ds_dependencies_native = [ 'SDL2main', ] - _3ds_library_dirs = meson.get_external_property('library_dirs', ['']) if _3ds_library_dirs.length() == 0 error('property \'library_dirs\' has to be set!') @@ -53,7 +50,6 @@ foreach dep : _3ds_dependencies_native ) endforeach - ## compilation _3ds_elf_file = build_target( @@ -72,7 +68,6 @@ use_smdh = ['true', 'True', '1', true].contains( meson.get_external_property('USE_SMDH', ''), ) - _3dsxtool = find_program('3dsxtool') _3DSX_FLAGS = [_3dsxtool, _3ds_elf_file.full_path(), _3ds_exe_name + '.3dsx'] @@ -95,7 +90,6 @@ if use_smdh endif SMDH_FLAGS += APP_DESC - APP_AUTHOR = meson.get_external_property('APP_AUTHOR', '') if APP_AUTHOR == '' error('If USE_SMDH is set, you have to provide an APP_AUTHOR') @@ -115,19 +109,13 @@ if use_smdh APP_ICON = meson.project_source_root() / APP_ICON endif - if not fs.exists(APP_ICON) error('APP_ICON should exist, but doesn\'t: \'' + APP_ICON + '\'') endif SMDH_FLAGS += APP_ICON - - SMDH_FLAGS += ( - _3ds_exe_name + '.smdh' # outfile - ) - - + SMDH_FLAGS += (_3ds_exe_name + '.smdh') APP_SMALL_ICON = meson.get_external_property('APP_SMALL_ICON', '') @@ -136,19 +124,17 @@ if use_smdh APP_SMALL_ICON = meson.project_source_root() / APP_SMALL_ICON endif - if not fs.exists(APP_SMALL_ICON) error( - 'APP_SMALL_ICON should exist, but doesn\'t: \'' + APP_SMALL_ICON + - '\'', + 'APP_SMALL_ICON should exist, but doesn\'t: \'' + + APP_SMALL_ICON + + '\'', ) endif SMDH_FLAGS += APP_SMALL_ICON endif - - # smdhtool --create <_3ds_exe_name>.smdh [] smdh_file = custom_target( _3ds_exe_name + '.smdh', @@ -161,8 +147,6 @@ if use_smdh endif - - APP_ROMFS = meson.get_external_property('APP_ROMFS', '') if APP_ROMFS != '' diff --git a/platforms/switch/meson.build b/src/executables/platforms/switch/meson.build similarity index 100% rename from platforms/switch/meson.build rename to src/executables/platforms/switch/meson.build diff --git a/platforms/windows/meson.build b/src/executables/platforms/windows/meson.build similarity index 95% rename from platforms/windows/meson.build rename to src/executables/platforms/windows/meson.build index 5d91bbdb..5a684a93 100644 --- a/platforms/windows/meson.build +++ b/src/executables/platforms/windows/meson.build @@ -25,4 +25,4 @@ oopetris_win_rc = configure_file( oopetris_resource_windows = windows.compile_resources(oopetris_win_rc) -main_files += oopetris_resource_windows \ No newline at end of file +main_files += oopetris_resource_windows diff --git a/platforms/windows/oopetris.rc.in b/src/executables/platforms/windows/oopetris.rc.in similarity index 100% rename from platforms/windows/oopetris.rc.in rename to src/executables/platforms/windows/oopetris.rc.in diff --git a/src/executables/utility/command_line_arguments.hpp b/src/executables/utility/command_line_arguments.hpp new file mode 100644 index 00000000..22be1a88 --- /dev/null +++ b/src/executables/utility/command_line_arguments.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + + +struct Dump { + bool ensure_ascii; + bool pretty_print; +}; + +struct Info { }; + + +struct CommandLineArguments final { +private: +public: + std::filesystem::path recording_path; + std::variant value; + + + template + CommandLineArguments(std::filesystem::path&& recording_path, T&& value) + : recording_path{ std::move(recording_path) }, + value{ std::forward(value) } { } + + template + CommandLineArguments(std::filesystem::path&& recording_path, const T& value) + : recording_path{ std::move(recording_path) }, + value{ value } { } + + + [[nodiscard]] static helper::expected from_args(int argc, char** argv) noexcept { + argparse::ArgumentParser parser{ argc >= 1 ? argv[0] //NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + : "oopetris_recording_utility", + "0.0.1", argparse::default_arguments::all }; + + + parser.add_argument("-r", "--recording").help("the path of a recorded game file").required(); + + + // git add subparser + argparse::ArgumentParser dump_parser("dump"); + dump_parser.add_description("Dump JSON value"); + dump_parser.add_argument("-a", "--ensure-ascii") + .help("Only use ASCII characters and escape sequences (\\uXXXX)") + .flag(); + dump_parser.add_argument("-p", "--pretty-print").help("Pretty print the JSON").flag(); + + argparse::ArgumentParser info_parser("info"); + info_parser.add_description("Print hHuman readable Info"); + + + parser.add_subparser(dump_parser); + parser.add_subparser(info_parser); + + try { + + parser.parse_args(argc, argv); + + auto recording_path = parser.get("--recording"); + + if (parser.is_subcommand_used(dump_parser)) { + const auto ensure_ascii = dump_parser.get("--ensure-ascii"); + const auto pretty_print = dump_parser.get("--pretty-print"); + + + return CommandLineArguments{ + std::move(recording_path), Dump{ .ensure_ascii = ensure_ascii, .pretty_print = pretty_print } + }; + } + + if (parser.is_subcommand_used(info_parser)) { + return CommandLineArguments{ + std::move(recording_path), + Info{}, + }; + } + + + return helper::unexpected{ "Unknown or no subcommand used" }; + + } catch (const std::exception& error) { + return helper::unexpected{ error.what() }; + } + } +}; diff --git a/src/executables/utility/main.cpp b/src/executables/utility/main.cpp new file mode 100644 index 00000000..2315bf8c --- /dev/null +++ b/src/executables/utility/main.cpp @@ -0,0 +1,96 @@ + +#include "./command_line_arguments.hpp" + +#include + +#include +#include +#include + +void print_info(const recorder::RecordingReader& recording_reader) noexcept { + //TODO(Totto): Implement, print basic information and final result for each simulation + UNUSED(recording_reader); + std::cerr << "NOT IMPLEMENTED\n"; +} + +void dump_json(const recorder::RecordingReader& recording_reader, bool pretty_print, bool ensure_ascii) noexcept { + + auto result = json::try_convert_to_json(recording_reader); + + if (not result.has_value()) { + std::cerr << fmt::format("An error occurred during converting to json: {}\n", result.error()); + std::exit(1); + } + + + int indent = -1; + char indent_char = ' '; + + if (pretty_print) { + indent = 1; + indent_char = '\t'; + } + + try { + + std::cout << result.value().dump(indent, indent_char, ensure_ascii); + + } catch (const std::exception& error) { + std::cerr << error.what(); + std::exit(1); + } + + if (pretty_print) { + std::cout << "\n"; + } +} + + +int main(int argc, char** argv) noexcept { + + try { + + + auto arguments_result = CommandLineArguments::from_args(argc, argv); + + if (not arguments_result.has_value()) { + std::cerr << arguments_result.error(); + std::exit(1); + } + + auto arguments = std::move(arguments_result.value()); + + if (not std::filesystem::exists(arguments.recording_path)) { + std::cerr << arguments.recording_path << " does not exist!\n"; + return 1; + } + + + auto parsed = recorder::RecordingReader::from_path(arguments.recording_path); + + if (not parsed.has_value()) { + std::cerr << fmt::format( + "An error occurred during parsing of the recording file '{}': {}\n", + arguments.recording_path.string(), parsed.error() + ); + return 1; + } + + + const auto recording_reader = std::move(parsed.value()); + + std::visit( + helper::overloaded{ [&recording_reader](const Dump& dump) { + dump_json(recording_reader, dump.pretty_print, dump.ensure_ascii); + }, + [&recording_reader](const Info& /* info */) { print_info(recording_reader); } }, + arguments.value + ); + + } catch (const std::exception& error) { + std::cerr << error.what(); + return 1; + } + + return 0; +} diff --git a/src/executables/utility/meson.build b/src/executables/utility/meson.build new file mode 100644 index 00000000..997e8923 --- /dev/null +++ b/src/executables/utility/meson.build @@ -0,0 +1,4 @@ +recordings_main_files += files( + 'command_line_arguments.hpp', + 'main.cpp', +) diff --git a/src/game/bag.hpp b/src/game/bag.hpp index dba8f1fd..2774d31c 100644 --- a/src/game/bag.hpp +++ b/src/game/bag.hpp @@ -1,7 +1,7 @@ #pragma once -#include "game/tetromino_type.hpp" -#include "helper/random.hpp" +#include +#include #include diff --git a/src/game/command_line_arguments.cpp b/src/game/command_line_arguments.cpp new file mode 100644 index 00000000..48f068aa --- /dev/null +++ b/src/game/command_line_arguments.cpp @@ -0,0 +1,16 @@ + + + +#include "command_line_arguments.hpp" + + +CommandLineArguments::CommandLineArguments( + std::optional recording_path, + std::optional target_fps, + Level starting_level, + bool silent +) + : recording_path{ std::move(recording_path) }, + target_fps{ target_fps }, + starting_level{ starting_level }, + silent{ silent } { } diff --git a/src/game/command_line_arguments.hpp b/src/game/command_line_arguments.hpp new file mode 100644 index 00000000..274e6b58 --- /dev/null +++ b/src/game/command_line_arguments.hpp @@ -0,0 +1,29 @@ + + +#pragma once + +#include +#include +#include + +#include +#include + +struct CommandLineArguments final { + + static const constexpr auto default_starting_level = u32{ 0 }; + static const constexpr auto default_silent = true; + + std::optional recording_path; + std::optional target_fps; + using Level = std::remove_cvref_t; + Level starting_level; + bool silent; + + CommandLineArguments( + std::optional recording_path, + std::optional target_fps, + Level starting_level = default_starting_level, + bool silent = default_silent + ); +}; diff --git a/src/game/game.cpp b/src/game/game.cpp index dc71f83c..282c3d81 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -1,7 +1,8 @@ +#include +#include + #include "game.hpp" -#include "helper/magic_enum_wrapper.hpp" -#include "helper/utils.hpp" #include "input/replay_input.hpp" Game::Game( diff --git a/src/game/game.hpp b/src/game/game.hpp index bc566c10..fbd8e005 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -1,8 +1,9 @@ #pragma once +#include + #include "helper/clock_source.hpp" #include "input/input_creator.hpp" -#include "recordings/recording.hpp" #include "tetrion.hpp" #include "ui/widget.hpp" diff --git a/src/game/graphic_helpers.cpp b/src/game/graphic_helpers.cpp index fd08cf87..5630713d 100644 --- a/src/game/graphic_helpers.cpp +++ b/src/game/graphic_helpers.cpp @@ -1,6 +1,7 @@ +#include + #include "graphic_helpers.hpp" #include "graphics/renderer.hpp" -#include "helper/utils.hpp" #include diff --git a/src/game/graphic_helpers.hpp b/src/game/graphic_helpers.hpp index 2dff9d97..c25e49d1 100644 --- a/src/game/graphic_helpers.hpp +++ b/src/game/graphic_helpers.hpp @@ -1,10 +1,9 @@ #pragma once -#include "graphics/point.hpp" -#include "helper/types.hpp" +#include + #include "manager/service_provider.hpp" -#include "mino.hpp" -#include "mino_stack.hpp" + enum class MinoTransparency : u8 { // here the enum value is used as index into the preview alpha array diff --git a/src/game/grid.hpp b/src/game/grid.hpp index 44a59da8..a0ad6bd9 100644 --- a/src/game/grid.hpp +++ b/src/game/grid.hpp @@ -1,9 +1,9 @@ #pragma once -#include "graphics/point.hpp" +#include +#include + #include "graphics/rect.hpp" -#include "grid_properties.hpp" -#include "helper/color.hpp" #include "manager/service_provider.hpp" #include "ui/layout.hpp" #include "ui/widget.hpp" diff --git a/src/game/meson.build b/src/game/meson.build index 5e360af8..0399f90a 100644 --- a/src/game/meson.build +++ b/src/game/meson.build @@ -1,28 +1,18 @@ -core_src_files += files( - 'mino.cpp', - 'mino.hpp', - 'mino_stack.cpp', - 'mino_stack.hpp', -) - graphics_src_files += files( 'bag.cpp', 'bag.hpp', + 'command_line_arguments.cpp', + 'command_line_arguments.hpp', 'game.cpp', 'game.hpp', 'graphic_helpers.cpp', 'graphic_helpers.hpp', 'grid.cpp', 'grid.hpp', - 'grid_properties.hpp', 'rotation.cpp', 'rotation.hpp', 'tetrion.cpp', 'tetrion.hpp', 'tetromino.cpp', 'tetromino.hpp', - 'tetromino_type.cpp', - 'tetromino_type.hpp', ) - - diff --git a/src/game/rotation.hpp b/src/game/rotation.hpp index a71b4d42..3d8e37f4 100644 --- a/src/game/rotation.hpp +++ b/src/game/rotation.hpp @@ -1,6 +1,6 @@ #pragma once -#include "helper/types.hpp" +#include enum class Rotation : u8 { North = 0, diff --git a/src/game/tetrion.cpp b/src/game/tetrion.cpp index abff894e..1e8b68e8 100644 --- a/src/game/tetrion.cpp +++ b/src/game/tetrion.cpp @@ -1,13 +1,14 @@ -#include "tetrion.hpp" +#include +#include +#include + #include "helper/constants.hpp" #include "helper/graphic_utils.hpp" -#include "helper/magic_enum_wrapper.hpp" #include "helper/music_utils.hpp" #include "helper/platform.hpp" -#include "helper/utils.hpp" #include "manager/music_manager.hpp" #include "manager/resource_manager.hpp" -#include "recordings/recording_writer.hpp" +#include "tetrion.hpp" #include "ui/components/label.hpp" #include @@ -20,7 +21,7 @@ Tetrion::Tetrion( const Random::Seed random_seed, const u32 starting_level, ServiceProvider* const service_provider, - helper::optional> recording_writer, + std::optional> recording_writer, const ui::Layout& layout, bool is_top_level ) @@ -631,7 +632,7 @@ bool Tetrion::move(const Tetrion::MoveDirection move_direction) { UNREACHABLE(); } -helper::optional Tetrion::get_wall_kick_table() const { +std::optional Tetrion::get_wall_kick_table() const { assert(m_active_tetromino.has_value() and "no active tetromino"); const auto type = m_active_tetromino->type(); // NOLINT(bugprone-unchecked-optional-access) switch (type) { diff --git a/src/game/tetrion.hpp b/src/game/tetrion.hpp index 83f8a3ae..cbff250f 100644 --- a/src/game/tetrion.hpp +++ b/src/game/tetrion.hpp @@ -1,14 +1,14 @@ #pragma once +#include +#include +#include +#include + #include "bag.hpp" #include "grid.hpp" -#include "helper/optional.hpp" -#include "helper/random.hpp" -#include "helper/types.hpp" #include "input/game_input.hpp" #include "manager/service_provider.hpp" -#include "mino_stack.hpp" -#include "recordings/tetrion_core_information.hpp" #include "tetromino.hpp" #include "ui/layout.hpp" #include "ui/layouts/grid_layout.hpp" @@ -61,7 +61,7 @@ struct Tetrion final : public ui::Widget { u32 m_num_executed_lock_delays = 0; u64 m_lock_delay_step_index; ServiceProvider* const m_service_provider; - helper::optional> m_recording_writer; + std::optional> m_recording_writer; MinoStack m_mino_stack; Random m_random; u32 m_level; @@ -70,10 +70,10 @@ struct Tetrion final : public ui::Widget { int m_sequence_index = 0; u64 m_score = 0; std::array m_sequence_bags{ Bag{ m_random }, Bag{ m_random } }; - helper::optional m_active_tetromino; - helper::optional m_ghost_tetromino; - helper::optional m_tetromino_on_hold; - std::array, num_preview_tetrominos> m_preview_tetrominos{}; + std::optional m_active_tetromino; + std::optional m_ghost_tetromino; + std::optional m_tetromino_on_hold; + std::array, num_preview_tetrominos> m_preview_tetrominos{}; u8 m_tetrion_index; u64 m_next_gravity_simulation_step_index; ui::TileLayout m_main_layout; @@ -84,7 +84,7 @@ struct Tetrion final : public ui::Widget { Random::Seed random_seed, u32 starting_level, ServiceProvider* service_provider, - helper::optional> recording_writer, + std::optional> recording_writer, const ui::Layout& layout, bool is_top_level); void update_step(SimulationStep simulation_step_index); @@ -130,7 +130,7 @@ struct Tetrion final : public ui::Widget { bool rotate(RotationDirection rotation_direction); bool move(MoveDirection move_direction); - [[nodiscard]] helper::optional get_wall_kick_table() const; + [[nodiscard]] std::optional get_wall_kick_table() const; void reset_lock_delay(SimulationStep simulation_step_index); void refresh_texts(); void clear_fully_occupied_lines(); diff --git a/src/game/tetromino.hpp b/src/game/tetromino.hpp index 8bc98ec8..5b83840a 100644 --- a/src/game/tetromino.hpp +++ b/src/game/tetromino.hpp @@ -1,10 +1,9 @@ #pragma once +#include + #include "graphic_helpers.hpp" -#include "graphics/point.hpp" -#include "mino.hpp" #include "rotation.hpp" -#include "tetromino_type.hpp" #include diff --git a/src/graphics/meson.build b/src/graphics/meson.build index fb095fdc..098f6680 100644 --- a/src/graphics/meson.build +++ b/src/graphics/meson.build @@ -1,5 +1,4 @@ graphics_src_files += files( - 'point.hpp', 'rect.hpp', 'renderer.cpp', 'renderer.hpp', diff --git a/src/graphics/rect.hpp b/src/graphics/rect.hpp index 50a3985b..bd955d70 100644 --- a/src/graphics/rect.hpp +++ b/src/graphics/rect.hpp @@ -1,6 +1,6 @@ #pragma once -#include "graphics/point.hpp" +#include #include #include diff --git a/src/graphics/renderer.cpp b/src/graphics/renderer.cpp index 4e7bc42a..b5f71f80 100644 --- a/src/graphics/renderer.cpp +++ b/src/graphics/renderer.cpp @@ -1,5 +1,7 @@ +#include + #include "renderer.hpp" -#include "helper/errors.hpp" + //TODO(Totto): assert return values of all sdl functions diff --git a/src/graphics/renderer.hpp b/src/graphics/renderer.hpp index 2686201f..44715b02 100644 --- a/src/graphics/renderer.hpp +++ b/src/graphics/renderer.hpp @@ -1,6 +1,7 @@ #pragma once -#include "helper/color.hpp" +#include + #include "manager/font.hpp" #include "rect.hpp" #include "texture.hpp" diff --git a/src/graphics/sdl_context.cpp b/src/graphics/sdl_context.cpp index 24ed8b03..e0c426f1 100644 --- a/src/graphics/sdl_context.cpp +++ b/src/graphics/sdl_context.cpp @@ -1,5 +1,6 @@ +#include + #include "graphics/sdl_context.hpp" -#include "helper/errors.hpp" #include #include diff --git a/src/graphics/text.hpp b/src/graphics/text.hpp index eb1c8688..319b6132 100644 --- a/src/graphics/text.hpp +++ b/src/graphics/text.hpp @@ -1,6 +1,7 @@ #pragma once -#include "helper/color.hpp" +#include + #include "manager/font.hpp" #include "manager/service_provider.hpp" #include "rect.hpp" diff --git a/src/graphics/texture.cpp b/src/graphics/texture.cpp index 2fb3b879..0ec8e501 100644 --- a/src/graphics/texture.cpp +++ b/src/graphics/texture.cpp @@ -1,7 +1,7 @@ +#include -#include "texture.hpp" -#include "graphics/point.hpp" #include "helper/graphic_utils.hpp" +#include "texture.hpp" Texture::Texture(SDL_Texture* raw_texture) : m_raw_texture{ raw_texture } { } diff --git a/src/graphics/texture.hpp b/src/graphics/texture.hpp index ae82a16d..872b38e1 100644 --- a/src/graphics/texture.hpp +++ b/src/graphics/texture.hpp @@ -2,10 +2,11 @@ #pragma once -#include "helper/color.hpp" -#include "helper/utils.hpp" +#include +#include +#include + #include "manager/font.hpp" -#include "point.hpp" #include "rect.hpp" #include diff --git a/src/graphics/window.hpp b/src/graphics/window.hpp index ee630d9d..7ea426c6 100644 --- a/src/graphics/window.hpp +++ b/src/graphics/window.hpp @@ -1,6 +1,7 @@ #pragma once -#include "graphics/point.hpp" +#include + #include "graphics/rect.hpp" #include "helper/message_box.hpp" #include "sdl_context.hpp" diff --git a/src/helper/bool_wrapper.hpp b/src/helper/bool_wrapper.hpp deleted file mode 100644 index 0fb4a36b..00000000 --- a/src/helper/bool_wrapper.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include "helper/optional.hpp" - -namespace helper { - - template - struct BoolWrapper { - private: - bool m_value; - helper::optional additional; - - public: - BoolWrapper(bool value) : m_value{ value }, additional{ helper::nullopt } { } - - BoolWrapper(bool value, const T& additional) : m_value{ value }, additional{ additional } { } - - BoolWrapper(bool value, const helper::optional& additional) : m_value{ value }, additional{ additional } { } - - const helper::optional& get_additional() const { - return additional; - } - - [[nodiscard]] bool has_additional() const { - return additional.has_value(); - } - - - bool is(const T& value) const { - return value == additional; - } - - operator bool() const { - return m_value; - } - }; - -} // namespace helper diff --git a/src/helper/clock_source.cpp b/src/helper/clock_source.cpp index f003e28c..385d8247 100644 --- a/src/helper/clock_source.cpp +++ b/src/helper/clock_source.cpp @@ -41,7 +41,7 @@ double LocalClock::resume() { } const auto duration = elapsed_time() - *m_paused_at; m_start_time += duration; - m_paused_at = helper::nullopt; + m_paused_at = std::nullopt; spdlog::info("resuming clock (duration of pause: {} s)", duration); return duration; } diff --git a/src/helper/clock_source.hpp b/src/helper/clock_source.hpp index 5818f3e6..91db02c6 100644 --- a/src/helper/clock_source.hpp +++ b/src/helper/clock_source.hpp @@ -1,8 +1,9 @@ #pragma once -#include "helper/optional.hpp" -#include "helper/types.hpp" +#include + +#include #include struct ClockSource { @@ -28,7 +29,7 @@ struct LocalClock : public ClockSource { private: double m_start_time; double m_step_duration; - helper::optional m_paused_at{}; + std::optional m_paused_at; public: explicit LocalClock(u32 target_frequency); diff --git a/src/helper/command_line_arguments.hpp b/src/helper/command_line_arguments.hpp deleted file mode 100644 index 2f372650..00000000 --- a/src/helper/command_line_arguments.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include "helper/constants.hpp" -#include "helper/graphic_utils.hpp" -#include "helper/optional.hpp" -#include "helper/types.hpp" -#include "helper/utils.hpp" - -#include -#include -#include -#include -#include -#include - -struct CommandLineArguments final { -private: - static inline constexpr auto default_starting_level = u32{ 0 }; - -public: - helper::optional recording_path{}; - helper::optional target_fps{}; - std::remove_cvref_t starting_level{ default_starting_level }; - bool silent{ false }; - - CommandLineArguments(const std::vector& arguments) { - argparse::ArgumentParser parser{ constants::program_name, constants::version, - argparse::default_arguments::all }; - parser.add_argument("-r", "--recording").help("the path of a recorded game used for replay"); - parser.add_argument("-f", "--target-fps").help("the number of simulation steps per second").scan<'i', u32>(); - parser.add_argument("-l", "--level") - .help("the starting level of the game") - .scan<'i', decltype(starting_level)>() - .default_value(default_starting_level); - parser.add_argument("-s", "--silent").help("disable audio output").default_value(false).implicit_value(true); - try { - parser.parse_args(arguments); - - - if (auto path = parser.present("--recording")) { - spdlog::info("recording is present"); - recording_path = utils::get_root_folder() / *path; - } - - const auto fps = parser.present("--target-fps"); - if (fps.has_value()) { - if (fps.value() >= 1) { - target_fps = fps.value(); - } else { - spdlog::error( - "invalid value for target fps ({}), using default value instead (VSYNC)", fps.value() - ); - } - } - - const auto level = parser.get("--level"); - if (level <= 30) { - starting_level = level; - } else { - spdlog::error( - "invalid value for starting level ({}), using default value instead ({})", level, starting_level - ); - } - - silent = parser.get("--silent"); - } catch (const std::exception& err) { - spdlog::error("error parsing command line arguments: {}", err.what()); -#if defined(__ANDROID__) - // calling exit() in android doesn't do the correct job, it completely avoids resource cleanup by the underlying SDLActivity.java - // (java wrapper), that calls the main and expects it to return ALWAYS and throwing an exception in a catch statement is bad, - // but is required here - throw std::runtime_error{ "exit with status code 1: " + std::string{ err.what() } }; -#else - std::exit(1); -#endif - } - } -}; diff --git a/src/helper/console_helpers.cpp b/src/helper/console_helpers.cpp index 99af1e49..c804a0ac 100644 --- a/src/helper/console_helpers.cpp +++ b/src/helper/console_helpers.cpp @@ -1,10 +1,11 @@ -#include "helper/errors.hpp" +#include + #if defined(__NINTENDO_CONSOLE__) #include "console_helpers.hpp" -#include "helper/utils.hpp" +#include #if defined(__SWITCH__) #include diff --git a/src/helper/constants.hpp b/src/helper/constants.hpp index 1c497b52..d9189081 100644 --- a/src/helper/constants.hpp +++ b/src/helper/constants.hpp @@ -1,6 +1,6 @@ #pragma once -#include "helper/static_string.hpp" +#include namespace constants { @@ -19,6 +19,9 @@ namespace constants { constexpr auto recordings_directory = "recordings"; constexpr u32 simulation_frequency = 60; +#undef STRINGIFY +#undef STRINGIFY_HELPER_ + #if not defined(AUDIO_QUALITY) #define AUDIO_QUALITY 0 // NOLINT(cppcoreguidelines-macro-usage) #endif diff --git a/src/helper/graphic_utils.cpp b/src/helper/graphic_utils.cpp index 75f2191a..080969c2 100644 --- a/src/helper/graphic_utils.cpp +++ b/src/helper/graphic_utils.cpp @@ -1,5 +1,6 @@ #include "graphic_utils.hpp" +#include SDL_Color utils::sdl_color_from_color(const Color& color) { return SDL_Color{ color.r, color.g, color.b, color.a }; @@ -90,7 +91,29 @@ std::vector utils::supported_features() { #endif } -helper::optional utils::log_error(const std::string& error) { +std::optional utils::log_error(const std::string& error) { spdlog::error(error); - return helper::nullopt; + return std::nullopt; +} + +utils::ExitException::ExitException(int status_code) noexcept : m_status_code{ status_code } { } + +[[nodiscard]] int utils::ExitException::status_code() const { + return m_status_code; +} + +[[nodiscard]] const char* utils::ExitException::what() const noexcept { + return "An exit exception occurred"; +} + +void utils::exit(int status_code) { +#if defined(__ANDROID__) + // calling exit() in android doesn't do the correct job, it completely avoids resource cleanup by the underlying SDLActivity.java + // (java wrapper), that calls the main and expects it to return ALWAYS and throwing an exception in a catch statement is bad, + // but is required here + // see: https://github.com/libsdl-org/SDL/blob/main/docs/README-android.md + throw utils::ExitException{ status_code }; +#else + std::exit(status_code); +#endif } diff --git a/src/helper/graphic_utils.hpp b/src/helper/graphic_utils.hpp index 3977bd61..4550ce0a 100644 --- a/src/helper/graphic_utils.hpp +++ b/src/helper/graphic_utils.hpp @@ -1,9 +1,10 @@ #pragma once -#include "color.hpp" +#include + #include "helper/constants.hpp" -#include "helper/optional.hpp" + #include #include @@ -18,5 +19,21 @@ namespace utils { [[nodiscard]] std::filesystem::path get_root_folder(); - helper::optional log_error(const std::string& error); + std::optional log_error(const std::string& error); + + struct ExitException : std::exception { + private: + int m_status_code; + + public: + explicit ExitException(int status_code) noexcept; + + [[nodiscard]] int status_code() const; + + [[nodiscard]] const char* what() const noexcept override; + }; + + + [[noreturn]] void exit(int status_code); + } // namespace utils diff --git a/src/helper/meson.build b/src/helper/meson.build index a8956e18..f7210ff3 100644 --- a/src/helper/meson.build +++ b/src/helper/meson.build @@ -1,38 +1,10 @@ -core_src_files += files( - 'bool_wrapper.hpp', - 'color.cpp', - 'color.hpp', - 'color_literals.hpp', - 'command_line_arguments.hpp', - 'const_utils.hpp', - 'constants.hpp', - 'date.cpp', - 'date.hpp', - 'errors.cpp', - 'errors.hpp', - 'expected.hpp', - 'git_helper.hpp', - 'magic_enum_wrapper.hpp', - 'optional.hpp', - 'parse_json.cpp', - 'parse_json.hpp', - 'random.cpp', - 'random.hpp', - 'sleep.cpp', - 'sleep.hpp', - 'static_string.hpp', - 'string_manipulation.cpp', - 'string_manipulation.hpp', - 'timer.hpp', - 'types.hpp', - 'utils.hpp', -) - graphics_src_files += files( 'clock_source.cpp', 'clock_source.hpp', 'console_helpers.cpp', 'console_helpers.hpp', + 'constants.hpp', + 'git_helper.hpp', 'graphic_utils.cpp', 'graphic_utils.hpp', 'message_box.cpp', @@ -49,7 +21,7 @@ endif git = find_program('git') vcs_dep = vcs_tag( - command: ['git', 'describe', '--tags', '--always', '--abbrev=12'], + command: [git, 'describe', '--tags', '--always', '--abbrev=12'], input: 'git_version.hpp.in', output: 'git_version.hpp', replace_string: '@GIT_VERSION@', diff --git a/src/helper/music_utils.hpp b/src/helper/music_utils.hpp index 7707eade..8482c976 100644 --- a/src/helper/music_utils.hpp +++ b/src/helper/music_utils.hpp @@ -1,6 +1,6 @@ #pragma once -#include "static_string.hpp" +#include namespace utils { diff --git a/src/helper/nfd.cpp b/src/helper/nfd.cpp index a840f498..ffdbf135 100644 --- a/src/helper/nfd.cpp +++ b/src/helper/nfd.cpp @@ -1,9 +1,10 @@ -#include "nfd.hpp" -#include "helper/utils.hpp" + #if defined(_HAVE_FILE_DIALOGS) -#include "helper/types.hpp" +#include +#include + #include "nfd_include.hpp" #include @@ -69,7 +70,7 @@ namespace { helper::expected helper::openFileDialog( const std::vector& allowed_files, - helper::optional default_path + std::optional default_path ) { NFD::UniquePathU8 out_path{}; @@ -109,7 +110,7 @@ helper::expected helper::openFileDialog( [[nodiscard]] helper::expected, std::string> helper::openMultipleFilesDialog( const std::vector& allowed_files, - helper::optional default_path + std::optional default_path ) { NFD::UniquePathSet out_paths{}; @@ -160,7 +161,7 @@ helper::expected helper::openFileDialog( } [[nodiscard]] helper::expected helper::openFolderDialog( - helper::optional default_path + std::optional default_path ) { NFD::UniquePathU8 out_path{}; diff --git a/src/helper/nfd_include.hpp b/src/helper/nfd_include.hpp index 0bb65f61..7b8117e3 100644 --- a/src/helper/nfd_include.hpp +++ b/src/helper/nfd_include.hpp @@ -3,8 +3,8 @@ #if defined(_HAVE_FILE_DIALOGS) -#include "helper/expected.hpp" -#include "helper/optional.hpp" +#include + #define NFD_THROWS_EXCEPTIONS #ifdef _WIN32 @@ -13,10 +13,10 @@ #include #include +#include #include #include - namespace helper { @@ -29,16 +29,16 @@ namespace helper { //NOTE: this API is blocking and can't be asynchronous, due to os (linux, windows, macos) restrictions, it HAS to be launched in the same thread NFD_Init() was launched /the main thread) [[nodiscard]] helper::expected openFileDialog( const std::vector& allowed_files = {}, - helper::optional default_path = helper::nullopt + std::optional default_path = std::nullopt ); [[nodiscard]] helper::expected, std::string> openMultipleFilesDialog( const std::vector& allowed_files = {}, - helper::optional default_path = helper::nullopt + std::optional default_path = std::nullopt ); [[nodiscard]] helper::expected openFolderDialog( - helper::optional default_path = helper::nullopt + std::optional default_path = std::nullopt ); } // namespace helper diff --git a/src/helper/optional.hpp b/src/helper/optional.hpp deleted file mode 100644 index bf44d7cb..00000000 --- a/src/helper/optional.hpp +++ /dev/null @@ -1,30 +0,0 @@ - -#pragma once - -#ifdef __USE_TL_OPTIONAL -#include -#else -#include -#endif - - -namespace helper { - -#ifdef __USE_TL_OPTIONAL - - template - using optional = tl::optional; - - constexpr auto nullopt = tl::nullopt; - -#else - - template - using optional = std::optional; - - constexpr auto nullopt = std::nullopt; - -#endif - - -} // namespace helper diff --git a/src/helper/platform.hpp b/src/helper/platform.hpp index 42e7249f..305a7c8e 100644 --- a/src/helper/platform.hpp +++ b/src/helper/platform.hpp @@ -2,7 +2,7 @@ #pragma once -#include "helper/types.hpp" +#include #include diff --git a/src/input/controller_input.cpp b/src/input/controller_input.cpp index abd1fa45..5841a542 100644 --- a/src/input/controller_input.cpp +++ b/src/input/controller_input.cpp @@ -1,7 +1,7 @@ +#include #include "controller_input.hpp" -#include "helper/string_manipulation.hpp" #include "input/input.hpp" #include "input/joystick_input.hpp" #include "manager/sdl_controller_key.hpp" @@ -77,13 +77,12 @@ input::ControllerInput::get_by_device_index(int device_index) { } -[[nodiscard]] helper::optional input::ControllerInput::get_navigation_event( - const SDL_Event& event +[[nodiscard]] std::optional input::ControllerInput::get_navigation_event(const SDL_Event& event ) const { if (event.type == SDL_CONTROLLERBUTTONDOWN) { if (event.cbutton.which != instance_id()) { - return helper::nullopt; + return std::nullopt; } switch (event.cbutton.button) { @@ -100,7 +99,7 @@ input::ControllerInput::get_by_device_index(int device_index) { case SDL_CONTROLLER_BUTTON_BACK: return NavigationEvent::BACK; default: - return helper::nullopt; + return std::nullopt; //note, that NavigationEvent::TAB is not supported } @@ -131,7 +130,7 @@ input::ControllerInput::get_by_device_index(int device_index) { } -[[nodiscard]] helper::optional input::ControllerInput::handle_axis_navigation_event( +[[nodiscard]] std::optional input::ControllerInput::handle_axis_navigation_event( const SDL_Event& event ) const { if (event.type == SDL_CONTROLLERAXISMOTION) { @@ -144,7 +143,7 @@ input::ControllerInput::get_by_device_index(int device_index) { static_cast(static_cast(SDL_JOYSTICK_AXIS_MAX) * axis_threshold_percentage); if (event.caxis.which != instance_id()) { - return helper::nullopt; + return std::nullopt; } // x axis movement @@ -157,7 +156,7 @@ input::ControllerInput::get_by_device_index(int device_index) { return NavigationEvent::LEFT; } - return helper::nullopt; + return std::nullopt; } // y axis movement @@ -170,19 +169,19 @@ input::ControllerInput::get_by_device_index(int device_index) { return NavigationEvent::UP; } - return helper::nullopt; + return std::nullopt; } //Note: not all types of SDL_GameControllerAxis are handled, since they are not needed for navigation events - return helper::nullopt; + return std::nullopt; } - return helper::nullopt; + return std::nullopt; } -[[nodiscard]] helper::expected input::ControllerSettings::validate() const { +[[nodiscard]] helper::expected input::ControllerSettings::validate() const { const std::vector to_use{ rotate_left, rotate_right, move_left, move_right, move_down, drop, hold, pause, open_settings }; @@ -205,12 +204,11 @@ input::ControllerGameInput::ControllerGameInput( } -[[nodiscard]] helper::optional input::ControllerGameInput::get_menu_event(const SDL_Event& event -) const { +[[nodiscard]] std::optional input::ControllerGameInput::get_menu_event(const SDL_Event& event) const { if (event.type == SDL_CONTROLLERBUTTONDOWN) { if (event.cbutton.which != underlying_input()->instance_id()) { - return helper::nullopt; + return std::nullopt; } const auto button = sdl::ControllerKey{ static_cast(event.cbutton.button) }; @@ -223,7 +221,7 @@ input::ControllerGameInput::ControllerGameInput( } } - return helper::nullopt; + return std::nullopt; // } @@ -240,12 +238,12 @@ input::ControllerGameInput::ControllerGameInput( } -[[nodiscard]] helper::optional input::ControllerGameInput::sdl_event_to_input_event(const SDL_Event& event +[[nodiscard]] std::optional input::ControllerGameInput::sdl_event_to_input_event(const SDL_Event& event ) const { if (event.type == SDL_CONTROLLERBUTTONDOWN) { if (event.cbutton.which != underlying_input()->instance_id()) { - return helper::nullopt; + return std::nullopt; } //TODO(Totto): use switch case @@ -275,7 +273,7 @@ input::ControllerGameInput::ControllerGameInput( } else if (event.type == SDL_CONTROLLERBUTTONUP) { if (event.cbutton.which != underlying_input()->instance_id()) { - return helper::nullopt; + return std::nullopt; } const auto button = sdl::ControllerKey{ static_cast(event.cbutton.button) }; @@ -302,7 +300,7 @@ input::ControllerGameInput::ControllerGameInput( return InputEvent::HoldReleased; } } - return helper::nullopt; + return std::nullopt; } diff --git a/src/input/controller_input.hpp b/src/input/controller_input.hpp index 8561cdbb..7d2a577e 100644 --- a/src/input/controller_input.hpp +++ b/src/input/controller_input.hpp @@ -27,13 +27,12 @@ namespace input { int device_index ); - [[nodiscard]] helper::optional get_navigation_event(const SDL_Event& event) const override; + [[nodiscard]] std::optional get_navigation_event(const SDL_Event& event) const override; [[nodiscard]] std::string describe_navigation_event(NavigationEvent event) const override; private: - [[nodiscard]] helper::optional handle_axis_navigation_event(const SDL_Event& event - ) const; + [[nodiscard]] std::optional handle_axis_navigation_event(const SDL_Event& event) const; }; struct ControllerSettings { @@ -49,7 +48,7 @@ namespace input { sdl::ControllerKey open_settings; - [[nodiscard]] helper::expected validate() const; + [[nodiscard]] helper::expected validate() const; [[nodiscard]] static ControllerSettings default_settings() { @@ -79,14 +78,14 @@ namespace input { ControllerInput* underlying_input ); - [[nodiscard]] helper::optional get_menu_event(const SDL_Event& event) const override; + [[nodiscard]] std::optional get_menu_event(const SDL_Event& event) const override; [[nodiscard]] std::string describe_menu_event(MenuEvent event) const override; [[nodiscard]] const ControllerInput* underlying_input() const override; protected: - [[nodiscard]] helper::optional sdl_event_to_input_event(const SDL_Event& event) const override; + [[nodiscard]] std::optional sdl_event_to_input_event(const SDL_Event& event) const override; }; diff --git a/src/input/game_input.hpp b/src/input/game_input.hpp index add8cc76..bda37d19 100644 --- a/src/input/game_input.hpp +++ b/src/input/game_input.hpp @@ -1,11 +1,11 @@ #pragma once +#include +#include +#include + #include "helper/clock_source.hpp" -#include "helper/optional.hpp" -#include "helper/random.hpp" -#include "helper/types.hpp" #include "manager/event_listener.hpp" -#include "manager/input_event.hpp" #include #include @@ -77,7 +77,7 @@ namespace input { virtual void update(SimulationStep simulation_step_index); virtual void late_update(SimulationStep /*simulation_step*/){}; - [[nodiscard]] virtual helper::optional get_menu_event(const SDL_Event& event) const = 0; + [[nodiscard]] virtual std::optional get_menu_event(const SDL_Event& event) const = 0; [[nodiscard]] virtual std::string describe_menu_event(MenuEvent event) const = 0; diff --git a/src/input/guid.cpp b/src/input/guid.cpp index bbdaeb2b..0ec5105e 100644 --- a/src/input/guid.cpp +++ b/src/input/guid.cpp @@ -1,6 +1,7 @@ +#include + #include "guid.hpp" -#include "helper/utils.hpp" #include #include diff --git a/src/input/guid.hpp b/src/input/guid.hpp index 274b9f70..c0c7b208 100644 --- a/src/input/guid.hpp +++ b/src/input/guid.hpp @@ -1,9 +1,9 @@ #pragma once -#include "helper/const_utils.hpp" -#include "helper/expected.hpp" -#include "helper/types.hpp" +#include +#include +#include #include #include diff --git a/src/input/input.cpp b/src/input/input.cpp index f6f4bab3..90b34c49 100644 --- a/src/input/input.cpp +++ b/src/input/input.cpp @@ -1,7 +1,7 @@ +#include +#include + #include "input.hpp" -#include "helper/expected.hpp" -#include "helper/optional.hpp" -#include "helper/utils.hpp" #include "input/controller_input.hpp" #include "joystick_input.hpp" #include "keyboard_input.hpp" @@ -96,7 +96,7 @@ input::InputManager::InputManager(const std::shared_ptr& window) { } -[[nodiscard]] helper::optional input::InputManager::get_navigation_event(const SDL_Event& event +[[nodiscard]] std::optional input::InputManager::get_navigation_event(const SDL_Event& event ) const { for (const auto& input : m_inputs) { @@ -105,10 +105,10 @@ input::InputManager::InputManager(const std::shared_ptr& window) { } } - return helper::nullopt; + return std::nullopt; } -[[nodiscard]] helper::optional input::InputManager::get_pointer_event(const SDL_Event& event +[[nodiscard]] std::optional input::InputManager::get_pointer_event(const SDL_Event& event ) const { for (const auto& input : m_inputs) { if (const auto pointer_input = utils::is_child_class(input); pointer_input.has_value()) { @@ -118,7 +118,7 @@ input::InputManager::InputManager(const std::shared_ptr& window) { } } - return helper::nullopt; + return std::nullopt; } @@ -151,6 +151,32 @@ input::InputManager::InputManager(const std::shared_ptr& window) { break; } break; + //TODO(Totto): handle those types correctly, to e.g. pause the game automatically on background enter + case SDL_APP_TERMINATING: + /* Terminate the app. + Shut everything down before returning from this function. + */ + case SDL_APP_LOWMEMORY: + /* You will get this when your app is paused and iOS wants more memory. + Release as much memory as possible. + */ + + case SDL_APP_WILLENTERBACKGROUND: + /* Prepare your app to go into the background. Stop loops, etc. + This gets called when the user hits the home button, or gets a call. + */ + case SDL_APP_DIDENTERBACKGROUND: + /* This will get called if the user accepted whatever sent your app to the background. + If the user got a phone call and canceled it, you'll instead get an SDL_APP_DID_ENTER_FOREGROUND event and restart your loops. + When you get this, you have 5 seconds to save all your state or the app will be terminated. + Your app is NOT active at this point. + */ + case SDL_APP_WILLENTERFOREGROUND: + /* This call happens when your app is coming back to the foreground. + Restore all your state here. + */ + case SDL_APP_DIDENTERFOREGROUND: + return true; default: break; } @@ -284,7 +310,7 @@ namespace { } - helper::optional> + std::optional> get_game_input_by_input(ServiceProvider* service_provider, const std::unique_ptr& input) { const auto& settings = service_provider->settings_manager().settings(); @@ -311,7 +337,7 @@ namespace { if (not game_input.has_value()) { spdlog::warn("Not possible to get joystick by settings: {}", game_input.error()); - return helper::nullopt; + return std::nullopt; } @@ -340,7 +366,7 @@ namespace { } - return helper::nullopt; + return std::nullopt; } diff --git a/src/input/input.hpp b/src/input/input.hpp index 3d52f3af..f6c75b77 100644 --- a/src/input/input.hpp +++ b/src/input/input.hpp @@ -2,14 +2,14 @@ #pragma once +#include +#include +#include #include "game_input.hpp" -#include "graphics/point.hpp" #include "graphics/rect.hpp" #include "graphics/window.hpp" -#include "helper/bool_wrapper.hpp" -#include "helper/expected.hpp" -#include "helper/optional.hpp" + #include "manager/service_provider.hpp" @@ -45,7 +45,7 @@ namespace input { [[nodiscard]] const std::string& name() const; [[nodiscard]] InputType type() const; - [[nodiscard]] virtual helper::optional get_navigation_event(const SDL_Event& event) const = 0; + [[nodiscard]] virtual std::optional get_navigation_event(const SDL_Event& event) const = 0; [[nodiscard]] virtual std::string describe_navigation_event(NavigationEvent event) const = 0; }; @@ -75,7 +75,7 @@ namespace input { struct PointerInput : Input { explicit PointerInput(const std::string& name); - [[nodiscard]] virtual helper::optional get_pointer_event(const SDL_Event& event) const = 0; + [[nodiscard]] virtual std::optional get_pointer_event(const SDL_Event& event) const = 0; [[nodiscard]] virtual SDL_Event offset_pointer_event(const SDL_Event& event, const shapes::IPoint& point) const = 0; @@ -90,9 +90,9 @@ namespace input { [[nodiscard]] const std::vector>& inputs() const; - [[nodiscard]] helper::optional get_navigation_event(const SDL_Event& event) const; + [[nodiscard]] std::optional get_navigation_event(const SDL_Event& event) const; - [[nodiscard]] helper::optional get_pointer_event(const SDL_Event& event) const; + [[nodiscard]] std::optional get_pointer_event(const SDL_Event& event) const; /** * @brief Offsets a pointer event, only safe to call, if get_pointer_event returns a non null optional @@ -116,7 +116,7 @@ namespace input { struct InputSettings { template - [[nodiscard]] static helper::expected has_unique_members(const std::vector& to_check) { + [[nodiscard]] static helper::expected has_unique_members(const std::vector& to_check) { std::vector already_bound{}; @@ -129,7 +129,7 @@ namespace input { already_bound.push_back(single_check); } - return true; + return {}; } }; diff --git a/src/input/input_creator.cpp b/src/input/input_creator.cpp index 5a1a360b..ac1b5697 100644 --- a/src/input/input_creator.cpp +++ b/src/input/input_creator.cpp @@ -1,14 +1,16 @@ -#include "input_creator.hpp" -#include "additional_information.hpp" -#include "helper/command_line_arguments.hpp" -#include "helper/date.hpp" -#include "helper/errors.hpp" -#include "helper/expected.hpp" -#include "helper/optional.hpp" +#include +#include +#include +#include + +#include "game/command_line_arguments.hpp" +#include "helper/constants.hpp" +#include "helper/graphic_utils.hpp" #include "input.hpp" #include "input/replay_input.hpp" +#include "input_creator.hpp" #include #include @@ -84,7 +86,7 @@ input::get_game_parameters_for_replay( const auto starting_level = header.starting_level; const tetrion::StartingParameters starting_parameters = { target_fps, seed, starting_level, tetrion_index, - helper::nullopt }; + std::nullopt }; result.emplace_back(std::move(input), starting_parameters); } diff --git a/src/input/input_creator.hpp b/src/input/input_creator.hpp index d73f87a0..5e720fd7 100644 --- a/src/input/input_creator.hpp +++ b/src/input/input_creator.hpp @@ -1,11 +1,12 @@ #pragma once -#include "helper/date.hpp" -#include "helper/optional.hpp" +#include +#include + #include "input/game_input.hpp" #include "manager/service_provider.hpp" -#include "recordings/recording_writer.hpp" + #include @@ -15,14 +16,14 @@ namespace tetrion { Random::Seed seed; u32 starting_level; u8 tetrion_index; - helper::optional> recording_writer; + std::optional> recording_writer; StartingParameters( u32 target_fps, Random::Seed seed, u32 starting_level, // NOLINT(bugprone-easily-swappable-parameters) u8 tetrion_index, - helper::optional> recording_writer = helper::nullopt + std::optional> recording_writer = std::nullopt ) : target_fps{ target_fps }, seed{ seed }, diff --git a/src/input/joystick_input.cpp b/src/input/joystick_input.cpp index dac72192..6608fb66 100644 --- a/src/input/joystick_input.cpp +++ b/src/input/joystick_input.cpp @@ -1,13 +1,13 @@ -#include "joystick_input.hpp" +#include +#include + #include "controller_input.hpp" -#include "helper/expected.hpp" #include "helper/graphic_utils.hpp" -#include "helper/optional.hpp" -#include "helper/utils.hpp" #include "input/game_input.hpp" #include "input/input.hpp" +#include "joystick_input.hpp" #include #include @@ -42,7 +42,7 @@ input::JoystickInput& input::JoystickInput::operator=(const JoystickInput& input input::JoystickInput::JoystickInput(JoystickInput&& input) noexcept = default; input::JoystickInput& input::JoystickInput::operator=(JoystickInput&& input) noexcept = default; -[[nodiscard]] helper::optional> input::JoystickInput::get_joystick_by_guid( +[[nodiscard]] std::optional> input::JoystickInput::get_joystick_by_guid( const sdl::GUID& guid, SDL_Joystick* joystick, SDL_JoystickID instance_id, @@ -67,7 +67,7 @@ input::JoystickInput& input::JoystickInput::operator=(JoystickInput&& input) noe UNUSED(instance_id); UNUSED(name); - return helper::nullopt; + return std::nullopt; } @@ -114,9 +114,8 @@ void input::JoyStickInputManager::discover_devices(std::vectorget()->name() + ); + inputs.push_back(std::move(joystick.value())); + } else { spdlog::warn( "Failed to add newly attached {}: {}", @@ -250,7 +255,8 @@ void input::JoyStickInputManager::remove_device( ) { for (auto it = inputs.cbegin(); it != inputs.cend(); it++) { - if (const auto joystick_input = utils::is_child_class(*it); joystick_input.has_value()) { + if (const auto joystick_input = utils::is_child_class(*it); + joystick_input.has_value()) { if (joystick_input.value()->instance_id() == instance_id) { //TODO(Totto): if we use this joystick as game input we have to notify the user about it,and pause the game, until he is inserted again @@ -261,8 +267,9 @@ void input::JoyStickInputManager::remove_device( } } - spdlog::warn(fmt::format( - "Failed to remove removed {} from internal input vector (maybe he was not recognized in the first place)", + //this happens way to often, since Sdl outputs both SDL_JOYDEVICEREMOVED and SDL_CONTROLLERDEVICEREMOVED at the same time, in case of a controller, so the second time, this is reached :( + spdlog::debug(fmt::format( + "Failed to remove removed {} from internal input vector", type == input::JoystickLikeType::Joystick ? "joystick" : "controller" )); } @@ -339,13 +346,13 @@ input::SwitchJoystickInput_Type1::SwitchJoystickInput_Type1( } { } -[[nodiscard]] helper::optional input::SwitchJoystickInput_Type1::get_navigation_event( +[[nodiscard]] std::optional input::SwitchJoystickInput_Type1::get_navigation_event( const SDL_Event& event ) const { if (event.type == SDL_JOYBUTTONDOWN) { if (event.jbutton.which != instance_id()) { - return helper::nullopt; + return std::nullopt; } switch (event.jbutton.button) { @@ -370,7 +377,7 @@ input::SwitchJoystickInput_Type1::SwitchJoystickInput_Type1( case JOYCON_MINUS: return NavigationEvent::BACK; default: - return helper::nullopt; + return std::nullopt; //note, that NavigationEvent::TAB is not supported } @@ -518,13 +525,13 @@ input::_3DSJoystickInput_Type1::_3DSJoystickInput_Type1( } { } -[[nodiscard]] helper::optional input::_3DSJoystickInput_Type1::get_navigation_event( +[[nodiscard]] std::optional input::_3DSJoystickInput_Type1::get_navigation_event( const SDL_Event& event ) const { if (event.type == SDL_JOYBUTTONDOWN) { if (event.jbutton.which != instance_id()) { - return helper::nullopt; + return std::nullopt; } switch (event.jbutton.button) { @@ -549,7 +556,7 @@ input::_3DSJoystickInput_Type1::_3DSJoystickInput_Type1( case JOYCON_B: return NavigationEvent::BACK; default: - return helper::nullopt; + return std::nullopt; //note, that NavigationEvent::TAB is not supported } @@ -695,7 +702,7 @@ input::JoystickGameInput::JoystickGameInput(EventDispatcher* event_dispatcher, J } namespace { - [[nodiscard]] helper::optional> get_game_joystick_by_guid( + [[nodiscard]] std::optional> get_game_joystick_by_guid( const sdl::GUID& guid, const input::JoystickSettings& settings, EventDispatcher* event_dispatcher, @@ -719,7 +726,7 @@ namespace { UNUSED(event_dispatcher); UNUSED(underlying_input); - return helper::nullopt; + return std::nullopt; } @@ -777,7 +784,7 @@ input::ConsoleJoystickInput::ConsoleJoystickInput( return to_normal_settings(default_settings_raw()); } -[[nodiscard]] helper::optional input::ConsoleJoystickInput::handle_axis_navigation_event( +[[nodiscard]] std::optional input::ConsoleJoystickInput::handle_axis_navigation_event( const SDL_Event& event ) const { if (event.type == SDL_JOYAXISMOTION) { @@ -790,7 +797,7 @@ input::ConsoleJoystickInput::ConsoleJoystickInput( static_cast(static_cast(SDL_JOYSTICK_AXIS_MAX) * axis_threshold_percentage); if (event.jaxis.which != instance_id()) { - return helper::nullopt; + return std::nullopt; } // x axis movement @@ -803,7 +810,7 @@ input::ConsoleJoystickInput::ConsoleJoystickInput( return NavigationEvent::LEFT; } - return helper::nullopt; + return std::nullopt; } // y axis movement @@ -816,13 +823,13 @@ input::ConsoleJoystickInput::ConsoleJoystickInput( return NavigationEvent::UP; } - return helper::nullopt; + return std::nullopt; } throw std::runtime_error(fmt::format("Reached unsupported axis for SDL_JOYAXISMOTION {}", event.jaxis.axis)); } - return helper::nullopt; + return std::nullopt; } @@ -856,11 +863,11 @@ input::ConsoleJoystickGameInput::~ConsoleJoystickGameInput() = default; // game_input uses Input to handle events, but stores the config settings for the specific button -helper::optional input::ConsoleJoystickGameInput::sdl_event_to_input_event(const SDL_Event& event) const { +std::optional input::ConsoleJoystickGameInput::sdl_event_to_input_event(const SDL_Event& event) const { if (event.type == SDL_JOYBUTTONDOWN) { if (event.jbutton.which != underlying_input()->instance_id()) { - return helper::nullopt; + return std::nullopt; } //TODO(Totto): use switch case @@ -889,7 +896,7 @@ helper::optional input::ConsoleJoystickGameInput::sdl_event_to_input } else if (event.type == SDL_JOYBUTTONUP) { if (event.jbutton.which != underlying_input()->instance_id()) { - return helper::nullopt; + return std::nullopt; } const auto button = event.jbutton.button; @@ -915,15 +922,15 @@ helper::optional input::ConsoleJoystickGameInput::sdl_event_to_input return InputEvent::HoldReleased; } } - return helper::nullopt; + return std::nullopt; } -[[nodiscard]] helper::optional input::ConsoleJoystickGameInput::get_menu_event(const SDL_Event& event +[[nodiscard]] std::optional input::ConsoleJoystickGameInput::get_menu_event(const SDL_Event& event ) const { if (event.type == SDL_JOYBUTTONDOWN) { if (event.jbutton.which != underlying_input()->instance_id()) { - return helper::nullopt; + return std::nullopt; } const auto button = event.jbutton.button; @@ -936,7 +943,7 @@ helper::optional input::ConsoleJoystickGameInput::sdl_event_to_input } } - return helper::nullopt; + return std::nullopt; } diff --git a/src/input/joystick_input.hpp b/src/input/joystick_input.hpp index 3e3a6e80..add8bf27 100644 --- a/src/input/joystick_input.hpp +++ b/src/input/joystick_input.hpp @@ -1,9 +1,9 @@ #pragma once +#include +#include #include "guid.hpp" -#include "helper/expected.hpp" -#include "helper/parse_json.hpp" #include "input.hpp" #include "input/console_buttons.hpp" #include "input/game_input.hpp" @@ -44,7 +44,7 @@ namespace input { T open_settings; - [[nodiscard]] helper::expected validate() const { + [[nodiscard]] helper::expected validate() const { const std::vector to_use{ rotate_left, rotate_right, move_left, move_right, move_down, drop, hold, pause, open_settings }; @@ -88,7 +88,7 @@ namespace input { [[nodiscard]] virtual JoystickSettings default_settings() const = 0; - [[nodiscard]] static helper::optional> get_joystick_by_guid( + [[nodiscard]] static std::optional> get_joystick_by_guid( const sdl::GUID& guid, SDL_Joystick* joystick, SDL_JoystickID instance_id, @@ -147,8 +147,7 @@ namespace input { [[nodiscard]] virtual AbstractJoystickSettings default_settings_raw() const = 0; - [[nodiscard]] helper::optional handle_axis_navigation_event(const SDL_Event& event - ) const; + [[nodiscard]] std::optional handle_axis_navigation_event(const SDL_Event& event) const; }; #if defined(__SWITCH__) @@ -159,7 +158,7 @@ namespace input { }; SwitchJoystickInput_Type1(SDL_Joystick* joystick, SDL_JoystickID instance_id, const std::string& name); - [[nodiscard]] helper::optional get_navigation_event(const SDL_Event& event) const override; + [[nodiscard]] std::optional get_navigation_event(const SDL_Event& event) const override; [[nodiscard]] std::string describe_navigation_event(NavigationEvent event) const override; @@ -183,7 +182,7 @@ namespace input { }; _3DSJoystickInput_Type1(SDL_Joystick* joystick, SDL_JoystickID instance_id, const std::string& name); - [[nodiscard]] helper::optional get_navigation_event(const SDL_Event& event) const override; + [[nodiscard]] std::optional get_navigation_event(const SDL_Event& event) const override; [[nodiscard]] std::string describe_navigation_event(NavigationEvent event) const override; @@ -253,7 +252,7 @@ namespace input { void update(SimulationStep simulation_step_index) override; protected: - [[nodiscard]] virtual helper::optional sdl_event_to_input_event(const SDL_Event& event) const = 0; + [[nodiscard]] virtual std::optional sdl_event_to_input_event(const SDL_Event& event) const = 0; }; @@ -309,12 +308,12 @@ namespace input { virtual ~ConsoleJoystickGameInput(); - [[nodiscard]] helper::optional get_menu_event(const SDL_Event& event) const override; + [[nodiscard]] std::optional get_menu_event(const SDL_Event& event) const override; [[nodiscard]] std::string describe_menu_event(MenuEvent event) const override; protected: - [[nodiscard]] helper::optional sdl_event_to_input_event(const SDL_Event& event) const override; + [[nodiscard]] std::optional sdl_event_to_input_event(const SDL_Event& event) const override; }; #if !defined(__SWITCH__) && !defined(__3DS__) #error "unsupported console" diff --git a/src/input/keyboard_input.cpp b/src/input/keyboard_input.cpp index 679963f2..da37ce6e 100644 --- a/src/input/keyboard_input.cpp +++ b/src/input/keyboard_input.cpp @@ -1,14 +1,14 @@ -#include "keyboard_input.hpp" -#include "helper/optional.hpp" -#include "helper/utils.hpp" +#include + #include "input/game_input.hpp" #include "input/input.hpp" +#include "keyboard_input.hpp" input::KeyboardInput::KeyboardInput() : input::Input{ "keyboard", InputType::Keyboard } { } -[[nodiscard]] helper::optional input::KeyboardInput::get_navigation_event(const SDL_Event& event +[[nodiscard]] std::optional input::KeyboardInput::get_navigation_event(const SDL_Event& event ) const { @@ -50,7 +50,7 @@ input::KeyboardInput::KeyboardInput() : input::Input{ "keyboard", InputType::Key } } - return helper::nullopt; + return std::nullopt; } @@ -95,7 +95,7 @@ void input::KeyboardGameInput::update(SimulationStep simulation_step_index) { GameInput::update(simulation_step_index); } -helper::optional input::KeyboardGameInput::sdl_event_to_input_event(const SDL_Event& event) const { +std::optional input::KeyboardGameInput::sdl_event_to_input_event(const SDL_Event& event) const { if (event.type == SDL_KEYDOWN and event.key.repeat == 0) { const auto key = sdl::Key{ event.key.keysym }; if (key == m_settings.rotate_left) { @@ -143,7 +143,7 @@ helper::optional input::KeyboardGameInput::sdl_event_to_input_event( return InputEvent::HoldReleased; } } - return helper::nullopt; + return std::nullopt; } input::KeyboardGameInput::KeyboardGameInput(const KeyboardSettings& settings, EventDispatcher* event_dispatcher) @@ -163,7 +163,7 @@ input::KeyboardGameInput::KeyboardGameInput(KeyboardGameInput&& input) noexcept ) noexcept = default; -[[nodiscard]] helper::expected input::KeyboardSettings::validate() const { +[[nodiscard]] helper::expected input::KeyboardSettings::validate() const { const std::vector to_use{ rotate_left, rotate_right, move_left, move_right, move_down, drop, hold, pause, open_settings }; @@ -190,8 +190,7 @@ sdl::Key json_helper::get_key(const nlohmann::json& obj, const std::string& name } -[[nodiscard]] helper::optional input::KeyboardGameInput::get_menu_event(const SDL_Event& event -) const { +[[nodiscard]] std::optional input::KeyboardGameInput::get_menu_event(const SDL_Event& event) const { if (event.type == SDL_KEYDOWN and event.key.repeat == 0) { const auto key = sdl::Key{ event.key.keysym }; @@ -203,7 +202,7 @@ sdl::Key json_helper::get_key(const nlohmann::json& obj, const std::string& name } } - return helper::nullopt; + return std::nullopt; } [[nodiscard]] std::string input::KeyboardGameInput::describe_menu_event(MenuEvent event) const { diff --git a/src/input/keyboard_input.hpp b/src/input/keyboard_input.hpp index a09e5a12..ef2f5368 100644 --- a/src/input/keyboard_input.hpp +++ b/src/input/keyboard_input.hpp @@ -1,8 +1,9 @@ #pragma once +#include +#include + #include "game_input.hpp" -#include "helper/expected.hpp" -#include "helper/parse_json.hpp" #include "input.hpp" #include "manager/event_dispatcher.hpp" #include "manager/sdl_key.hpp" @@ -19,7 +20,7 @@ namespace input { public: KeyboardInput(); - [[nodiscard]] helper::optional get_navigation_event(const SDL_Event& event) const override; + [[nodiscard]] std::optional get_navigation_event(const SDL_Event& event) const override; [[nodiscard]] std::string describe_navigation_event(NavigationEvent event) const override; }; @@ -38,7 +39,7 @@ namespace input { sdl::Key open_settings; - [[nodiscard]] helper::expected validate() const; + [[nodiscard]] helper::expected validate() const; [[nodiscard]] static KeyboardSettings default_settings() { return KeyboardSettings{ .rotate_left = sdl::Key{ SDLK_LEFT }, @@ -77,14 +78,14 @@ namespace input { void update(SimulationStep simulation_step_index) override; - [[nodiscard]] helper::optional get_menu_event(const SDL_Event& event) const override; + [[nodiscard]] std::optional get_menu_event(const SDL_Event& event) const override; [[nodiscard]] std::string describe_menu_event(MenuEvent event) const override; [[nodiscard]] const KeyboardInput* underlying_input() const override; private: - [[nodiscard]] helper::optional sdl_event_to_input_event(const SDL_Event& event) const; + [[nodiscard]] std::optional sdl_event_to_input_event(const SDL_Event& event) const; }; } // namespace input diff --git a/src/input/mouse_input.cpp b/src/input/mouse_input.cpp index 0cd16ffc..f286b75d 100644 --- a/src/input/mouse_input.cpp +++ b/src/input/mouse_input.cpp @@ -1,8 +1,8 @@ +#include #include "mouse_input.hpp" -#include "graphics/point.hpp" -#include "helper/optional.hpp" + #include "input/input.hpp" @@ -35,16 +35,16 @@ input::MouseInput::MouseInput() : PointerInput("mouse") { } } -[[nodiscard]] helper::optional -input::MouseInput::get_navigation_event(const SDL_Event& /*event*/) const { - return helper::nullopt; +[[nodiscard]] std::optional input::MouseInput::get_navigation_event(const SDL_Event& /*event*/) + const { + return std::nullopt; } [[nodiscard]] std::string input::MouseInput::describe_navigation_event(NavigationEvent /*event*/) const { throw std::runtime_error("not supported"); } -[[nodiscard]] helper::optional input::MouseInput::get_pointer_event(const SDL_Event& event +[[nodiscard]] std::optional input::MouseInput::get_pointer_event(const SDL_Event& event ) const { auto pointer_event = input::PointerEvent::PointerUp; @@ -66,11 +66,11 @@ input::MouseInput::get_navigation_event(const SDL_Event& /*event*/) const { case SDL_MOUSEBUTTONUP: break; default: - return helper::nullopt; + return std::nullopt; } if (event.button.button != SDL_BUTTON_LEFT) { - return helper::nullopt; + return std::nullopt; } const shapes::IPoint pos{ event.button.x, event.button.y }; diff --git a/src/input/mouse_input.hpp b/src/input/mouse_input.hpp index 4ac37c82..cd3a4c4d 100644 --- a/src/input/mouse_input.hpp +++ b/src/input/mouse_input.hpp @@ -9,11 +9,11 @@ namespace input { public: MouseInput(); - [[nodiscard]] helper::optional get_navigation_event(const SDL_Event& event) const override; + [[nodiscard]] std::optional get_navigation_event(const SDL_Event& event) const override; [[nodiscard]] std::string describe_navigation_event(NavigationEvent event) const override; - [[nodiscard]] helper::optional get_pointer_event(const SDL_Event& event) const override; + [[nodiscard]] std::optional get_pointer_event(const SDL_Event& event) const override; [[nodiscard]] SDL_Event offset_pointer_event(const SDL_Event& event, const shapes::IPoint& point) const override; diff --git a/src/input/replay_input.cpp b/src/input/replay_input.cpp index ccb99119..5913ffc0 100644 --- a/src/input/replay_input.cpp +++ b/src/input/replay_input.cpp @@ -1,7 +1,7 @@ #include "replay_input.hpp" #include "game/tetrion.hpp" -#include "helper/magic_enum_wrapper.hpp" -#include "helper/optional.hpp" +#include + input::ReplayGameInput::ReplayGameInput( std::shared_ptr recording_reader, @@ -81,9 +81,8 @@ void input::ReplayGameInput::late_update(const SimulationStep simulation_step_in } -[[nodiscard]] helper::optional input::ReplayGameInput::get_menu_event(const SDL_Event& /*event*/) - const { - return helper::nullopt; +[[nodiscard]] std::optional input::ReplayGameInput::get_menu_event(const SDL_Event& /*event*/) const { + return std::nullopt; } [[nodiscard]] std::string input::ReplayGameInput::describe_menu_event(MenuEvent /*event*/) const { diff --git a/src/input/replay_input.hpp b/src/input/replay_input.hpp index d4301b5a..3d00afa8 100644 --- a/src/input/replay_input.hpp +++ b/src/input/replay_input.hpp @@ -1,7 +1,8 @@ #pragma once +#include + #include "game_input.hpp" -#include "recordings/recording_reader.hpp" #include @@ -21,7 +22,7 @@ namespace input { void update(SimulationStep simulation_step_index) override; void late_update(SimulationStep simulation_step_index) override; - [[nodiscard]] helper::optional get_menu_event(const SDL_Event& event) const override; + [[nodiscard]] std::optional get_menu_event(const SDL_Event& event) const override; [[nodiscard]] std::string describe_menu_event(MenuEvent event) const override; diff --git a/src/input/touch_input.cpp b/src/input/touch_input.cpp index febc580f..b7654f01 100644 --- a/src/input/touch_input.cpp +++ b/src/input/touch_input.cpp @@ -1,9 +1,9 @@ -#include "touch_input.hpp" -#include "graphics/point.hpp" -#include "helper/optional.hpp" -#include "helper/utils.hpp" +#include +#include + #include "input/game_input.hpp" #include "input/input.hpp" +#include "touch_input.hpp" #include #include @@ -26,11 +26,11 @@ void input::TouchGameInput::update(SimulationStep simulation_step_index) { GameInput::update(simulation_step_index); } -helper::optional input::TouchGameInput::sdl_event_to_input_event(const SDL_Event& event) { +std::optional input::TouchGameInput::sdl_event_to_input_event(const SDL_Event& event) { if (event.tfinger.touchId != m_underlying_input->m_id) { - return helper::nullopt; + return std::nullopt; } //TODO(Totto): to handle those things better, holding has to be supported @@ -44,7 +44,7 @@ helper::optional input::TouchGameInput::sdl_event_to_input_event(con if (m_finger_state.contains(finger_id) and m_finger_state.at(finger_id).has_value()) { // there are some valid reasons, this can occur now - return helper::nullopt; + return std::nullopt; } const auto x_pos = event.tfinger.x; @@ -53,7 +53,7 @@ helper::optional input::TouchGameInput::sdl_event_to_input_event(con m_finger_state.insert_or_assign( finger_id, - helper::optional{ + std::optional{ PressedState{ timestamp, x_pos, y_pos } } ); @@ -68,13 +68,13 @@ helper::optional input::TouchGameInput::sdl_event_to_input_event(con if (!m_finger_state.contains(finger_id)) { // there are some valid reasons, this can occur now - return helper::nullopt; + return std::nullopt; } const auto& finger_state = m_finger_state.at(finger_id); if (!finger_state.has_value()) { - return helper::nullopt; + return std::nullopt; } const auto& pressed_state = finger_state.value(); @@ -95,7 +95,7 @@ helper::optional input::TouchGameInput::sdl_event_to_input_event(con const auto threshold_x = m_settings.move_x_threshold; const auto threshold_y = m_settings.move_y_threshold; - m_finger_state.insert_or_assign(finger_id, helper::nullopt); + m_finger_state.insert_or_assign(finger_id, std::nullopt); if (duration < m_settings.rotation_duration_threshold) { if (dx_abs < threshold_x and dy_abs < threshold_y) { // tap on the right side of the screen @@ -138,7 +138,7 @@ helper::optional input::TouchGameInput::sdl_event_to_input_event(con } - return helper::nullopt; + return std::nullopt; } @@ -162,13 +162,13 @@ input::TouchGameInput::~TouchGameInput() { input::TouchGameInput::TouchGameInput(TouchGameInput&& input) noexcept = default; [[nodiscard]] input::TouchGameInput& input::TouchGameInput::operator=(TouchGameInput&& input) noexcept = default; -[[nodiscard]] helper::optional input::TouchGameInput::get_menu_event(const SDL_Event& event) const { +[[nodiscard]] std::optional input::TouchGameInput::get_menu_event(const SDL_Event& event) const { if (event.type == SDL_KEYDOWN and event.key.keysym.sym == SDLK_AC_BACK) { return MenuEvent::Pause; } - return helper::nullopt; + return std::nullopt; } [[nodiscard]] std::string input::TouchGameInput::describe_menu_event(MenuEvent event) const { @@ -213,14 +213,14 @@ input::TouchInput::get_by_device_index(const std::shared_ptr& window, in } -[[nodiscard]] helper::optional input::TouchInput::get_navigation_event(const SDL_Event& event +[[nodiscard]] std::optional input::TouchInput::get_navigation_event(const SDL_Event& event ) const { //technically no touch event, but it's a navigation event, and by APi design it can also handle those if (event.type == SDL_KEYDOWN and event.key.keysym.sym == SDLK_AC_BACK) { return input::NavigationEvent::BACK; } - return helper::nullopt; + return std::nullopt; } [[nodiscard]] std::string input::TouchInput::describe_navigation_event(NavigationEvent event) const { @@ -239,7 +239,7 @@ input::TouchInput::get_by_device_index(const std::shared_ptr& window, in } } -[[nodiscard]] helper::optional input::TouchInput::get_pointer_event(const SDL_Event& event +[[nodiscard]] std::optional input::TouchInput::get_pointer_event(const SDL_Event& event ) const { auto pointer_event = input::PointerEvent::PointerUp; @@ -254,11 +254,11 @@ input::TouchInput::get_by_device_index(const std::shared_ptr& window, in case SDL_FINGERUP: break; default: - return helper::nullopt; + return std::nullopt; } if (event.tfinger.touchId != m_id) { - return helper::nullopt; + return std::nullopt; } // These are doubles, from 0-1 (or if using virtual layouts > 0) in percent, the have to be casted to absolut x coordinates! @@ -299,7 +299,7 @@ input::TouchInput::get_by_device_index(const std::shared_ptr& window, in return new_event; } -[[nodiscard]] helper::expected input::TouchSettings::validate() const { +[[nodiscard]] helper::expected input::TouchSettings::validate() const { if (move_x_threshold > 1.0 || move_x_threshold < 0.0) { return helper::unexpected{ @@ -313,7 +313,7 @@ input::TouchInput::get_by_device_index(const std::shared_ptr& window, in }; } - return true; + return {}; } diff --git a/src/input/touch_input.hpp b/src/input/touch_input.hpp index 4b75dcbc..134c9e9c 100644 --- a/src/input/touch_input.hpp +++ b/src/input/touch_input.hpp @@ -1,8 +1,8 @@ #pragma once +#include +#include -#include "helper/expected.hpp" -#include "helper/parse_json.hpp" #include "input.hpp" #include "input/game_input.hpp" #include "manager/event_dispatcher.hpp" @@ -20,11 +20,11 @@ namespace input { [[nodiscard]] static helper::expected, std::string> get_by_device_index(const std::shared_ptr& window, int device_index); - [[nodiscard]] helper::optional get_navigation_event(const SDL_Event& event) const override; + [[nodiscard]] std::optional get_navigation_event(const SDL_Event& event) const override; [[nodiscard]] std::string describe_navigation_event(NavigationEvent event) const override; - [[nodiscard]] helper::optional get_pointer_event(const SDL_Event& event) const override; + [[nodiscard]] std::optional get_pointer_event(const SDL_Event& event) const override; [[nodiscard]] SDL_Event offset_pointer_event(const SDL_Event& event, const shapes::IPoint& point) const override; @@ -45,7 +45,7 @@ namespace input { u32 rotation_duration_threshold; u32 drop_duration_threshold; - [[nodiscard]] helper::expected validate() const; + [[nodiscard]] helper::expected validate() const; [[nodiscard]] static TouchSettings default_settings() { return TouchSettings{ .move_x_threshold = 150.0 / 2160.0, @@ -70,7 +70,7 @@ namespace input { private: TouchSettings m_settings; - std::unordered_map> m_finger_state; + std::unordered_map> m_finger_state; std::vector m_event_buffer; EventDispatcher* m_event_dispatcher; TouchInput* m_underlying_input; @@ -94,14 +94,14 @@ namespace input { void handle_event(const SDL_Event& event) override; void update(SimulationStep simulation_step_index) override; - [[nodiscard]] helper::optional get_menu_event(const SDL_Event& event) const override; + [[nodiscard]] std::optional get_menu_event(const SDL_Event& event) const override; [[nodiscard]] std::string describe_menu_event(MenuEvent event) const override; [[nodiscard]] const TouchInput* underlying_input() const override; private: - [[nodiscard]] helper::optional sdl_event_to_input_event(const SDL_Event& event); + [[nodiscard]] std::optional sdl_event_to_input_event(const SDL_Event& event); }; } // namespace input @@ -116,7 +116,7 @@ namespace json_helper { template [[nodiscard]] T get_number(const nlohmann::json& obj, const std::string& name) { - helper::expected error = true; + helper::expected error{}; auto context = obj.at(name); diff --git a/src/libs/core/core.hpp b/src/libs/core/core.hpp new file mode 100644 index 00000000..9c15c3dd --- /dev/null +++ b/src/libs/core/core.hpp @@ -0,0 +1,33 @@ + + +#pragma once + +// this is a easy public header, that you can include, to get all header of liboopetris_core + + +#include "./game/grid_properties.hpp" +#include "./game/mino.hpp" +#include "./game/mino_stack.hpp" +#include "./game/tetromino_type.hpp" + +#include "./hash-library/sha256.h" + +#include "./helper/bool_wrapper.hpp" +#include "./helper/color.hpp" +#include "./helper/color_literals.hpp" +#include "./helper/const_utils.hpp" +#include "./helper/date.hpp" +#include "./helper/errors.hpp" +#include "./helper/expected.hpp" +#include "./helper/input_event.hpp" +#include "./helper/magic_enum_wrapper.hpp" +#include "./helper/parse_json.hpp" +#include "./helper/point.hpp" +#include "./helper/random.hpp" +#include "./helper/sleep.hpp" +#include "./helper/static_string.hpp" +#include "./helper/string_manipulation.hpp" +#include "./helper/timer.hpp" +#include "./helper/types.hpp" +#include "./helper/utils.hpp" +#include "./helper/versions.hpp" diff --git a/src/game/grid_properties.hpp b/src/libs/core/game/grid_properties.hpp similarity index 96% rename from src/game/grid_properties.hpp rename to src/libs/core/game/grid_properties.hpp index 4aa6d41b..0d67c5a0 100644 --- a/src/game/grid_properties.hpp +++ b/src/libs/core/game/grid_properties.hpp @@ -2,7 +2,7 @@ #pragma once -#include "graphics/point.hpp" +#include "../helper/point.hpp" namespace grid { diff --git a/src/libs/core/game/meson.build b/src/libs/core/game/meson.build new file mode 100644 index 00000000..c5f6f48c --- /dev/null +++ b/src/libs/core/game/meson.build @@ -0,0 +1,20 @@ +core_src_files += files( + 'mino.cpp', + 'mino_stack.cpp', + 'tetromino_type.cpp', +) + +_header_files = files( + 'grid_properties.hpp', + 'mino.hpp', + 'mino_stack.hpp', + 'tetromino_type.hpp', +) + +core_header_files += _header_files + +install_headers( + _header_files, + install_dir: core_include_dir / 'game', + preserve_path: true, +) diff --git a/src/game/mino.cpp b/src/libs/core/game/mino.cpp similarity index 95% rename from src/game/mino.cpp rename to src/libs/core/game/mino.cpp index 3b22ff96..0856d8d4 100644 --- a/src/game/mino.cpp +++ b/src/libs/core/game/mino.cpp @@ -1,4 +1,4 @@ -#include "mino.hpp" +#include "./mino.hpp" [[nodiscard]] helper::TetrominoType Mino::type() const { return m_type; diff --git a/src/game/mino.hpp b/src/libs/core/game/mino.hpp similarity index 83% rename from src/game/mino.hpp rename to src/libs/core/game/mino.hpp index 1e14123e..016a9759 100644 --- a/src/game/mino.hpp +++ b/src/libs/core/game/mino.hpp @@ -1,9 +1,9 @@ #pragma once -#include "graphics/point.hpp" -#include "grid_properties.hpp" -#include "helper/types.hpp" -#include "tetromino_type.hpp" +#include "../helper/point.hpp" +#include "../helper/types.hpp" +#include "./grid_properties.hpp" +#include "./tetromino_type.hpp" #include struct Mino final { diff --git a/src/game/mino_stack.cpp b/src/libs/core/game/mino_stack.cpp similarity index 96% rename from src/game/mino_stack.cpp rename to src/libs/core/game/mino_stack.cpp index 770774d6..a4b655fe 100644 --- a/src/game/mino_stack.cpp +++ b/src/libs/core/game/mino_stack.cpp @@ -1,6 +1,6 @@ -#include "mino_stack.hpp" -#include "grid_properties.hpp" -#include "helper/magic_enum_wrapper.hpp" +#include "./mino_stack.hpp" +#include "./grid_properties.hpp" +#include "./helper/magic_enum_wrapper.hpp" #include diff --git a/src/game/mino_stack.hpp b/src/libs/core/game/mino_stack.hpp similarity index 92% rename from src/game/mino_stack.hpp rename to src/libs/core/game/mino_stack.hpp index fc9a62a6..161c74b9 100644 --- a/src/game/mino_stack.hpp +++ b/src/libs/core/game/mino_stack.hpp @@ -1,7 +1,7 @@ #pragma once -#include "helper/types.hpp" -#include "mino.hpp" +#include "../helper/types.hpp" +#include "./mino.hpp" #include diff --git a/src/game/tetromino_type.cpp b/src/libs/core/game/tetromino_type.cpp similarity index 97% rename from src/game/tetromino_type.cpp rename to src/libs/core/game/tetromino_type.cpp index 2cc949c2..944dfb3b 100644 --- a/src/game/tetromino_type.cpp +++ b/src/libs/core/game/tetromino_type.cpp @@ -1,5 +1,5 @@ -#include "tetromino_type.hpp" +#include "./tetromino_type.hpp" [[nodiscard]] Color helper::get_foreground_color(TetrominoType type, u8 alpha) { diff --git a/src/game/tetromino_type.hpp b/src/libs/core/game/tetromino_type.hpp similarity index 85% rename from src/game/tetromino_type.hpp rename to src/libs/core/game/tetromino_type.hpp index e88875f5..5ed6e5c4 100644 --- a/src/game/tetromino_type.hpp +++ b/src/libs/core/game/tetromino_type.hpp @@ -1,6 +1,8 @@ #pragma once -#include "helper/color.hpp" +#include "../helper/color.hpp" +#include "../helper/types.hpp" + namespace helper { enum class TetrominoType : u8 { diff --git a/src/thirdparty/hash-library/LICENSE b/src/libs/core/hash-library/LICENSE similarity index 100% rename from src/thirdparty/hash-library/LICENSE rename to src/libs/core/hash-library/LICENSE diff --git a/src/libs/core/hash-library/meson.build b/src/libs/core/hash-library/meson.build new file mode 100644 index 00000000..df1ddcc6 --- /dev/null +++ b/src/libs/core/hash-library/meson.build @@ -0,0 +1,15 @@ +core_src_files += files( + 'sha256.cpp', +) + +_header_files = files( + 'sha256.h', +) + +core_header_files += _header_files + +install_headers( + _header_files, + install_dir: core_include_dir / 'hash-library', + preserve_path: true, +) diff --git a/src/thirdparty/hash-library/readme.md b/src/libs/core/hash-library/readme.md similarity index 100% rename from src/thirdparty/hash-library/readme.md rename to src/libs/core/hash-library/readme.md diff --git a/src/thirdparty/hash-library/sha256.cpp b/src/libs/core/hash-library/sha256.cpp similarity index 99% rename from src/thirdparty/hash-library/sha256.cpp rename to src/libs/core/hash-library/sha256.cpp index 66edea18..d085a97e 100644 --- a/src/thirdparty/hash-library/sha256.cpp +++ b/src/libs/core/hash-library/sha256.cpp @@ -6,7 +6,7 @@ // Altered by Totto16 to fix some bugs and compiler warnings / errors -#include "sha256.h" +#include "./sha256.h" // big endian architectures need #define __BYTE_ORDER __BIG_ENDIAN #if defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) diff --git a/src/thirdparty/hash-library/sha256.h b/src/libs/core/hash-library/sha256.h similarity index 100% rename from src/thirdparty/hash-library/sha256.h rename to src/libs/core/hash-library/sha256.h diff --git a/src/thirdparty/hash-library/url.txt b/src/libs/core/hash-library/url.txt similarity index 100% rename from src/thirdparty/hash-library/url.txt rename to src/libs/core/hash-library/url.txt diff --git a/src/libs/core/helper/bool_wrapper.hpp b/src/libs/core/helper/bool_wrapper.hpp new file mode 100644 index 00000000..32c41eb7 --- /dev/null +++ b/src/libs/core/helper/bool_wrapper.hpp @@ -0,0 +1,41 @@ +#pragma once + + +#include + +namespace helper { + + template + struct BoolWrapper { + private: + bool m_value; + std::optional m_additional; + + public: + BoolWrapper(bool value) //NOLINT(google-explicit-constructor) + : m_value{ value }, + m_additional{ std::nullopt } { } + + BoolWrapper(bool value, const T& additional) : m_value{ value }, m_additional{ additional } { } + + BoolWrapper(bool value, const std::optional& additional) : m_value{ value }, m_additional{ additional } { } + + const std::optional& get_additional() const { + return m_additional; + } + + [[nodiscard]] bool has_additional() const { + return m_additional.has_value(); + } + + + bool is(const T& value) const { + return value == m_additional; + } + + operator bool() const { //NOLINT(google-explicit-constructor) + return m_value; + } + }; + +} // namespace helper diff --git a/src/helper/color.cpp b/src/libs/core/helper/color.cpp similarity index 97% rename from src/helper/color.cpp rename to src/libs/core/helper/color.cpp index f6a0fc16..51b24a72 100644 --- a/src/helper/color.cpp +++ b/src/libs/core/helper/color.cpp @@ -1,9 +1,9 @@ -#include "color.hpp" -#include "color_literals.hpp" -#include "helper/expected.hpp" -#include "helper/utils.hpp" +#include "./color.hpp" +#include "./color_literals.hpp" +#include "./helper/expected.hpp" +#include "./helper/utils.hpp" #include diff --git a/src/helper/color.hpp b/src/libs/core/helper/color.hpp similarity index 98% rename from src/helper/color.hpp rename to src/libs/core/helper/color.hpp index 81090220..8b6e1f48 100644 --- a/src/helper/color.hpp +++ b/src/libs/core/helper/color.hpp @@ -1,9 +1,9 @@ #pragma once -#include "const_utils.hpp" -#include "helper/expected.hpp" -#include "helper/types.hpp" -#include "helper/utils.hpp" +#include "./const_utils.hpp" +#include "./expected.hpp" +#include "./types.hpp" +#include "./utils.hpp" #include #include diff --git a/src/helper/color_literals.hpp b/src/libs/core/helper/color_literals.hpp similarity index 99% rename from src/helper/color_literals.hpp rename to src/libs/core/helper/color_literals.hpp index 8d0f5cee..d96fcd5b 100644 --- a/src/helper/color_literals.hpp +++ b/src/libs/core/helper/color_literals.hpp @@ -1,8 +1,8 @@ #pragma once -#include "color.hpp" -#include "const_utils.hpp" -#include "helper/types.hpp" +#include "./color.hpp" +#include "./const_utils.hpp" +#include "./types.hpp" #include #include diff --git a/src/helper/const_utils.hpp b/src/libs/core/helper/const_utils.hpp similarity index 99% rename from src/helper/const_utils.hpp rename to src/libs/core/helper/const_utils.hpp index 0dda909b..233348b6 100644 --- a/src/helper/const_utils.hpp +++ b/src/libs/core/helper/const_utils.hpp @@ -1,7 +1,7 @@ #pragma once -#include "helper/utils.hpp" +#include "./utils.hpp" #include #include diff --git a/src/helper/date.cpp b/src/libs/core/helper/date.cpp similarity index 99% rename from src/helper/date.cpp rename to src/libs/core/helper/date.cpp index c938404d..dc78b9e8 100644 --- a/src/helper/date.cpp +++ b/src/libs/core/helper/date.cpp @@ -1,5 +1,5 @@ -#include "date.hpp" +#include "./date.hpp" #include #include diff --git a/src/helper/date.hpp b/src/libs/core/helper/date.hpp similarity index 93% rename from src/helper/date.hpp rename to src/libs/core/helper/date.hpp index 95dfc44c..36f50b47 100644 --- a/src/helper/date.hpp +++ b/src/libs/core/helper/date.hpp @@ -1,7 +1,7 @@ #pragma once -#include "helper/expected.hpp" -#include "helper/types.hpp" +#include "./expected.hpp" +#include "./types.hpp" #include #include diff --git a/src/helper/errors.cpp b/src/libs/core/helper/errors.cpp similarity index 93% rename from src/helper/errors.cpp rename to src/libs/core/helper/errors.cpp index 659dc7f0..a8377eed 100644 --- a/src/helper/errors.cpp +++ b/src/libs/core/helper/errors.cpp @@ -1,4 +1,4 @@ -#include "errors.hpp" +#include "./errors.hpp" helper::GeneralError::GeneralError(const std::string& message, error::Severity severity) noexcept : m_message{ message }, @@ -21,6 +21,11 @@ helper::GeneralError::GeneralError(GeneralError&& error) noexcept = default; return m_message; } +[[nodiscard]] const char* helper::GeneralError::what() const noexcept { + return m_message.c_str(); +} + + [[nodiscard]] helper::error::Severity helper::GeneralError::severity() const { return m_severity; } diff --git a/src/helper/errors.hpp b/src/libs/core/helper/errors.hpp similarity index 95% rename from src/helper/errors.hpp rename to src/libs/core/helper/errors.hpp index f1c6550f..1c66dfad 100644 --- a/src/helper/errors.hpp +++ b/src/libs/core/helper/errors.hpp @@ -31,6 +31,8 @@ namespace helper { [[nodiscard]] const std::string& message() const; [[nodiscard]] error::Severity severity() const; + + [[nodiscard]] const char* what() const noexcept override; }; struct FatalError : public GeneralError { diff --git a/src/helper/expected.hpp b/src/libs/core/helper/expected.hpp similarity index 100% rename from src/helper/expected.hpp rename to src/libs/core/helper/expected.hpp diff --git a/src/manager/input_event.hpp b/src/libs/core/helper/input_event.hpp similarity index 92% rename from src/manager/input_event.hpp rename to src/libs/core/helper/input_event.hpp index a2f4dcfa..a0fb4642 100644 --- a/src/manager/input_event.hpp +++ b/src/libs/core/helper/input_event.hpp @@ -1,6 +1,6 @@ #pragma once -#include "helper/types.hpp" +#include "./types.hpp" enum class InputEvent : u8 { RotateLeftPressed, diff --git a/src/helper/magic_enum_wrapper.hpp b/src/libs/core/helper/magic_enum_wrapper.hpp similarity index 86% rename from src/helper/magic_enum_wrapper.hpp rename to src/libs/core/helper/magic_enum_wrapper.hpp index d62cd664..b3b64dd7 100644 --- a/src/helper/magic_enum_wrapper.hpp +++ b/src/libs/core/helper/magic_enum_wrapper.hpp @@ -8,4 +8,4 @@ #define MAGIC_ENUM_RANGE_MAX 256 // NOLINT(cppcoreguidelines-macro-usage) #endif -#include +#include diff --git a/src/libs/core/helper/meson.build b/src/libs/core/helper/meson.build new file mode 100644 index 00000000..b0e2eecf --- /dev/null +++ b/src/libs/core/helper/meson.build @@ -0,0 +1,38 @@ +core_src_files += files( + 'color.cpp', + 'date.cpp', + 'errors.cpp', + 'parse_json.cpp', + 'random.cpp', + 'sleep.cpp', + 'string_manipulation.cpp', +) + +_header_files = files( + 'bool_wrapper.hpp', + 'color.hpp', + 'color_literals.hpp', + 'const_utils.hpp', + 'date.hpp', + 'errors.hpp', + 'expected.hpp', + 'input_event.hpp', + 'magic_enum_wrapper.hpp', + 'parse_json.hpp', + 'point.hpp', + 'random.hpp', + 'sleep.hpp', + 'static_string.hpp', + 'string_manipulation.hpp', + 'timer.hpp', + 'types.hpp', + 'utils.hpp', + 'versions.hpp', +) +core_header_files += _header_files + +install_headers( + _header_files, + install_dir: core_include_dir / 'helper', + preserve_path: true, +) diff --git a/src/helper/parse_json.cpp b/src/libs/core/helper/parse_json.cpp similarity index 98% rename from src/helper/parse_json.cpp rename to src/libs/core/helper/parse_json.cpp index 76415fc5..1a05b99d 100644 --- a/src/helper/parse_json.cpp +++ b/src/libs/core/helper/parse_json.cpp @@ -1,6 +1,6 @@ -#include "parse_json.hpp" +#include "./parse_json.hpp" std::string json::get_json_type(const nlohmann::json::value_t& type) { diff --git a/src/helper/parse_json.hpp b/src/libs/core/helper/parse_json.hpp similarity index 70% rename from src/helper/parse_json.hpp rename to src/libs/core/helper/parse_json.hpp index ceb790f1..cd6923f2 100644 --- a/src/helper/parse_json.hpp +++ b/src/libs/core/helper/parse_json.hpp @@ -8,8 +8,8 @@ #include -#include "helper/expected.hpp" -#include "helper/optional.hpp" +#include "./expected.hpp" + #include #include @@ -19,12 +19,12 @@ // START: general json parser helper -//helper for helper::optional json conversion +//helper for std::optional json conversion NLOHMANN_JSON_NAMESPACE_BEGIN template -struct adl_serializer> { - static void to_json(json& obj, const helper::optional& opt) { +struct adl_serializer> { + static void to_json(json& obj, const std::optional& opt) { if (not opt) { obj = nullptr; } else { @@ -33,9 +33,9 @@ struct adl_serializer> { } } - static void from_json(const json& obj, helper::optional& opt) { + static void from_json(const json& obj, std::optional& opt) { if (obj.is_null()) { - opt = helper::nullopt; + opt = std::nullopt; } else { opt = obj.template get(); // same as above, but with // adl_serializer::from_json @@ -49,9 +49,8 @@ NLOHMANN_JSON_NAMESPACE_END namespace json { - template - [[nodiscard]] helper::expected try_parse_json(const std::string& content) { + [[nodiscard]] helper::expected try_parse_json(const std::string& content) noexcept { try { T result = nlohmann::json::parse(content); @@ -69,7 +68,7 @@ namespace json { } template - [[nodiscard]] helper::expected try_parse_json_file(const std::filesystem::path& file) { + [[nodiscard]] helper::expected try_parse_json_file(const std::filesystem::path& file) noexcept { if (not std::filesystem::exists(file)) { return helper::unexpected{ fmt::format("File '{}' doesn't exist", file.string()) }; @@ -88,17 +87,38 @@ namespace json { } + template + [[nodiscard]] helper::expected try_convert_to_json(const T& input) noexcept { + + try { + nlohmann::json value = input; + return value; + } catch (nlohmann::json::type_error& type_error) { + return helper::unexpected{ fmt::format("type error: {}", type_error.what()) }; + } catch (nlohmann::json::exception& exception) { + return helper::unexpected{ fmt::format("unknown json exception: {}", exception.what()) }; + } catch (std::exception& exception) { + return helper::unexpected{ fmt::format("unknown exception: {}", exception.what()) }; + } + } + + template [[nodiscard]] helper::expected - try_json_to_string(const T& type, const bool pretty = false) { + try_json_to_string(const T& type, const bool pretty = false) noexcept { try { - const nlohmann::json value = type; + auto value = try_convert_to_json(type); + + if (not value.has_value()) { + return helper::unexpected{ value.error() }; + } + if (pretty) { - return value.dump(1, '\t'); + return value.value().dump(1, '\t'); } - return value.dump(-1, ' '); + return value.value().dump(-1, ' '); } catch (nlohmann::json::type_error& type_error) { return helper::unexpected{ fmt::format("type error: {}", type_error.what()) }; diff --git a/src/graphics/point.hpp b/src/libs/core/helper/point.hpp similarity index 99% rename from src/graphics/point.hpp rename to src/libs/core/helper/point.hpp index b0849901..d7443388 100644 --- a/src/graphics/point.hpp +++ b/src/libs/core/helper/point.hpp @@ -4,7 +4,7 @@ #include #include -#include "helper/types.hpp" +#include "../helper/types.hpp" namespace shapes { template diff --git a/src/helper/random.cpp b/src/libs/core/helper/random.cpp similarity index 93% rename from src/helper/random.cpp rename to src/libs/core/helper/random.cpp index 0481b7f5..de74f572 100644 --- a/src/helper/random.cpp +++ b/src/libs/core/helper/random.cpp @@ -1,4 +1,5 @@ -#include "helper/random.hpp" +#include "./helper/random.hpp" + #include Random::Random() : Random{ generate_seed() } { } diff --git a/src/helper/random.hpp b/src/libs/core/helper/random.hpp similarity index 96% rename from src/helper/random.hpp rename to src/libs/core/helper/random.hpp index 226a5e89..06c913ec 100644 --- a/src/helper/random.hpp +++ b/src/libs/core/helper/random.hpp @@ -1,6 +1,6 @@ #pragma once -#include "helper/utils.hpp" +#include "./utils.hpp" #include diff --git a/src/helper/sleep.cpp b/src/libs/core/helper/sleep.cpp similarity index 98% rename from src/helper/sleep.cpp rename to src/libs/core/helper/sleep.cpp index 18049cd1..9ddde9d2 100644 --- a/src/helper/sleep.cpp +++ b/src/libs/core/helper/sleep.cpp @@ -1,7 +1,7 @@ -#include "sleep.hpp" +#include "./sleep.hpp" #if defined(_MSC_VER) #ifndef NOMINMAX diff --git a/src/helper/sleep.hpp b/src/libs/core/helper/sleep.hpp similarity index 100% rename from src/helper/sleep.hpp rename to src/libs/core/helper/sleep.hpp diff --git a/src/helper/static_string.hpp b/src/libs/core/helper/static_string.hpp similarity index 99% rename from src/helper/static_string.hpp rename to src/libs/core/helper/static_string.hpp index 5b4a96e0..f7ec92f1 100644 --- a/src/helper/static_string.hpp +++ b/src/libs/core/helper/static_string.hpp @@ -1,6 +1,6 @@ #pragma once -#include "helper/types.hpp" +#include "./types.hpp" #include #include diff --git a/src/helper/string_manipulation.cpp b/src/libs/core/helper/string_manipulation.cpp similarity index 97% rename from src/helper/string_manipulation.cpp rename to src/libs/core/helper/string_manipulation.cpp index 881ae20c..30bcc4d7 100644 --- a/src/helper/string_manipulation.cpp +++ b/src/libs/core/helper/string_manipulation.cpp @@ -1,5 +1,5 @@ -#include "string_manipulation.hpp" +#include "./string_manipulation.hpp" #include #include diff --git a/src/helper/string_manipulation.hpp b/src/libs/core/helper/string_manipulation.hpp similarity index 100% rename from src/helper/string_manipulation.hpp rename to src/libs/core/helper/string_manipulation.hpp diff --git a/src/helper/timer.hpp b/src/libs/core/helper/timer.hpp similarity index 100% rename from src/helper/timer.hpp rename to src/libs/core/helper/timer.hpp diff --git a/src/helper/types.hpp b/src/libs/core/helper/types.hpp similarity index 100% rename from src/helper/types.hpp rename to src/libs/core/helper/types.hpp diff --git a/src/helper/utils.hpp b/src/libs/core/helper/utils.hpp similarity index 83% rename from src/helper/utils.hpp rename to src/libs/core/helper/utils.hpp index f88aa08b..2260b841 100644 --- a/src/helper/utils.hpp +++ b/src/libs/core/helper/utils.hpp @@ -1,7 +1,7 @@ #pragma once -#include "helper/optional.hpp" -#include "helper/types.hpp" + +#include "./types.hpp" #include #include @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -94,26 +95,38 @@ namespace utils { template - constexpr helper::optional is_child_class(S* parent) { + constexpr std::optional is_child_class(S* parent) { auto* result = dynamic_cast(parent); if (result == nullptr) { - return helper::nullopt; + return std::nullopt; } return result; } template - constexpr helper::optional is_child_class(const std::unique_ptr& parent) { + constexpr std::optional is_child_class(const std::unique_ptr& parent) { return is_child_class(parent.get()); } template - constexpr helper::optional is_child_class(const std::shared_ptr& parent) { + constexpr std::optional is_child_class(const std::shared_ptr& parent) { return is_child_class(parent.get()); } + template + struct IsIterator : std::false_type { }; + + template + struct IsIterator::iterator_category>> : std::true_type { }; + +#if __cpp_static_assert >= 202306L +#define STATIC_ASSERT_WITH_MESSAGE(check, msg) static_assert(check, msg); // NOLINT(cppcoreguidelines-macro-usage) +#else +#define STATIC_ASSERT_WITH_MESSAGE(check, msg) static_assert(check); // NOLINT(cppcoreguidelines-macro-usage) +#endif + } // namespace utils diff --git a/src/libs/core/helper/versions.hpp b/src/libs/core/helper/versions.hpp new file mode 100644 index 00000000..d4c48eda --- /dev/null +++ b/src/libs/core/helper/versions.hpp @@ -0,0 +1,27 @@ +#include + + +namespace utils { + + +#define STRINGIFY(a) STRINGIFY_HELPER_(a) //NOLINT(cppcoreguidelines-macro-usage) +#define STRINGIFY_HELPER_(a) #a //NOLINT(cppcoreguidelines-macro-usage) + + +#if !defined(OOPETRIS_VERSION) +#error "OOPETRIS_VERSION not defined" +#endif + + + [[nodiscard]] inline std::string version() { + + + return STRINGIFY(OOPETRIS_VERSION); + } + + +#undef STRINGIFY +#undef STRINGIFY_HELPER_ + + +} // namespace utils diff --git a/src/libs/core/meson.build b/src/libs/core/meson.build new file mode 100644 index 00000000..89c80342 --- /dev/null +++ b/src/libs/core/meson.build @@ -0,0 +1,53 @@ +core_src_files = [] +core_header_files = [files('core.hpp')] + +core_include_dir = include_dir / 'oopetris' / 'core' + +subdir('game') +subdir('hash-library') +subdir('helper') + +liboopetris_core = library( + 'oopetris_core', + core_src_files, + core_header_files, + include_directories: core_lib.get('inc_dirs'), + dependencies: core_lib.get('deps'), + cpp_args: core_lib.get('compile_args'), + override_options: { + 'warning_level': '3', + 'werror': true, + }, + version: meson.project_version(), + install: true, +) + +liboopetris_core_dep = declare_dependency( + link_with: liboopetris_core, + include_directories: core_lib.get('inc_dirs'), + compile_args: core_lib.get('compile_args'), + dependencies: core_lib.get('deps'), + version: meson.project_version(), +) +meson.override_dependency('liboopetris-core', liboopetris_core_dep) + +install_headers( + files('core.hpp'), + install_dir: core_include_dir, + preserve_path: true, +) + +# generate pkgconfig files +pkg = import('pkgconfig') + +pkg.generate( + liboopetris_core, + description: 'The core library for oopetris', + name: 'oopetris-core', + filebase: 'oopetris-core', + subdirs: 'oopetris', + extra_cflags: core_lib.get('compile_args'), +) + +# setting this to strings, so += {...} gets detected as an error, if it is done after that +core_lib = 'undefined' diff --git a/src/libs/meson.build b/src/libs/meson.build new file mode 100644 index 00000000..7f80bbb9 --- /dev/null +++ b/src/libs/meson.build @@ -0,0 +1,13 @@ +include_dir = get_option('prefix') / get_option('includedir') + +core_lib += { + 'inc_dirs': [core_lib.get('inc_dirs'), include_directories('.')], +} + +subdir('core') + +recordings_lib += { + 'inc_dirs': [recordings_lib.get('inc_dirs'), include_directories('.')], +} + +subdir('recordings') diff --git a/src/libs/recordings/meson.build b/src/libs/recordings/meson.build new file mode 100644 index 00000000..e1a93e85 --- /dev/null +++ b/src/libs/recordings/meson.build @@ -0,0 +1,55 @@ +recordings_src_files = [] +recordings_header_files = [files('recordings.hpp')] + +recordings_include_dir = include_dir / 'oopetris' / 'recordings' + +subdir('utility') + +recordings_lib += { + 'deps': [recordings_lib.get('deps'), liboopetris_core_dep], + 'inc_dirs': [recordings_lib.get('inc_dirs'), include_directories('.')], +} + +liboopetris_recordings = library( + 'oopetris_recordings', + recordings_src_files, + recordings_header_files, + include_directories: recordings_lib.get('inc_dirs'), + dependencies: recordings_lib.get('deps'), + cpp_args: recordings_lib.get('compile_args'), + override_options: { + 'warning_level': '3', + 'werror': true, + }, + version: meson.project_version(), + install: true, +) + +liboopetris_recordings_dep = declare_dependency( + link_with: liboopetris_recordings, + include_directories: recordings_lib.get('inc_dirs'), + compile_args: recordings_lib.get('compile_args'), + dependencies: recordings_lib.get('deps'), + version: meson.project_version(), +) +meson.override_dependency('liboopetris-recordings', liboopetris_recordings_dep) + +install_headers( + files('recordings.hpp'), + install_dir: recordings_include_dir, + preserve_path: true, +) + +# generate pkgconfig files +pkg = import('pkgconfig') +pkg.generate( + liboopetris_recordings, + description: 'The recordings library for oopetris', + name: 'oopetris-recordings', + filebase: 'oopetris-recordings', + subdirs: 'oopetris', + extra_cflags: recordings_lib.get('compile_args'), +) + +# setting this to strings, so += {...} gets detected as an error, if it is done after that +recordings_lib = 'undefined' diff --git a/src/libs/recordings/recordings.hpp b/src/libs/recordings/recordings.hpp new file mode 100644 index 00000000..68660430 --- /dev/null +++ b/src/libs/recordings/recordings.hpp @@ -0,0 +1,16 @@ + + +#pragma once + +// this is a easy public header, that you can include, to get all header of liboopetris_recordings + + +#include "./utility/additional_information.hpp" +#include "./utility/checksum_helper.hpp" +#include "./utility/helper.hpp" +#include "./utility/recording.hpp" +#include "./utility/recording_json_wrapper.hpp" +#include "./utility/recording_reader.hpp" +#include "./utility/recording_writer.hpp" +#include "./utility/tetrion_core_information.hpp" +#include "./utility/tetrion_snapshot.hpp" diff --git a/src/recordings/additional_information.cpp b/src/libs/recordings/utility/additional_information.cpp similarity index 96% rename from src/recordings/additional_information.cpp rename to src/libs/recordings/utility/additional_information.cpp index 692339f7..1321b094 100644 --- a/src/recordings/additional_information.cpp +++ b/src/libs/recordings/utility/additional_information.cpp @@ -1,7 +1,7 @@ -#include "additional_information.hpp" -#include "helper.hpp" +#include "./additional_information.hpp" +#include "./helper.hpp" #include #include @@ -390,8 +390,7 @@ recorder::InformationValue::read_value_from_istream( // NOLINT(misc-no-recursion }; } -recorder::AdditionalInformation::AdditionalInformation(std::unordered_map&& values) - : m_values{ std::move(values) } { } +recorder::AdditionalInformation::AdditionalInformation(UnderlyingContainer&& values) : m_values{ std::move(values) } { } recorder::AdditionalInformation::AdditionalInformation() = default; @@ -414,7 +413,7 @@ helper::expected recorder::Additio return helper::unexpected{ "unable to read number of pairs" }; } - std::unordered_map values{}; + UnderlyingContainer values{}; values.reserve(num_pairs.value()); for (u32 i = 0; i < num_pairs.value(); ++i) { @@ -468,10 +467,10 @@ void recorder::AdditionalInformation::add_value(const std::string& key, const In m_values.insert_or_assign(key, value); } -helper::optional recorder::AdditionalInformation::get(const std::string& key) const { +std::optional recorder::AdditionalInformation::get(const std::string& key) const { if (not has(key)) { - return helper::nullopt; + return std::nullopt; } return m_values.at(key); @@ -547,3 +546,20 @@ helper::optional recorder::AdditionalInformation::ge return sha256_creator.get_hash(); } + + +recorder::AdditionalInformation::iterator recorder::AdditionalInformation::begin() { + return m_values.begin(); +} + +recorder::AdditionalInformation::const_iterator recorder::AdditionalInformation::begin() const { + return m_values.begin(); +} + +recorder::AdditionalInformation::iterator recorder::AdditionalInformation::end() { + return m_values.end(); +} + +recorder::AdditionalInformation::const_iterator recorder::AdditionalInformation::end() const { + return m_values.end(); +} diff --git a/src/recordings/additional_information.hpp b/src/libs/recordings/utility/additional_information.hpp similarity index 73% rename from src/recordings/additional_information.hpp rename to src/libs/recordings/utility/additional_information.hpp index feeefa84..8c937564 100644 --- a/src/recordings/additional_information.hpp +++ b/src/libs/recordings/utility/additional_information.hpp @@ -1,11 +1,10 @@ #pragma once -#include "checksum_helper.hpp" -#include "helper.hpp" -#include "helper/expected.hpp" -#include "helper/optional.hpp" -#include "helper/types.hpp" +#include "./checksum_helper.hpp" +#include "./helper.hpp" +#include +#include #include #include @@ -20,9 +19,11 @@ namespace recorder { struct InformationValue { + using UnderlyingType = std:: + variant>; + private: - std::variant> - m_value; + UnderlyingType m_value; enum class ValueType : u8 { String = 0, Float, Double, Bool, U8, I8, U32, I32, U64, I64, Vector }; constexpr static u32 max_recursion_depth = 15; @@ -59,6 +60,9 @@ namespace recorder { return std::get(m_value); } + [[nodiscard]] const UnderlyingType& underlying() const { + return m_value; + } [[nodiscard]] std::string to_string(u32 recursion_depth = 0) const; @@ -129,9 +133,12 @@ namespace recorder { struct AdditionalInformation { private: static constexpr u32 magic_start_byte = 0xABCDEF01; - std::unordered_map m_values{}; - AdditionalInformation(std::unordered_map&& values); + using UnderlyingContainer = std::unordered_map; + + UnderlyingContainer m_values; + + explicit AdditionalInformation(UnderlyingContainer&& values); public: explicit AdditionalInformation(); @@ -147,21 +154,21 @@ namespace recorder { add_value(key, value, overwrite); } - [[nodiscard]] helper::optional get(const std::string& key) const; + [[nodiscard]] std::optional get(const std::string& key) const; [[nodiscard]] bool has(const std::string& key) const; template - [[nodiscard]] helper::optional get_if(const std::string& key) const { + [[nodiscard]] std::optional get_if(const std::string& key) const { if (not has(key)) { - return helper::nullopt; + return std::nullopt; } const auto& value = m_values.at(key); if (not value.is()) { - return helper::nullopt; + return std::nullopt; } return value.as(); @@ -170,7 +177,30 @@ namespace recorder { [[nodiscard]] helper::expected, std::string> to_bytes() const; [[nodiscard]] helper::expected get_checksum() const; + + // iterator trait + using iterator = UnderlyingContainer::iterator; //NOLINT(readability-identifier-naming) + using const_iterator = UnderlyingContainer::const_iterator; //NOLINT(readability-identifier-naming) + using difference_type = UnderlyingContainer::difference_type; //NOLINT(readability-identifier-naming) + using value_type = UnderlyingContainer::value_type; //NOLINT(readability-identifier-naming) + using pointer = UnderlyingContainer::pointer; //NOLINT(readability-identifier-naming) + using reference = UnderlyingContainer::reference; //NOLINT(readability-identifier-naming) + using iterator_category = std::bidirectional_iterator_tag; //NOLINT(readability-identifier-naming) + + + [[nodiscard]] iterator begin(); + + [[nodiscard]] const_iterator begin() const; + + [[nodiscard]] iterator end(); + + [[nodiscard]] const_iterator end() const; }; + STATIC_ASSERT_WITH_MESSAGE( + utils::IsIterator::value, + "AdditionalInformation has to be an iterator" + ); + } // namespace recorder diff --git a/src/recordings/checksum_helper.cpp b/src/libs/recordings/utility/checksum_helper.cpp similarity index 93% rename from src/recordings/checksum_helper.cpp rename to src/libs/recordings/utility/checksum_helper.cpp index df1083cc..25fb8258 100644 --- a/src/recordings/checksum_helper.cpp +++ b/src/libs/recordings/utility/checksum_helper.cpp @@ -1,6 +1,6 @@ -#include "checksum_helper.hpp" +#include "./checksum_helper.hpp" Sha256Stream::Sha256Stream() = default; diff --git a/src/recordings/checksum_helper.hpp b/src/libs/recordings/utility/checksum_helper.hpp similarity index 94% rename from src/recordings/checksum_helper.hpp rename to src/libs/recordings/utility/checksum_helper.hpp index dc54a516..6f259b84 100644 --- a/src/recordings/checksum_helper.hpp +++ b/src/libs/recordings/utility/checksum_helper.hpp @@ -2,10 +2,10 @@ #pragma once -#include "helper/utils.hpp" +#include +#include #include -#include #include #include diff --git a/src/recordings/helper.hpp b/src/libs/recordings/utility/helper.hpp similarity index 88% rename from src/recordings/helper.hpp rename to src/libs/recordings/utility/helper.hpp index 8479b411..3f50c671 100644 --- a/src/recordings/helper.hpp +++ b/src/libs/recordings/utility/helper.hpp @@ -3,9 +3,9 @@ #pragma once -#include "helper/expected.hpp" -#include "helper/types.hpp" -#include "helper/utils.hpp" +#include +#include +#include #include #include @@ -79,9 +79,9 @@ namespace helper { } template - [[nodiscard]] helper::optional read_from_istream(std::istream& istream) { + [[nodiscard]] std::optional read_from_istream(std::istream& istream) { if (not istream) { - return helper::nullopt; + return std::nullopt; } auto value = Integral{}; istream.read( @@ -90,29 +90,29 @@ namespace helper { ); if (not istream) { - return helper::nullopt; + return std::nullopt; } return utils::from_little_endian(value); } template - [[nodiscard]] helper::optional> read_array_from_istream(std::istream& istream) { + [[nodiscard]] std::optional> read_array_from_istream(std::istream& istream) { if (not istream) { - return helper::nullopt; + return std::nullopt; } std::array result{}; for (decltype(Size) i = 0; i < Size; ++i) { const auto read_data = read_from_istream(istream); if (not read_data.has_value()) { - return helper::nullopt; + return std::nullopt; } result.at(i) = read_data.value(); } if (not istream) { - return helper::nullopt; + return std::nullopt; } return result; @@ -124,7 +124,7 @@ namespace helper { namespace writer { template - helper::expected write_integral_to_file(std::ofstream& file, const Integral data) { + helper::expected write_integral_to_file(std::ofstream& file, const Integral data) { if (not file) { return helper::unexpected{ fmt::format("failed to write data \"{}\"", data) }; } @@ -137,12 +137,12 @@ namespace helper { sizeof(little_endian_data) ); - return true; + return {}; } template - helper::expected write_vector_to_file(std::ofstream& file, const std::vector& values) { - helper::expected result{ true }; + helper::expected write_vector_to_file(std::ofstream& file, const std::vector& values) { + helper::expected result{}; for (const auto& value : values) { result = write_integral_to_file(file, value); if (not result.has_value()) { diff --git a/src/recordings/meson.build b/src/libs/recordings/utility/meson.build similarity index 57% rename from src/recordings/meson.build rename to src/libs/recordings/utility/meson.build index 5b6aa214..d6a782e0 100644 --- a/src/recordings/meson.build +++ b/src/libs/recordings/utility/meson.build @@ -1,21 +1,28 @@ recordings_src_files = files( 'additional_information.cpp', - 'additional_information.hpp', 'checksum_helper.cpp', - 'checksum_helper.hpp', 'recording.cpp', - 'recording.hpp', 'recording_reader.cpp', - 'recording_reader.hpp', 'recording_writer.cpp', - 'recording_writer.hpp', 'tetrion_snapshot.cpp', - 'tetrion_snapshot.hpp', ) +_header_files = files( + 'additional_information.hpp', + 'checksum_helper.hpp', + 'helper.hpp', + 'recording.hpp', + 'recording_json_wrapper.hpp', + 'recording_reader.hpp', + 'recording_writer.hpp', + 'tetrion_core_information.hpp', + 'tetrion_snapshot.hpp', +) -subdir('executable') +recordings_header_files += _header_files -recordings_lib += { - 'inc_dirs': [recordings_lib.get('inc_dirs'), include_directories('.')], -} +install_headers( + _header_files, + install_dir: recordings_include_dir / 'utility', + preserve_path: true, +) diff --git a/src/recordings/recording.cpp b/src/libs/recordings/utility/recording.cpp similarity index 98% rename from src/recordings/recording.cpp rename to src/libs/recordings/utility/recording.cpp index 53efdce5..7c621ac0 100644 --- a/src/recordings/recording.cpp +++ b/src/libs/recordings/utility/recording.cpp @@ -1,5 +1,6 @@ -#include "recording.hpp" +#include "./recording.hpp" + #include diff --git a/src/recordings/recording.hpp b/src/libs/recordings/utility/recording.hpp similarity index 87% rename from src/recordings/recording.hpp rename to src/libs/recordings/utility/recording.hpp index 433d78e7..f010cffb 100644 --- a/src/recordings/recording.hpp +++ b/src/libs/recordings/utility/recording.hpp @@ -1,11 +1,11 @@ #pragma once +#include +#include +#include -#include "additional_information.hpp" -#include "checksum_helper.hpp" -#include "helper/random.hpp" -#include "helper/types.hpp" -#include "manager/input_event.hpp" +#include "./additional_information.hpp" +#include "./checksum_helper.hpp" #include #include @@ -40,7 +40,6 @@ namespace recorder { }; struct Recording { - protected: std::vector m_tetrion_headers; AdditionalInformation m_information; @@ -50,6 +49,8 @@ namespace recorder { m_information{ std::move(information) } { } public: + constexpr const static u8 current_supported_version_number = 1; + Recording(const Recording&) = delete; Recording(Recording&&) = delete; Recording& operator=(const Recording&) = delete; diff --git a/src/libs/recordings/utility/recording_json_wrapper.hpp b/src/libs/recordings/utility/recording_json_wrapper.hpp new file mode 100644 index 00000000..f13babb3 --- /dev/null +++ b/src/libs/recordings/utility/recording_json_wrapper.hpp @@ -0,0 +1,211 @@ + + +#pragma once + +#include +#include +#include + +#include "./additional_information.hpp" +#include "./recording.hpp" +#include "./recording_reader.hpp" +#include "./tetrion_snapshot.hpp" + +namespace nlohmann { + template<> + struct adl_serializer { + static recorder::InformationValue from_json(const json& /* obj */) { + //TODO(Totto): Implement + throw std::runtime_error{ "NOT IMPLEMENTED" }; + } + + static void to_json(json& obj, const recorder::InformationValue& information) { // NOLINT(misc-no-recursion) + std::visit( + helper::overloaded{ + [&obj](const std::string& value) { obj = value; }, + [&obj](const float& value) { obj = value; }, [&obj](const double& value) { obj = value; }, + [&obj](const bool& value) { obj = value; }, [&obj](const u8& value) { obj = value; }, + [&obj](const i8& value) { obj = value; }, [&obj](const u32& value) { obj = value; }, + [&obj](const i32& value) { obj = value; }, [&obj](const u64& value) { obj = value; }, + [&obj](const i64& value) { obj = value; }, + [&obj](const std::vector& value) { // NOLINT(misc-no-recursion) + obj = value; + } }, + information.underlying() + ); + } + }; + + template<> + struct adl_serializer { + static recorder::AdditionalInformation from_json(const json& /* obj */) { + //TODO(Totto): Implement + throw std::runtime_error{ "NOT IMPLEMENTED" }; + } + + static void to_json(json& obj, const recorder::AdditionalInformation& information) { + + obj = nlohmann::json::object(); + + for (const auto& [key, value] : information) { + + json value_json; + nlohmann::adl_serializer::to_json(value_json, value); + obj[key] = value_json; + } + } + }; + + template<> + struct adl_serializer { + static recorder::TetrionHeader from_json(const json& /* obj */) { + //TODO(Totto): Implement + throw std::runtime_error{ "NOT IMPLEMENTED" }; + } + + static void to_json(json& obj, const recorder::TetrionHeader& tetrion_header) { + obj = nlohmann::json{ + { "seed", tetrion_header.seed }, + { "starting_level", tetrion_header.starting_level } + }; + } + }; + + template<> + struct adl_serializer { + static InputEvent from_json(const json& /* obj */) { + //TODO(Totto): Implement + throw std::runtime_error{ "NOT IMPLEMENTED" }; + } + + static void to_json(json& obj, const InputEvent& event) { + + obj = magic_enum::enum_name(event); + } + }; + + template<> + struct adl_serializer { + static recorder::Record from_json(const json& /* obj */) { + //TODO(Totto): Implement + throw std::runtime_error{ "NOT IMPLEMENTED" }; + } + + static void to_json(json& obj, const recorder::Record& record) { + + obj = nlohmann::json{ + { "tetrion_index", record.tetrion_index }, + { "simulation_step_index", record.simulation_step_index }, + { "event", record.event } + }; + } + }; + + + template + struct adl_serializer> { + static shapes::AbstractPoint from_json(const json& /* obj */) { + //TODO(Totto): Implement + throw std::runtime_error{ "NOT IMPLEMENTED" }; + } + + static void to_json(json& obj, const shapes::AbstractPoint& point) { + obj = nlohmann::json{ + { "x", point.x }, + { "y", point.y } + }; + } + }; + + + template<> + struct adl_serializer { + static helper::TetrominoType from_json(const json& /* obj */) { + //TODO(Totto): Implement + throw std::runtime_error{ "NOT IMPLEMENTED" }; + } + + static void to_json(json& obj, const helper::TetrominoType& type) { + obj = magic_enum::enum_name(type); + } + }; + + + template<> + struct adl_serializer { + static Mino from_json(const json& /* obj */) { + //TODO(Totto): Implement + throw std::runtime_error{ "NOT IMPLEMENTED" }; + } + + static void to_json(json& obj, const Mino& mino) { + obj = nlohmann::json{ + { "position", mino.position() }, + { "type", mino.type() } + }; + } + }; + + + template<> + struct adl_serializer { + static TetrionSnapshot from_json(const json& /* obj */) { + //TODO(Totto): Implement + throw std::runtime_error{ "NOT IMPLEMENTED" }; + } + + static void to_json(json& obj, const TetrionSnapshot& snapshot) { + + json mino_stack_json; + nlohmann::adl_serializer>::to_json(mino_stack_json, snapshot.mino_stack().minos()); + + + obj = nlohmann::json{ + { "tetrion_index", snapshot.tetrion_index() }, + { "level", snapshot.level() }, + { "score", snapshot.score() }, + { "lines_cleared", snapshot.lines_cleared() }, + { "simulation_step_index", snapshot.simulation_step_index() }, + { "mino_stack", mino_stack_json } + }; + } + }; + + + template<> + struct adl_serializer { + static recorder::RecordingReader from_json(const json& /* obj */) { + //TODO(Totto): Implement + throw std::runtime_error{ "NOT IMPLEMENTED" }; + } + + static void to_json(json& obj, const recorder::RecordingReader& recording_reader) { + + json information_json; + nlohmann::adl_serializer::to_json( + information_json, recording_reader.information() + ); + + json tetrion_headers_json; + nlohmann::adl_serializer>::to_json( + tetrion_headers_json, recording_reader.tetrion_headers() + ); + + json records_json; + nlohmann::adl_serializer>::to_json(records_json, recording_reader.records()); + + json snapshots_json; + nlohmann::adl_serializer>>::to_json( + snapshots_json, recording_reader.snapshots() + ); + + obj = nlohmann::json{ + { "version", recorder::Recording::current_supported_version_number }, + { "information", information_json }, + { "tetrion_headers", tetrion_headers_json }, + { "records", records_json }, + { "snapshots", snapshots_json }, + }; + } + }; +} // namespace nlohmann diff --git a/src/recordings/recording_reader.cpp b/src/libs/recordings/utility/recording_reader.cpp similarity index 95% rename from src/recordings/recording_reader.cpp rename to src/libs/recordings/utility/recording_reader.cpp index ceac9cfa..58f04d96 100644 --- a/src/recordings/recording_reader.cpp +++ b/src/libs/recordings/utility/recording_reader.cpp @@ -1,7 +1,8 @@ -#include "recording_reader.hpp" -#include "helper/magic_enum_wrapper.hpp" -#include "recordings/additional_information.hpp" +#include + +#include "./additional_information.hpp" +#include "./recording_reader.hpp" #include #include @@ -50,10 +51,11 @@ recorder::RecordingReader::get_header_from_path(const std::filesystem::path& pat if (not version_number.has_value()) { return helper::unexpected{ "unable to read recording version from recorded game" }; } - if (version_number.value() != 1) { - return helper::unexpected{ - fmt::format("only supported version at the moment is {}, but got {}", 1, version_number.value()) - }; + if (version_number.value() != Recording::current_supported_version_number) { + return helper::unexpected{ fmt::format( + "only supported version at the moment is {}, but got {}", Recording::current_supported_version_number, + version_number.value() + ) }; } const auto num_tetrions = helper::reader::read_integral_from_file(file); @@ -172,12 +174,9 @@ helper::expected recorder::RecordingRead return m_records.cend(); } -[[nodiscard]] const std::vector& recorder::RecordingReader::records() { +[[nodiscard]] const std::vector& recorder::RecordingReader::records() const { return m_records; } -[[nodiscard]] const std::vector& recorder::RecordingReader::snapshots() { - return m_snapshots; -} [[nodiscard]] const std::vector& recorder::RecordingReader::snapshots() const { return m_snapshots; diff --git a/src/recordings/recording_reader.hpp b/src/libs/recordings/utility/recording_reader.hpp similarity index 87% rename from src/recordings/recording_reader.hpp rename to src/libs/recordings/utility/recording_reader.hpp index 0841f75d..a6cc6ca8 100644 --- a/src/recordings/recording_reader.hpp +++ b/src/libs/recordings/utility/recording_reader.hpp @@ -1,9 +1,9 @@ #pragma once -#include "helper.hpp" -#include "helper/optional.hpp" -#include "recording.hpp" -#include "tetrion_snapshot.hpp" +#include "./helper.hpp" + +#include "./recording.hpp" +#include "./tetrion_snapshot.hpp" #include @@ -32,8 +32,7 @@ namespace recorder { [[nodiscard]] auto begin() const; [[nodiscard]] auto end() const; - [[nodiscard]] const std::vector& records(); - [[nodiscard]] const std::vector& snapshots(); + [[nodiscard]] const std::vector& records() const; [[nodiscard]] const std::vector& snapshots() const; diff --git a/src/recordings/recording_writer.cpp b/src/libs/recordings/utility/recording_writer.cpp similarity index 87% rename from src/recordings/recording_writer.cpp rename to src/libs/recordings/utility/recording_writer.cpp index 7b876ce3..9da53ff9 100644 --- a/src/recordings/recording_writer.cpp +++ b/src/libs/recordings/utility/recording_writer.cpp @@ -1,5 +1,6 @@ -#include "recording_writer.hpp" -#include "tetrion_snapshot.hpp" +#include "./recording_writer.hpp" +#include "./recording.hpp" +#include "./tetrion_snapshot.hpp" recorder::RecordingWriter::RecordingWriter( std::ofstream&& output_file, @@ -27,7 +28,7 @@ helper::expected recorder::RecordingWrit return helper::unexpected{ fmt::format("failed to open output file \"{}\"", path.string()) }; } - helper::expected result{ true }; + helper::expected result{}; static_assert(sizeof(constants::recording::magic_file_byte) == 4); result = helper::writer::write_integral_to_file(output_file, constants::recording::magic_file_byte); @@ -35,8 +36,8 @@ helper::expected recorder::RecordingWrit return helper::unexpected{ fmt::format("error while writing: {}", result.error()) }; } - static_assert(sizeof(RecordingWriter::version_number) == 1); - result = helper::writer::write_integral_to_file(output_file, RecordingWriter::version_number); + static_assert(sizeof(Recording::current_supported_version_number) == 1); + result = helper::writer::write_integral_to_file(output_file, Recording::current_supported_version_number); if (not result.has_value()) { return helper::unexpected{ fmt::format("error while writing: {}", result.error()) }; } @@ -73,14 +74,14 @@ helper::expected recorder::RecordingWrit return RecordingWriter{ std::move(output_file), std::move(tetrion_headers), std::move(information) }; } -helper::expected recorder::RecordingWriter::add_event( +helper::expected recorder::RecordingWriter::add_event( const u8 tetrion_index, // NOLINT(bugprone-easily-swappable-parameters) const u64 simulation_step_index, const InputEvent event ) { assert(tetrion_index < m_tetrion_headers.size()); - helper::expected result{ true }; + helper::expected result{}; static_assert(sizeof(std::underlying_type_t) == 1); result = write(utils::to_underlying(MagicByte::Record)); @@ -106,13 +107,13 @@ helper::expected recorder::RecordingWriter::add_event( return result; } -helper::expected recorder::RecordingWriter::add_snapshot( +helper::expected recorder::RecordingWriter::add_snapshot( const u8 tetrion_index, const u64 simulation_step_index, std::unique_ptr information ) { - helper::expected result{ true }; + helper::expected result{}; static_assert(sizeof(std::underlying_type_t) == 1); result = write(utils::to_underlying(MagicByte::Snapshot)); @@ -131,9 +132,9 @@ helper::expected recorder::RecordingWriter::add_snapshot( } -helper::expected +helper::expected recorder::RecordingWriter::write_tetrion_header_to_file(std::ofstream& file, const TetrionHeader& header) { - helper::expected result{ true }; + helper::expected result{}; static_assert(sizeof(decltype(header.seed)) == 8); result = helper::writer::write_integral_to_file(file, header.seed); @@ -150,16 +151,17 @@ recorder::RecordingWriter::write_tetrion_header_to_file(std::ofstream& file, con return result; } -helper::expected recorder::RecordingWriter::write_checksum_to_file( +helper::expected recorder::RecordingWriter::write_checksum_to_file( std::ofstream& file, const std::vector& tetrion_headers, const AdditionalInformation& information ) { - const auto checksum = Recording::get_header_checksum(RecordingWriter::version_number, tetrion_headers, information); + const auto checksum = + Recording::get_header_checksum(Recording::current_supported_version_number, tetrion_headers, information); static_assert(sizeof(decltype(checksum)) == 32); - helper::expected result{ true }; + helper::expected result{}; for (const auto& checksum_byte : checksum) { result = helper::writer::write_integral_to_file(file, checksum_byte); diff --git a/src/recordings/recording_writer.hpp b/src/libs/recordings/utility/recording_writer.hpp similarity index 75% rename from src/recordings/recording_writer.hpp rename to src/libs/recordings/utility/recording_writer.hpp index 509f7987..13d42441 100644 --- a/src/recordings/recording_writer.hpp +++ b/src/libs/recordings/utility/recording_writer.hpp @@ -1,10 +1,10 @@ #pragma once -#include "helper.hpp" -#include "helper/expected.hpp" -#include "recording.hpp" -#include "recordings/additional_information.hpp" -#include "tetrion_core_information.hpp" +#include "./additional_information.hpp" +#include "./helper.hpp" +#include "./recording.hpp" +#include "./tetrion_core_information.hpp" +#include #include @@ -13,7 +13,6 @@ namespace recorder { struct RecordingWriter : public Recording { private: std::ofstream m_output_file; - constexpr static u8 version_number = 1; explicit RecordingWriter( std::ofstream&& output_file, @@ -30,32 +29,32 @@ namespace recorder { AdditionalInformation&& information ); - helper::expected add_event( + helper::expected add_event( u8 tetrion_index, // NOLINT(bugprone-easily-swappable-parameters) u64 simulation_step_index, InputEvent event ); - helper::expected + helper::expected add_snapshot(u8 tetrion_index, u64 simulation_step_index, std::unique_ptr information); private: - static helper::expected + static helper::expected write_tetrion_header_to_file(std::ofstream& file, const TetrionHeader& header); - static helper::expected write_checksum_to_file( + static helper::expected write_checksum_to_file( std::ofstream& file, const std::vector& tetrion_headers, const AdditionalInformation& information ); template - helper::expected write(Integral data) { + helper::expected write(Integral data) { const auto result = helper::writer::write_integral_to_file(m_output_file, data); if (not result.has_value()) { return helper::unexpected{ fmt::format("error while writing: {}", result.error()) }; } - return true; + return {}; } }; diff --git a/src/recordings/tetrion_core_information.hpp b/src/libs/recordings/utility/tetrion_core_information.hpp similarity index 93% rename from src/recordings/tetrion_core_information.hpp rename to src/libs/recordings/utility/tetrion_core_information.hpp index a9384f50..f41a727a 100644 --- a/src/recordings/tetrion_core_information.hpp +++ b/src/libs/recordings/utility/tetrion_core_information.hpp @@ -1,7 +1,7 @@ #pragma once -#include "game/mino_stack.hpp" +#include struct TetrionCoreInformation { u8 tetrion_index; diff --git a/src/recordings/tetrion_snapshot.cpp b/src/libs/recordings/utility/tetrion_snapshot.cpp similarity index 90% rename from src/recordings/tetrion_snapshot.cpp rename to src/libs/recordings/utility/tetrion_snapshot.cpp index 0dff1256..fca9d6bb 100644 --- a/src/recordings/tetrion_snapshot.cpp +++ b/src/libs/recordings/utility/tetrion_snapshot.cpp @@ -1,8 +1,9 @@ -#include "tetrion_snapshot.hpp" -#include "helper.hpp" -#include "helper/expected.hpp" -#include "helper/magic_enum_wrapper.hpp" -#include "tetrion_core_information.hpp" +#include +#include + +#include "./helper.hpp" +#include "./tetrion_core_information.hpp" +#include "./tetrion_snapshot.hpp" #include #include @@ -103,10 +104,26 @@ TetrionSnapshot::TetrionSnapshot( return m_tetrion_index; } +[[nodiscard]] TetrionSnapshot::Level TetrionSnapshot::level() const { + return m_level; +} + +[[nodiscard]] TetrionSnapshot::Score TetrionSnapshot::score() const { + return m_score; +} + +[[nodiscard]] TetrionSnapshot::LineCount TetrionSnapshot::lines_cleared() const { + return m_lines_cleared; +} + [[nodiscard]] u64 TetrionSnapshot::simulation_step_index() const { return m_simulation_step_index; } +[[nodiscard]] const MinoStack& TetrionSnapshot::mino_stack() const { + return m_mino_stack; +} + [[nodiscard]] std::vector TetrionSnapshot::to_bytes() const { auto bytes = std::vector{}; @@ -148,19 +165,19 @@ TetrionSnapshot::TetrionSnapshot( namespace { template - helper::expected + helper::expected compare_values(const std::string_view name, const Value& this_value, const Value& other_value) { if (this_value != other_value) { return helper::unexpected{ fmt::format("{} do not match:\n {} vs. {}", name, this_value, other_value) }; } - return true; + return {}; } } // namespace -helper::expected TetrionSnapshot::compare_to(const TetrionSnapshot& other) const { - helper::expected result{ true }; +helper::expected TetrionSnapshot::compare_to(const TetrionSnapshot& other) const { + helper::expected result{}; result = compare_values("tetrion indices", m_tetrion_index, other.m_tetrion_index); if (not result.has_value()) { @@ -197,5 +214,5 @@ helper::expected TetrionSnapshot::compare_to(const TetrionSna return helper::unexpected{ fmt::format("mino stacks do not match:\n {}", string_stream.str()) }; } - return true; + return {}; } diff --git a/src/recordings/tetrion_snapshot.hpp b/src/libs/recordings/utility/tetrion_snapshot.hpp similarity index 73% rename from src/recordings/tetrion_snapshot.hpp rename to src/libs/recordings/utility/tetrion_snapshot.hpp index 31264ae4..4aa7a11a 100644 --- a/src/recordings/tetrion_snapshot.hpp +++ b/src/libs/recordings/utility/tetrion_snapshot.hpp @@ -1,13 +1,12 @@ #pragma once -#include "game/mino_stack.hpp" -#include "helper/expected.hpp" -#include "helper/utils.hpp" -#include "tetrion_core_information.hpp" +#include +#include +#include + +#include "./tetrion_core_information.hpp" -#include #include -#include #include struct TetrionSnapshot final { @@ -45,9 +44,17 @@ struct TetrionSnapshot final { [[nodiscard]] u8 tetrion_index() const; + [[nodiscard]] Level level() const; + + [[nodiscard]] Score score() const; + + [[nodiscard]] LineCount lines_cleared() const; + [[nodiscard]] u64 simulation_step_index() const; + [[nodiscard]] const MinoStack& mino_stack() const; + [[nodiscard]] std::vector to_bytes() const; - [[nodiscard]] helper::expected compare_to(const TetrionSnapshot& other) const; + [[nodiscard]] helper::expected compare_to(const TetrionSnapshot& other) const; }; diff --git a/src/lobby/api.cpp b/src/lobby/api.cpp new file mode 100644 index 00000000..27df24dd --- /dev/null +++ b/src/lobby/api.cpp @@ -0,0 +1,339 @@ + + +#include "api.hpp" + + +namespace { + + + inline helper::expected is_json_response(const httplib::Result& result) { + if (not result->has_header("Content-Type")) { + return helper::unexpected{ "Content-Type not set!" }; + } + + if (const auto value = result->get_header_value("Content-Type"); value != constants::json_content_type) { + return helper::unexpected{ fmt::format("Content-Type is not json but {}", value) }; + } + + return result->body; + } + + + inline helper::expected is_error_message_response(const httplib::Result& result + ) { + + const auto body = is_json_response(result); + if (not body.has_value()) { + return helper::unexpected{ body.error() }; + } + + const auto parsed = json::try_parse_json(body.value()); + + if (parsed.has_value()) { + return parsed.value(); + } + + return helper::unexpected{ fmt::format("Couldn't parse json with error: {}", parsed.error()) }; + } + + inline helper::expected is_request_ok(const httplib::Result& result, int ok_code = 200) { + + if (not result) { + return helper::unexpected{ + fmt::format("Request failed with: {}", httplib::to_string(result.error())) + }; + } + + if (result->status == 401) { + + const auto error_type = is_error_message_response(result); + + if (error_type.has_value()) { + return helper::unexpected{ fmt::format("Unauthorized: {}", error_type.value().message) }; + } + + return helper::unexpected{ "Unauthorized" }; + } + + + if (result->status != ok_code) { + + const auto error_type = is_error_message_response(result); + + if (error_type.has_value()) { + return helper::unexpected{ fmt::format( + "Got error response with status code {}: '{}' and message: {}", result->status, + httplib::status_message(result->status), error_type.value().message + ) }; + } + + + return helper::unexpected{ fmt::format( + "Got error response with status code {}: '{}' but expected {}", result->status, + httplib::status_message(result->status), ok_code + ) }; + } + + return {}; + }; + + + template + helper::expected get_json_from_request(const httplib::Result& result, int ok_code = 200) { + + const auto temp = is_request_ok(result, ok_code); + if (not temp.has_value()) { + return helper::unexpected{ temp.error() }; + } + + const auto body = is_json_response(result); + if (not body.has_value()) { + return helper::unexpected{ body.error() }; + } + + const auto parsed = json::try_parse_json(body.value()); + + if (parsed.has_value()) { + return parsed.value(); + } + + return helper::unexpected{ fmt::format("Couldn't parse json with error: {}", parsed.error()) }; + } + + +} // namespace + + +helper::expected lobby::Client::check_compatibility() { + const auto server_version = get_version(); + + if (not server_version.has_value()) { + return helper::unexpected{ fmt::format( + "Connecting to unsupported server, he can't report his version\nGot error: {}", server_version.error() + ) }; + } + + const auto& version = server_version.value(); + + //TODO(Totto): if version is semver, support semver comparison + if (Client::supported_version.string() != version.version) { + return helper::unexpected{ fmt::format( + "Connecting to unsupported server, version is {}, but we support only {}", + Client::supported_version.string(), version.version + ) }; + } + + return {}; +} + +helper::expected lobby::Client::check_reachability() { + + auto result = m_client.Get("/"); + + if (not result) { + return helper::unexpected{ + fmt::format("Server not reachable: {}", httplib::to_string(result.error())) + }; + } + + return {}; +} + +lobby::Client::Client(const std::string& api_url) : m_client{ api_url } { + + // clang-format off + m_client.set_default_headers({ +#if defined(CPPHTTPLIB_ZLIB_SUPPORT) || defined(CPPHTTPLIB_BROTLI_SUPPORT) + { "Accept-Encoding", + +#if defined(CPPHTTPLIB_ZLIB_SUPPORT) + "gzip, deflate" +#endif +#if defined(CPPHTTPLIB_ZLIB_SUPPORT) && defined(CPPHTTPLIB_BROTLI_SUPPORT) + ", " +#endif +#if defined(CPPHTTPLIB_BROTLI_SUPPORT) + "br" +#endif + }, +#endif + // clang-format on + { "Accept", constants::json_content_type } }); + +#if defined(CPPHTTPLIB_ZLIB_SUPPORT) || defined(CPPHTTPLIB_BROTLI_SUPPORT) + m_client.set_compress(true); + m_client.set_decompress(true); +#endif +} + +helper::expected lobby::Client::get_version() { + auto res = m_client.Get("/version"); + + return get_json_from_request(res); +} + + +lobby::Client::Client(Client&& other) noexcept + : m_client{ std::move(other.m_client) }, + m_authentication_token{ std::move(other.m_authentication_token) } { } + +lobby::Client::~Client() = default; + +helper::expected lobby::Client::get_client(const std::string& url) { + + Client client{ url }; + + const auto reachable = client.check_reachability(); + + if (not reachable.has_value()) { + return helper::unexpected{ reachable.error() }; + } + + //TODO(Totto): once version is standard, check here if the version is supported + + return client; +} + + +helper::expected lobby::Client::login(const Credentials& credentials) { + const auto json_result = json::try_json_to_string(credentials); + if (not json_result.has_value()) { + return helper::unexpected{ json_result.error() }; + } + + auto res = m_client.Post("/login", json_result.value(), constants::json_content_type); + + return get_json_from_request(res); +} + + +bool lobby::Client::is_authenticated() { + return m_authentication_token.has_value(); +} + +bool lobby::Client::authenticate(const Credentials& credentials) { + + const auto result = login(credentials); + + if (not result.has_value()) { + spdlog::error("Failed authenticating user {}: {}", credentials.username, result.error()); + m_authentication_token = std::nullopt; + return false; + } + + m_authentication_token = result.value().jwt; + + m_client.set_bearer_token_auth(m_authentication_token.value()); + + return true; +} + +helper::expected, std::string> lobby::Client::get_lobbies() { + auto res = m_client.Get("/lobbies"); + + return get_json_from_request>(res); +} + + +helper::expected lobby::Client::join_lobby(int lobby_id) { + if (not is_authenticated()) { + return helper::unexpected{ + "Authentication needed for this " + "endpoint, but not authenticated!" + }; + } + + auto res = m_client.Post(fmt::format("/lobbies/{}", lobby_id)); + + return is_request_ok(res, 204); +} + +helper::expected lobby::Client::get_lobby_detail(int lobby_id) { + if (not is_authenticated()) { + return helper::unexpected{ + "Authentication needed for this " + "endpoint, but not authenticated!" + }; + } + + auto res = m_client.Get(fmt::format("/lobbies/{}", lobby_id)); + + return get_json_from_request(res); +} + +helper::expected lobby::Client::delete_lobby(int lobby_id) { + if (not is_authenticated()) { + return helper::unexpected{ + "Authentication needed for this " + "endpoint, but not authenticated!" + }; + } + + auto res = m_client.Delete(fmt::format("/lobbies/{}", lobby_id)); + + return is_request_ok(res, 204); +} + +helper::expected lobby::Client::leave_lobby(int lobby_id) { + if (not is_authenticated()) { + return helper::unexpected{ + "Authentication needed for this " + "endpoint, but not authenticated!" + }; + } + + auto res = m_client.Put(fmt::format("/lobbies/{}/leave", lobby_id)); + + return is_request_ok(res, 204); +} + +helper::expected lobby::Client::start_lobby(int lobby_id) { + if (not is_authenticated()) { + return helper::unexpected{ + "Authentication needed for this " + "endpoint, but not authenticated!" + }; + } + + auto res = m_client.Post(fmt::format("/lobbies/{}/start", lobby_id)); + + return is_request_ok(res, 204); +} + +helper::expected lobby::Client::create_lobby( + const CreateLobbyRequest& arguments +) { + if (not is_authenticated()) { + return helper::unexpected{ + "Authentication needed for this " + "endpoint, but not authenticated!" + }; + } + + const auto json_result = json::try_json_to_string(arguments); + if (not json_result.has_value()) { + return helper::unexpected{ json_result.error() }; + } + + auto res = m_client.Post("/lobbies", json_result.value(), constants::json_content_type); + + return get_json_from_request(res, 201); +} + +helper::expected, std::string> lobby::Client::get_users() { + + auto res = m_client.Get("/users"); + + return get_json_from_request>(res); +} + +helper::expected lobby::Client::register_user(const RegisterRequest& register_request) { + const auto json_result = json::try_json_to_string(register_request); + if (not json_result.has_value()) { + return helper::unexpected{ json_result.error() }; + } + + auto res = m_client.Post("/register", json_result.value(), constants::json_content_type); + + return is_request_ok(res, 204); +} diff --git a/src/lobby/api.hpp b/src/lobby/api.hpp index 2e04aa52..75e35a0f 100644 --- a/src/lobby/api.hpp +++ b/src/lobby/api.hpp @@ -23,359 +23,73 @@ #endif #include +#include #include -#include "helper/expected.hpp" -#include "helper/static_string.hpp" +#include +#include + #include "lobby/types.hpp" namespace constants { - const std::string json_content_type = "application/json"; + const constexpr auto json_content_type = "application/json"; } -namespace { - - - inline helper::expected is_json_response(const httplib::Result& result) { - if (not result->has_header("Content-Type")) { - return helper::unexpected{ "Content-Type not set!" }; - } - - if (const auto value = result->get_header_value("Content-Type"); value != constants::json_content_type) { - return helper::unexpected{ fmt::format("Content-Type is not json but {}", value) }; - } - - return result->body; - } - - - inline helper::expected is_error_message_response(const httplib::Result& result - ) { - - const auto body = is_json_response(result); - if (not body.has_value()) { - return helper::unexpected{ body.error() }; - } - - const auto parsed = json::try_parse_json(body.value()); - - if (parsed.has_value()) { - return parsed.value(); - } - - return helper::unexpected{ fmt::format("Couldn't parse json with error: {}", parsed.error()) }; - } - - inline helper::expected is_request_ok(const httplib::Result& result, int ok_code = 200) { - - if (not result) { - return helper::unexpected{ - fmt::format("Request failed with: {}", httplib::to_string(result.error())) - }; - } - - if (result->status == 401) { - - const auto error_type = is_error_message_response(result); - - if (error_type.has_value()) { - return helper::unexpected{ fmt::format("Unauthorized: {}", error_type.value().message) }; - } - - return helper::unexpected{ "Unauthorized" }; - } - - - if (result->status != ok_code) { - - const auto error_type = is_error_message_response(result); - - if (error_type.has_value()) { - return helper::unexpected{ fmt::format( - "Got error response with status code {}: '{}' and message: {}", result->status, - httplib::status_message(result->status), error_type.value().message - ) }; - } - - - return helper::unexpected{ fmt::format( - "Got error response with status code {}: '{}' but expected {}", result->status, - httplib::status_message(result->status), ok_code - ) }; - } - - return true; - }; - - - template - helper::expected get_json_from_request(const httplib::Result& result, int ok_code = 200) { - - const auto temp = is_request_ok(result, ok_code); - if (not temp.has_value()) { - return helper::unexpected{ temp.error() }; - } - - const auto body = is_json_response(result); - if (not body.has_value()) { - return helper::unexpected{ body.error() }; - } - - const auto parsed = json::try_parse_json(body.value()); - - if (parsed.has_value()) { - return parsed.value(); - } - - return helper::unexpected{ fmt::format("Couldn't parse json with error: {}", parsed.error()) }; - } - - -} // namespace - - namespace lobby { struct Client { private: httplib::Client m_client; - std::string authentication_token{}; + std::optional m_authentication_token; // lobby commit used: https://github.com/OpenBrickProtocolFoundation/lobby/commit/2e0c8d05592f4e4d08437e6cb754a30f02c4e97c static constexpr StaticString supported_version{ "0.1.0" }; - helper::expected check_compatibility() { - const auto server_version = get_version(); - - if (not server_version.has_value()) { - return helper::unexpected{ fmt::format( - "Connecting to unsupported server, he can't report his version\nGot error: {}", - server_version.error() - ) }; - } - - const auto& version = server_version.value(); - - //TODO(Totto): if version is semver, support semver comparison - if (Client::supported_version.string() != version.version) { - return helper::unexpected{ fmt::format( - "Connecting to unsupported server, version is {}, but we support only {}", - Client::supported_version.string(), version.version - ) }; - } - - return true; - } - - helper::expected check_reachability() { + helper::expected check_compatibility(); - auto result = m_client.Get("/"); + helper::expected check_reachability(); - if (not result) { - return helper::unexpected{ - fmt::format("Server not reachable: {}", httplib::to_string(result.error())) - }; - } + explicit Client(const std::string& api_url); - return true; - } + helper::expected get_version(); - explicit Client(const std::string& api_url) : m_client{ api_url } { + helper::expected login(const lobby::Credentials& credentials); - // clang-format off - m_client.set_default_headers({ -#if defined(CPPHTTPLIB_ZLIB_SUPPORT) || defined(CPPHTTPLIB_BROTLI_SUPPORT) - { "Accept-Encoding", - -#if defined(CPPHTTPLIB_ZLIB_SUPPORT) - "gzip, deflate" -#endif -#if defined(CPPHTTPLIB_ZLIB_SUPPORT) && defined(CPPHTTPLIB_BROTLI_SUPPORT) - ", " -#endif -#if defined(CPPHTTPLIB_BROTLI_SUPPORT) - "br" -#endif - }, -#endif - // clang-format on - { "Accept", constants::json_content_type } }); - -#if defined(CPPHTTPLIB_ZLIB_SUPPORT) || defined(CPPHTTPLIB_BROTLI_SUPPORT) - m_client.set_compress(true); - m_client.set_decompress(true); -#endif - } - - helper::expected get_version() { - auto res = m_client.Get("/version"); - - return get_json_from_request(res); - } public: - Client(Client&& other) noexcept - : m_client{ std::move(other.m_client) }, - authentication_token{ std::move(other.authentication_token) } { } - - helper::expected static get_client(const std::string& url) { - - Client client{ url }; - - const auto reachable = client.check_reachability(); - - if (not reachable.has_value()) { - return helper::unexpected{ reachable.error() }; - } - - //TODO(Totto): once version is standard, check here if the version is supported - - return client; - } - - private: - helper::expected login(const Credentials& credentials) { - const auto json_result = json::try_json_to_string(credentials); - if (not json_result.has_value()) { - return helper::unexpected{ json_result.error() }; - } - - auto res = m_client.Post("/login", json_result.value(), constants::json_content_type); - - return get_json_from_request(res); - } - - - public: - bool is_authenticated() { - return not authentication_token.empty(); - } - - bool authenticate(const Credentials& credentials) { - - const auto result = login(credentials); - - if (not result.has_value()) { - spdlog::error("Failed authenticating user {}: {}", credentials.username, result.error()); - authentication_token = ""; - return false; - } - - authentication_token = result.value().jwt; - - m_client.set_bearer_token_auth(authentication_token); - - return true; - } - - helper::expected, std::string> get_lobbies() { - auto res = m_client.Get("/lobbies"); - - return get_json_from_request>(res); - } - - - helper::expected join_lobby(int lobby_id) { - if (not is_authenticated()) { - return helper::unexpected{ - "Authentication needed for this " - "endpoint, but not authenticated!" - }; - } - - auto res = m_client.Post(fmt::format("/lobbies/{}", lobby_id)); - - return is_request_ok(res, 204); - } - - helper::expected get_lobby_detail(int lobby_id) { - if (not is_authenticated()) { - return helper::unexpected{ - "Authentication needed for this " - "endpoint, but not authenticated!" - }; - } - - auto res = m_client.Get(fmt::format("/lobbies/{}", lobby_id)); - - return get_json_from_request(res); - } - - helper::expected delete_lobby(int lobby_id) { - if (not is_authenticated()) { - return helper::unexpected{ - "Authentication needed for this " - "endpoint, but not authenticated!" - }; - } - - auto res = m_client.Delete(fmt::format("/lobbies/{}", lobby_id)); - - return is_request_ok(res, 204); - } - - helper::expected leave_lobby(int lobby_id) { - if (not is_authenticated()) { - return helper::unexpected{ - "Authentication needed for this " - "endpoint, but not authenticated!" - }; - } + Client(Client&& other) noexcept; + Client& operator=(Client&& other) noexcept = delete; - auto res = m_client.Put(fmt::format("/lobbies/{}/leave", lobby_id)); + Client(const Client& other) = delete; + Client& operator=(const Client& other) = delete; - return is_request_ok(res, 204); - } + ~Client(); - helper::expected start_lobby(int lobby_id) { - if (not is_authenticated()) { - return helper::unexpected{ - "Authentication needed for this " - "endpoint, but not authenticated!" - }; - } + helper::expected static get_client(const std::string& url); - auto res = m_client.Post(fmt::format("/lobbies/{}/start", lobby_id)); - return is_request_ok(res, 204); - } + bool is_authenticated(); - helper::expected create_lobby(const CreateLobbyRequest& arguments) { - if (not is_authenticated()) { - return helper::unexpected{ - "Authentication needed for this " - "endpoint, but not authenticated!" - }; - } + bool authenticate(const Credentials& credentials); - const auto json_result = json::try_json_to_string(arguments); - if (not json_result.has_value()) { - return helper::unexpected{ json_result.error() }; - } + helper::expected, std::string> get_lobbies(); - auto res = m_client.Post("/lobbies", json_result.value(), constants::json_content_type); + helper::expected join_lobby(int lobby_id); - return get_json_from_request(res, 201); - } + helper::expected get_lobby_detail(int lobby_id); - helper::expected, std::string> get_users() { + helper::expected delete_lobby(int lobby_id); - auto res = m_client.Get("/users"); + helper::expected leave_lobby(int lobby_id); - return get_json_from_request>(res); - } + helper::expected start_lobby(int lobby_id); - helper::expected register_user(const RegisterRequest& register_request) { - const auto json_result = json::try_json_to_string(register_request); - if (not json_result.has_value()) { - return helper::unexpected{ json_result.error() }; - } + helper::expected create_lobby(const CreateLobbyRequest& arguments); - auto res = m_client.Post("/register", json_result.value(), constants::json_content_type); + helper::expected, std::string> get_users(); - return is_request_ok(res, 204); - } + helper::expected register_user(const RegisterRequest& register_request); }; diff --git a/src/lobby/meson.build b/src/lobby/meson.build index 315429a6..6bf0b698 100644 --- a/src/lobby/meson.build +++ b/src/lobby/meson.build @@ -1 +1 @@ -graphics_src_files += files('api.hpp', 'types.hpp') +graphics_src_files += files('api.cpp', 'api.hpp', 'types.hpp') diff --git a/src/lobby/types.hpp b/src/lobby/types.hpp index 08fefae2..5d446895 100644 --- a/src/lobby/types.hpp +++ b/src/lobby/types.hpp @@ -3,8 +3,8 @@ #include -#include "helper/optional.hpp" -#include "helper/parse_json.hpp" + +#include namespace lobby { @@ -42,7 +42,7 @@ namespace lobby { int size; PlayerInfo host_info; std::vector player_infos; - helper::optional gameserver_port; + std::optional gameserver_port; }; diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 0f1a564d..00000000 --- a/src/main.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "application.hpp" -#include "helper/errors.hpp" -#include "helper/message_box.hpp" -#include "helper/utils.hpp" - -#include -#include -#include - -#if defined(__ANDROID__) -#include -#endif - -#if defined(__CONSOLE__) -#include "helper/console_helpers.hpp" -#endif - - -#include -#include -#include - - -int main(int argc, char** argv) { - const auto logs_path = utils::get_root_folder() / "logs"; - if (not std::filesystem::exists(logs_path)) { - std::filesystem::create_directory(logs_path); - } - - std::vector sinks; -#if defined(__ANDROID__) - sinks.push_back(std::make_shared()); -#elif defined(__CONSOLE__) - sinks.push_back(std::make_shared()); -#else - sinks.push_back(std::make_shared()); -#endif - sinks.push_back(std::make_shared( - fmt::format("{}/oopetris.log", logs_path.string()), 1024 * 1024 * 10, 5, true - )); - auto combined_logger = std::make_shared("combined_logger", begin(sinks), end(sinks)); - spdlog::set_default_logger(combined_logger); - -#if !defined(NDEBUG) - spdlog::set_level(spdlog::level::debug); -#else - spdlog::set_level(spdlog::level::err); -#endif - - std::vector arguments{}; - arguments.reserve(argc); - for (auto i = 0; i < argc; ++i) { - arguments.emplace_back(argv[i]); //NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) - } - - if (arguments.empty()) { - arguments.emplace_back("oopetris"); - } - - constexpr auto window_name = constants::program_name.c_str(); - - std::shared_ptr window{ nullptr }; - - try { -#if defined(__ANDROID__) or defined(__CONSOLE__) - window = std::make_shared(window_name, WindowPosition::Centered); -#else - static constexpr int width = 1280; - static constexpr int height = 720; - - window = std::make_shared(window_name, WindowPosition::Centered, width, height); -#endif - } catch (const helper::GeneralError& general_error) { - spdlog::error("{}", general_error.message()); - } - - if (window == nullptr) { - helper::MessageBox::show_simple( - helper::MessageBox::Type::Error, "Initialization Error", "failed to create SDL window", nullptr - ); - return EXIT_FAILURE; - } - - - try { - Application app{ std::move(window), arguments }; - - app.run(); - return EXIT_SUCCESS; - } catch (const helper::GeneralError& general_error) { - - spdlog::error("{}", general_error.message()); - return EXIT_FAILURE; - } -} diff --git a/src/manager/event_dispatcher.hpp b/src/manager/event_dispatcher.hpp index 66b245ad..2f00636e 100644 --- a/src/manager/event_dispatcher.hpp +++ b/src/manager/event_dispatcher.hpp @@ -1,7 +1,7 @@ #pragma once #include "graphics/rect.hpp" -#include "helper/optional.hpp" + #include "manager/event_listener.hpp" #include "sdl_key.hpp" @@ -76,7 +76,7 @@ struct EventDispatcher final { } } - void start_text_input(const helper::optional& rect) { + void start_text_input(const std::optional& rect) { if (m_input_activated) { return; } diff --git a/src/manager/meson.build b/src/manager/meson.build index fd1240c0..8258817b 100644 --- a/src/manager/meson.build +++ b/src/manager/meson.build @@ -3,7 +3,6 @@ graphics_src_files += files( 'event_listener.hpp', 'font.cpp', 'font.hpp', - 'input_event.hpp', 'music_manager.cpp', 'music_manager.hpp', 'sdl_controller_key.cpp', diff --git a/src/manager/music_manager.cpp b/src/manager/music_manager.cpp index a60ca0a6..ecd4201e 100644 --- a/src/manager/music_manager.cpp +++ b/src/manager/music_manager.cpp @@ -1,28 +1,30 @@ -#include "manager/music_manager.hpp" -#include "helper/command_line_arguments.hpp" +#include +#include + +#include "game/command_line_arguments.hpp" #include "helper/constants.hpp" -#include "helper/errors.hpp" -#include "helper/optional.hpp" -#include "helper/types.hpp" +#include "manager/music_manager.hpp" #include "manager/sdl_key.hpp" #include #include #include #include +#include #include #include - MusicManager::MusicManager(ServiceProvider* service_provider, u8 channel_size) : m_music{ nullptr }, m_queued_music{ nullptr }, m_channel_size{ channel_size }, m_chunk_map{ std::unordered_map{} }, - m_service_provider{ service_provider }, - volume{ m_service_provider->command_line_arguments().silent ? helper::nullopt : std::optional{ 1.0F } } { + m_volume{ service_provider->command_line_arguments().silent ? std::nullopt : std::optional{ 1.0F } } { if (s_instance != nullptr) { - spdlog::error("it's not allowed to create more than one MusicManager instance"); + spdlog::error( + "it's not allowed to create more than one MusicManager instance: {} != {}", + static_cast(s_instance), static_cast(this) + ); return; } @@ -37,13 +39,21 @@ MusicManager::MusicManager(ServiceProvider* service_provider, u8 channel_size) throw helper::InitializationError{ fmt::format("Failed to initialize any audio codec: {}", SDL_GetError()) }; } - // see: https://wiki.libsdl.org/SDL2_mixer/Mix_AllocateChannels - auto allocated_channels = Mix_AllocateChannels(channel_size); - if (allocated_channels != channel_size) { - throw helper::InitializationError{ fmt::format( - "Failed to initialize the requested channels, requested {} but only got {}: {}", channel_size, - allocated_channels, SDL_GetError() - ) }; + // retrieve allocated channels + const auto allocated_channels = Mix_AllocateChannels(-1); + + spdlog::debug("SDL_MIX: allocated_channels = {}", allocated_channels); + + if (allocated_channels == 0) { + + // see: https://wiki.libsdl.org/SDL2_mixer/Mix_AllocateChannels + auto newly_allocated_channels = Mix_AllocateChannels(channel_size); + if (newly_allocated_channels != channel_size) { + throw helper::InitializationError{ fmt::format( + "Failed to initialize the requested channels, requested {} but only got {}: {}", channel_size, + newly_allocated_channels, SDL_GetError() + ) }; + } } // 2 here means STEREO, note that channels above means tracks, e.g. simultaneous playing source that are mixed, @@ -57,7 +67,7 @@ MusicManager::MusicManager(ServiceProvider* service_provider, u8 channel_size) s_instance = this; - set_volume(volume, true); + set_volume(m_volume, true); } MusicManager::~MusicManager() noexcept { @@ -66,7 +76,11 @@ MusicManager::~MusicManager() noexcept { } // stop sounds and free loaded data - Mix_HaltChannel(-1); + const int result = Mix_HaltChannel(-1); + + if (result != 0) { + spdlog::warn("Mix_HaltChannel failed with error: {}", SDL_GetError()); + } if (m_music != nullptr) { Mix_FreeMusic(m_music); @@ -79,14 +93,16 @@ MusicManager::~MusicManager() noexcept { for (const auto& [_, value] : m_chunk_map) { Mix_FreeChunk(value); } + Mix_CloseAudio(); Mix_Quit(); + + s_instance = nullptr; } -helper::optional -MusicManager::load_and_play_music(const std::filesystem::path& location, const usize delay) { +std::optional MusicManager::load_and_play_music(const std::filesystem::path& location, const usize delay) { if (not validate_instance()) { - return helper::nullopt; + return std::nullopt; } Mix_Music* music = Mix_LoadMUS(location.string().c_str()); @@ -96,13 +112,13 @@ MusicManager::load_and_play_music(const std::filesystem::path& location, const u } // if we are mute, set the current music to this - if (not volume.has_value()) { + if (not m_volume.has_value()) { assert(m_queued_music == nullptr && "No queued music is possible, when muted!"); if (m_music != nullptr) { Mix_FreeMusic(m_music); } m_music = music; - return helper::nullopt; + return std::nullopt; } @@ -128,7 +144,7 @@ MusicManager::load_and_play_music(const std::filesystem::path& location, const u return ("an error occurred while trying to play the music: " + std::string{ Mix_GetError() }); } - return helper::nullopt; + return std::nullopt; } @@ -142,7 +158,11 @@ MusicManager::load_and_play_music(const std::filesystem::path& location, const u m_queued_music = music; m_delay = delay; Mix_HookMusicFinished([]() { - assert(s_instance != nullptr and "there must be a MusicManager instance"); + // this can happen on e.g. android, where if we exit the application, we don't close the window, so its reused, but the music manager is destroyed, but the hook is called later xD + /* if (s_instance == nullptr) { + return; + } */ + s_instance->hook_music_finished(); }); @@ -151,7 +171,7 @@ MusicManager::load_and_play_music(const std::filesystem::path& location, const u if (result == 0) { return "UNREACHABLE: m_music was not null but not playing, this is an implementation error!"; } - return helper::nullopt; + return std::nullopt; } const int result = Mix_PlayMusic(music, -1); @@ -159,13 +179,13 @@ MusicManager::load_and_play_music(const std::filesystem::path& location, const u return ("an error occurred while trying to play the music: " + std::string{ Mix_GetError() }); } - return helper::nullopt; + return std::nullopt; } -helper::optional MusicManager::load_effect(const std::string& name, std::filesystem::path& location) { +std::optional MusicManager::load_effect(const std::string& name, std::filesystem::path& location) { if (not validate_instance()) { - return helper::nullopt; + return std::nullopt; } if (m_chunk_map.contains(name)) { @@ -178,12 +198,12 @@ helper::optional MusicManager::load_effect(const std::string& name, } m_chunk_map.insert({ name, chunk }); - return helper::nullopt; + return std::nullopt; } -helper::optional MusicManager::play_effect(const std::string& name, u8 channel_num, int loop) { +std::optional MusicManager::play_effect(const std::string& name, u8 channel_num, int loop) { if (not validate_instance()) { - return helper::nullopt; + return std::nullopt; } if (m_chunk_map.contains(name)) { @@ -201,7 +221,7 @@ helper::optional MusicManager::play_effect(const std::string& name, return "couldn't play on channel: " + std::to_string(channel_num); } - return helper::nullopt; + return std::nullopt; } void MusicManager::hook_music_finished() { @@ -233,33 +253,36 @@ void MusicManager::hook_music_finished() { [[nodiscard]] bool MusicManager::validate_instance() { if (s_instance != this) { - spdlog::error("this MusicManager instance is not the instance that is used globally"); + spdlog::error( + "this MusicManager instance is not the instance that is used globally: {} != {}", + static_cast(s_instance), static_cast(this) + ); return false; } return true; } -[[nodiscard]] helper::optional MusicManager::get_volume() const { +[[nodiscard]] std::optional MusicManager::get_volume() const { #if !defined(NDEBUG) - int result = Mix_VolumeMusic(-1); + const int result = Mix_VolumeMusic(-1); if (result == 0) { - return helper::nullopt; + return std::nullopt; } return static_cast(result) / MIX_MAX_VOLUME; #else - return volume; + return m_volume; #endif } void MusicManager::set_volume( - const helper::optional new_volume, + const std::optional new_volume, const bool force_update, const bool notify_listeners ) { - if (volume == new_volume and not force_update) { + if (m_volume == new_volume and not force_update) { return; } @@ -269,7 +292,7 @@ void MusicManager::set_volume( Mix_HaltMusic(); } - volume = helper::nullopt; + m_volume = std::nullopt; } @@ -277,7 +300,7 @@ void MusicManager::set_volume( not new_volume.has_value() ? 0 : static_cast(MIX_MAX_VOLUME * new_volume.value()); Mix_VolumeMusic(new_volume_mapped); - if (not volume.has_value()) { + if (not m_volume.has_value()) { if (m_music != nullptr) { const int result = Mix_PlayMusic(m_music, -1); @@ -290,22 +313,22 @@ void MusicManager::set_volume( } - volume = new_volume; + m_volume = new_volume; if (notify_listeners) { - for (const auto& [_, listener] : volume_listeners) { - listener(volume); + for (const auto& [_, listener] : m_volume_listeners) { + listener(m_volume); } } } -helper::optional MusicManager::change_volume(const std::int8_t steps) { +std::optional MusicManager::change_volume(const std::int8_t steps) { const auto current_volume = get_volume(); if (steps == 0) { return current_volume; } - helper::optional new_volume = current_volume; + std::optional new_volume = current_volume; if (steps > 0) { @@ -330,18 +353,18 @@ helper::optional MusicManager::change_volume(const std::int8_t steps) { if (not current_volume.has_value()) { - return helper::nullopt; + return std::nullopt; } if (current_volume <= 0.0F) { - new_volume = helper::nullopt; + new_volume = std::nullopt; } else { new_volume = current_volume.value() + MusicManager::step_width * static_cast(steps); if (new_volume <= 0.0F) { - new_volume = helper::nullopt; + new_volume = std::nullopt; } } } diff --git a/src/manager/music_manager.hpp b/src/manager/music_manager.hpp index 496ee7bb..54128369 100644 --- a/src/manager/music_manager.hpp +++ b/src/manager/music_manager.hpp @@ -1,7 +1,8 @@ #pragma once -#include "helper/optional.hpp" -#include "helper/types.hpp" + +#include + #include "input/input.hpp" #include "manager/service_provider.hpp" @@ -21,7 +22,7 @@ struct MusicManager final { static inline MusicManager* s_instance{ nullptr }; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) static const constexpr double step_width = 0.05F; - using VolumeChangeFunction = std::function volume)>; + using VolumeChangeFunction = std::function volume)>; Mix_Music* m_music; std::atomic m_queued_music; @@ -29,48 +30,51 @@ struct MusicManager final { std::unordered_map m_chunk_map; static constexpr unsigned fade_ms = 500; usize m_delay = MusicManager::fade_ms; - ServiceProvider* m_service_provider; - helper::optional volume; - std::unordered_map volume_listeners; + + std::optional m_volume; + std::unordered_map m_volume_listeners; public: explicit MusicManager(ServiceProvider* service_provider, u8 channel_size = 2); + MusicManager(const MusicManager&) = delete; MusicManager& operator=(const MusicManager&) = delete; + MusicManager(const MusicManager&&) = delete; MusicManager& operator=(MusicManager&&) = delete; + ~MusicManager() noexcept; - helper::optional + std::optional load_and_play_music(const std::filesystem::path& location, usize delay = MusicManager::fade_ms); - helper::optional load_effect(const std::string& name, std::filesystem::path& location); - helper::optional play_effect(const std::string& name, u8 channel_num = 1, int loop = 0); + std::optional load_effect(const std::string& name, std::filesystem::path& location); + std::optional play_effect(const std::string& name, u8 channel_num = 1, int loop = 0); //TODO(Totto): atm the volume changers only work on the music channel, when adding more effects, this should support channels via https://wiki.libsdl.org/SDL2_mixer/Mix_Volume - [[nodiscard]] helper::optional get_volume() const; - void set_volume(helper::optional new_volume, bool force_update = false, bool notify_listeners = true); + [[nodiscard]] std::optional get_volume() const; + void set_volume(std::optional new_volume, bool force_update = false, bool notify_listeners = true); // no nodiscard, since the return value is only a side effect, that is maybe useful - helper::optional change_volume(std::int8_t steps); + std::optional change_volume(std::int8_t steps); bool handle_event(const std::shared_ptr& input_manager, const SDL_Event& event); bool add_volume_listener(const std::string& name, VolumeChangeFunction change_function) { - if (volume_listeners.contains(name)) { + if (m_volume_listeners.contains(name)) { return false; } - volume_listeners.insert_or_assign(name, std::move(change_function)); + m_volume_listeners.insert_or_assign(name, std::move(change_function)); return true; } bool remove_volume_listener(const std::string& name) { - if (not volume_listeners.contains(name)) { + if (not m_volume_listeners.contains(name)) { return false; } - volume_listeners.erase(name); + m_volume_listeners.erase(name); return true; } diff --git a/src/manager/resource_manager.hpp b/src/manager/resource_manager.hpp index ebada413..fdbeffb4 100644 --- a/src/manager/resource_manager.hpp +++ b/src/manager/resource_manager.hpp @@ -1,6 +1,7 @@ #pragma once -#include "helper/types.hpp" +#include + #include "manager/font.hpp" #include diff --git a/src/manager/sdl_controller_key.hpp b/src/manager/sdl_controller_key.hpp index 9816ba84..c8a578ad 100644 --- a/src/manager/sdl_controller_key.hpp +++ b/src/manager/sdl_controller_key.hpp @@ -1,6 +1,6 @@ #pragma once -#include "helper/expected.hpp" +#include #include diff --git a/src/manager/sdl_key.cpp b/src/manager/sdl_key.cpp index c0ead7ef..4a755720 100644 --- a/src/manager/sdl_key.cpp +++ b/src/manager/sdl_key.cpp @@ -1,8 +1,9 @@ + +#include +#include + #include "sdl_key.hpp" -#include "helper/optional.hpp" -#include "helper/string_manipulation.hpp" -#include "helper/utils.hpp" #include #include @@ -223,10 +224,10 @@ namespace { } - helper::optional modifier_from_string(const std::string& modifier) { + std::optional modifier_from_string(const std::string& modifier) { if (modifier.empty()) { - return helper::nullopt; + return std::nullopt; } const auto lower_case = string::to_lower_case(modifier); @@ -255,7 +256,7 @@ namespace { return map.at(lower_case); } - return helper::nullopt; + return std::nullopt; } } // namespace diff --git a/src/manager/sdl_key.hpp b/src/manager/sdl_key.hpp index 279850b5..28e479d0 100644 --- a/src/manager/sdl_key.hpp +++ b/src/manager/sdl_key.hpp @@ -1,8 +1,7 @@ #pragma once -#include "helper/expected.hpp" - -#include "helper/types.hpp" +#include +#include #include #include diff --git a/src/manager/service_provider.hpp b/src/manager/service_provider.hpp index 44160039..e72ff38a 100644 --- a/src/manager/service_provider.hpp +++ b/src/manager/service_provider.hpp @@ -6,7 +6,7 @@ #if defined(_HAVE_DISCORD_SDK) && !defined(_OOPETRIS_RECORDING_UTILITY) #include "discord/core.hpp" -#include "helper/optional.hpp" + #endif @@ -55,8 +55,8 @@ struct ServiceProvider { #if defined(_HAVE_DISCORD_SDK) && !defined(_OOPETRIS_RECORDING_UTILITY) - [[nodiscard]] virtual helper::optional& discord_instance() = 0; - [[nodiscard]] virtual const helper::optional& discord_instance() const = 0; + [[nodiscard]] virtual std::optional& discord_instance() = 0; + [[nodiscard]] virtual const std::optional& discord_instance() const = 0; #endif }; diff --git a/src/manager/settings_manager.cpp b/src/manager/settings_manager.cpp index 0ec6caee..ff1d99ad 100644 --- a/src/manager/settings_manager.cpp +++ b/src/manager/settings_manager.cpp @@ -18,7 +18,7 @@ SettingsManager::SettingsManager(ServiceProvider* service_provider) : m_service_ spdlog::warn("applying default settings"); m_settings = { - detail::Settings{ {}, helper::nullopt, 1.0, false } + detail::Settings{ {}, std::nullopt, 1.0, false } }; } } diff --git a/src/manager/settings_manager.hpp b/src/manager/settings_manager.hpp index e1da0be2..3d84765f 100644 --- a/src/manager/settings_manager.hpp +++ b/src/manager/settings_manager.hpp @@ -1,6 +1,6 @@ #pragma once -#include "helper/optional.hpp" + #include "input/controller_input.hpp" #include "input/joystick_input.hpp" #include "input/keyboard_input.hpp" @@ -71,7 +71,7 @@ namespace detail { struct Settings { std::vector controls; - helper::optional selected; + std::optional selected; float volume{ 0.2F }; bool discord{ false }; //changing this requires a restart }; diff --git a/src/meson.build b/src/meson.build index 9152205c..86650198 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,130 +1,54 @@ -core_src_files = [] -recordings_src_files = [] -graphics_src_files = [] +subdir('libs') + +if build_application + + graphics_src_files = [] + + subdir('game') + subdir('graphics') + subdir('helper') + subdir('input') + subdir('manager') + subdir('scenes') + subdir('ui') + + if online_multiplayer_supported + subdir('lobby') + endif + + if have_discord_sdk + subdir('discord') + endif + + graphics_lib += { + 'deps': [graphics_lib.get('deps'), liboopetris_recordings_dep], + 'inc_dirs': [graphics_lib.get('inc_dirs'), include_directories('.')], + } + + liboopetris_graphics = library( + 'oopetris_graphics', + graphics_src_files, + include_directories: graphics_lib.get('inc_dirs'), + dependencies: graphics_lib.get('deps'), + cpp_args: graphics_lib.get('compile_args'), + override_options: { + 'warning_level': '3', + 'werror': true, + }, + version: meson.project_version(), + install: true, + ) + + liboopetris_graphics_dep = declare_dependency( + link_with: liboopetris_graphics, + include_directories: graphics_lib.get('inc_dirs'), + compile_args: graphics_lib.get('compile_args'), + dependencies: graphics_lib.get('deps'), + version: meson.project_version(), + ) + meson.override_dependency('liboopetris-graphics', liboopetris_graphics_dep) + + # setting this to strings, so += {...} gets detected as an error, if it is done after that + graphics_lib = 'undefined' -graphics_src_files += files( - 'application.cpp', - 'application.hpp', -) - -main_files = files( - 'main.cpp', -) - -subdir('game') -subdir('graphics') -subdir('helper') -subdir('input') -subdir('manager') -subdir('recordings') -subdir('scenes') -subdir('thirdparty') -subdir('ui') - -if online_multiplayer_supported - subdir('lobby') -endif - - -if have_discord_sdk - subdir('discord') endif - - -core_lib += { - 'src_files': [core_lib.get('src_files'), core_src_files], - 'inc_dirs': [core_lib.get('inc_dirs'), include_directories('.')], -} - -recordings_lib += { - 'src_files': [recordings_lib.get('src_files'), recordings_src_files], -} - -graphics_lib += { - 'src_files': [graphics_lib.get('src_files'), graphics_src_files], -} - -core_lib += { - 'deps': [core_lib.get('deps'), global_deps], -} - -liboopetris_core = library( - 'oopetris_core', - core_lib.get('src_files'), - include_directories: core_lib.get('inc_dirs'), - dependencies: core_lib.get('deps'), - cpp_args: core_lib.get('compile_args'), - override_options: { - 'warning_level': '3', - 'werror': true, - }, - version: meson.project_version(), - install: true, -) - -liboopetris_core_dep = declare_dependency( - link_with: liboopetris_core, - include_directories: core_lib.get('inc_dirs'), - compile_args: core_lib.get('compile_args'), - dependencies: core_lib.get('deps'), - version: meson.project_version(), -) - - -recordings_lib += { - 'deps': [recordings_lib.get('deps'), liboopetris_core_dep, global_deps], -} - -liboopetris_recordings = library( - 'oopetris_recordings', - recordings_lib.get('src_files'), - include_directories: recordings_lib.get('inc_dirs'), - dependencies: recordings_lib.get('deps'), - cpp_args: recordings_lib.get('compile_args'), - override_options: { - 'warning_level': '3', - 'werror': true, - }, - version: meson.project_version(), - install: true, -) - -liboopetris_recordings_dep = declare_dependency( - link_with: liboopetris_recordings, - include_directories: recordings_lib.get('inc_dirs'), - compile_args: recordings_lib.get('compile_args'), - dependencies: recordings_lib.get('deps'), - version: meson.project_version(), -) - -graphics_lib += { - 'deps': [graphics_lib.get('deps'), liboopetris_recordings_dep, global_deps], -} - - -liboopetris_graphics = library( - 'oopetris_graphics', - graphics_lib.get('src_files'), - include_directories: graphics_lib.get('inc_dirs'), - dependencies: graphics_lib.get('deps'), - cpp_args: graphics_lib.get('compile_args'), - override_options: { - 'warning_level': '3', - 'werror': true, - }, - version: meson.project_version(), - install: true, -) - -liboopetris_graphics_dep = declare_dependency( - link_with: liboopetris_graphics, - include_directories: graphics_lib.get('inc_dirs'), - compile_args: graphics_lib.get('compile_args'), - dependencies: graphics_lib.get('deps'), - version: meson.project_version(), -) - -# setting this to strings, so += {...} gets detected as an error, if it is done after that -core_lib = 'undefined' -recordings_lib = 'undefined' -graphics_lib = 'undefined' diff --git a/src/recordings/executable/command_line_arguments.hpp b/src/recordings/executable/command_line_arguments.hpp deleted file mode 100644 index 67fa914c..00000000 --- a/src/recordings/executable/command_line_arguments.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "helper/optional.hpp" - -#include -#include -#include -#include -#include -#include - -enum class Command { Info, Dump }; - -struct CommandLineArguments final { -private: -public: - std::filesystem::path recording_path{}; - Command command{ Command::Info }; - - CommandLineArguments(int argc, char** argv) { - argparse::ArgumentParser parser{ argc >= 1 ? argv[0] //NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) - : "oopetris_recording_utility", - "0.0.1", argparse::default_arguments::all }; - - parser.add_argument("command") - .help("specify the command") - .default_value(std::string{ "info" }) - .choices("info", "i", "dump", "d") - .required(); - - parser.add_argument("-r", "--recording").help("the path of a recorded game file").required(); - try { - - parser.parse_args(argc, argv); - - recording_path = parser.get("--recording"); - - const auto command_string = parser.get("command"); - - - if (command_string == "info" or command_string == "i") { - command = Command::Info; - } else if (command_string == "dump" || command_string == "d") { - command = Command::Dump; - } else { - throw std::runtime_error(fmt::format("Unknown command: '{}'", command_string)); - } - } catch (const std::exception& err) { - std::cerr << err.what(); - std::exit(1); - } - } -}; diff --git a/src/recordings/executable/main.cpp b/src/recordings/executable/main.cpp deleted file mode 100644 index 85488de4..00000000 --- a/src/recordings/executable/main.cpp +++ /dev/null @@ -1,21 +0,0 @@ - -#include "command_line_arguments.hpp" -#include "recording_reader.hpp" - -#include -#include - -int main(int argc, char** argv) noexcept { - - const auto arguments = CommandLineArguments(argc, argv); - - if (not std::filesystem::exists(arguments.recording_path)) { - std::cerr << arguments.recording_path << " does not exist!\n"; - return 1; - } - - - const auto parsed = recorder::RecordingReader::from_path(arguments.recording_path); - - return 0; -} diff --git a/src/recordings/executable/meson.build b/src/recordings/executable/meson.build deleted file mode 100644 index 79c61ea0..00000000 --- a/src/recordings/executable/meson.build +++ /dev/null @@ -1,3 +0,0 @@ - -recordings_main_files = files('main.cpp') - diff --git a/src/scenes/about_page/about_page.cpp b/src/scenes/about_page/about_page.cpp index e7fea40d..8e47c0ee 100644 --- a/src/scenes/about_page/about_page.cpp +++ b/src/scenes/about_page/about_page.cpp @@ -1,9 +1,11 @@ +#include + #include "about_page.hpp" #include "graphics/window.hpp" #include "helper/constants.hpp" #include "helper/git_helper.hpp" +#include "helper/graphic_utils.hpp" #include "helper/platform.hpp" -#include "helper/utils.hpp" #include "manager/resource_manager.hpp" #include "ui/components/image_view.hpp" #include "ui/components/link_label.hpp" @@ -11,6 +13,7 @@ #include #include + namespace scenes { AboutPage::AboutPage(ServiceProvider* service_provider, const ui::Layout& layout) : Scene{service_provider, layout} @@ -91,7 +94,7 @@ namespace scenes { if (m_should_exit) { return UpdateResult{ SceneUpdate::StopUpdating, Scene::Pop{} }; } - return UpdateResult{ SceneUpdate::StopUpdating, helper::nullopt }; + return UpdateResult{ SceneUpdate::StopUpdating, std::nullopt }; } void AboutPage::render(const ServiceProvider& service_provider) { diff --git a/src/scenes/loading_screen/loading_screen.cpp b/src/scenes/loading_screen/loading_screen.cpp index 35b53046..5fe75625 100644 --- a/src/scenes/loading_screen/loading_screen.cpp +++ b/src/scenes/loading_screen/loading_screen.cpp @@ -1,11 +1,12 @@ -#include "loading_screen.hpp" +#include +#include + #include "game/graphic_helpers.hpp" -#include "game/tetromino_type.hpp" -#include "graphics/point.hpp" #include "graphics/rect.hpp" #include "graphics/renderer.hpp" #include "graphics/window.hpp" #include "helper/platform.hpp" +#include "loading_screen.hpp" #include "manager/service_provider.hpp" #include "scenes/logo/logo.hpp" #include "ui/layout.hpp" diff --git a/src/scenes/loading_screen/loading_screen.hpp b/src/scenes/loading_screen/loading_screen.hpp index 5fc114c6..994d92a7 100644 --- a/src/scenes/loading_screen/loading_screen.hpp +++ b/src/scenes/loading_screen/loading_screen.hpp @@ -1,7 +1,8 @@ #pragma once +#include + #include "../logo/logo.hpp" -#include "game/mino.hpp" #include "graphics/rect.hpp" #include "manager/service_provider.hpp" diff --git a/src/scenes/logo/logo.cpp b/src/scenes/logo/logo.cpp index add3a065..2a07c408 100644 --- a/src/scenes/logo/logo.cpp +++ b/src/scenes/logo/logo.cpp @@ -1,9 +1,9 @@ +#include +#include -#include "logo.hpp" #include "game/graphic_helpers.hpp" -#include "game/mino.hpp" -#include "graphics/point.hpp" #include "graphics/renderer.hpp" +#include "logo.hpp" #include diff --git a/src/scenes/main_menu/main_menu.cpp b/src/scenes/main_menu/main_menu.cpp index 144ac6f3..9f7ea881 100644 --- a/src/scenes/main_menu/main_menu.cpp +++ b/src/scenes/main_menu/main_menu.cpp @@ -1,6 +1,7 @@ #include "main_menu.hpp" #include "graphics/window.hpp" #include "helper/constants.hpp" +#include "helper/graphic_utils.hpp" #include "helper/music_utils.hpp" #include "helper/platform.hpp" #include "manager/music_manager.hpp" @@ -111,28 +112,28 @@ namespace scenes { switch (m_next_command.value()) { case Command::OpenPlaySelection: // perform a push and reset the command, so that the music keeps playing the entire time - m_next_command = helper::nullopt; + m_next_command = std::nullopt; return UpdateResult{ SceneUpdate::StopUpdating, Scene::Push{ SceneId::PlaySelectMenu, ui::FullScreenLayout{ m_service_provider->window() } } }; case Command::OpenAboutPage: // perform a push and reset the command, so that the music keeps playing the entire time - m_next_command = helper::nullopt; + m_next_command = std::nullopt; return UpdateResult{ SceneUpdate::StopUpdating, Scene::Push{ SceneId::AboutPage, ui::FullScreenLayout{ m_service_provider->window() } } }; case Command::OpenSettingsMenu: // perform a push and reset the command, so that the music keeps playing the entire time - m_next_command = helper::nullopt; + m_next_command = std::nullopt; return UpdateResult{ SceneUpdate::StopUpdating, Scene::Push{ SceneId::SettingsMenu, ui::FullScreenLayout{ m_service_provider->window() } } }; case Command::OpenAchievements: // perform a push and reset the command, so that the music keeps playing the entire time - m_next_command = helper::nullopt; + m_next_command = std::nullopt; return UpdateResult{ SceneUpdate::StopUpdating, Scene::Push{ SceneId::AchievementsPage, ui::FullScreenLayout{ m_service_provider->window() } } @@ -143,7 +144,7 @@ namespace scenes { UNREACHABLE(); } } - return UpdateResult{ SceneUpdate::StopUpdating, helper::nullopt }; + return UpdateResult{ SceneUpdate::StopUpdating, std::nullopt }; } void MainMenu::render(const ServiceProvider& service_provider) { diff --git a/src/scenes/main_menu/main_menu.hpp b/src/scenes/main_menu/main_menu.hpp index c513ab10..7e9fc2a1 100644 --- a/src/scenes/main_menu/main_menu.hpp +++ b/src/scenes/main_menu/main_menu.hpp @@ -18,7 +18,7 @@ namespace scenes { }; ui::GridLayout m_main_grid; - helper::optional m_next_command; + std::optional m_next_command; public: explicit MainMenu(ServiceProvider* service_provider, const ui::Layout& layout); diff --git a/src/scenes/multiplayer_menu/multiplayer_menu.cpp b/src/scenes/multiplayer_menu/multiplayer_menu.cpp index 49f36442..c4852bde 100644 --- a/src/scenes/multiplayer_menu/multiplayer_menu.cpp +++ b/src/scenes/multiplayer_menu/multiplayer_menu.cpp @@ -92,7 +92,7 @@ namespace scenes { }; case Command::OnlineMultiPlayer: // perform a push and reset the command, so that the music keeps playing the entire time - m_next_command = helper::nullopt; + m_next_command = std::nullopt; return UpdateResult{ SceneUpdate::StopUpdating, Scene::Push{ SceneId::OnlineLobby, ui::FullScreenLayout{ m_service_provider->window() } } @@ -108,7 +108,7 @@ namespace scenes { UNREACHABLE(); } } - return UpdateResult{ SceneUpdate::StopUpdating, helper::nullopt }; + return UpdateResult{ SceneUpdate::StopUpdating, std::nullopt }; } void MultiPlayerMenu::render(const ServiceProvider& service_provider) { diff --git a/src/scenes/multiplayer_menu/multiplayer_menu.hpp b/src/scenes/multiplayer_menu/multiplayer_menu.hpp index b498b418..68f4fdbe 100644 --- a/src/scenes/multiplayer_menu/multiplayer_menu.hpp +++ b/src/scenes/multiplayer_menu/multiplayer_menu.hpp @@ -12,7 +12,7 @@ namespace scenes { enum class Command : u8 { LocalMultiPlayer, OnlineMultiPlayer, AIMultiPlayer, Return }; ui::GridLayout m_main_grid; - helper::optional m_next_command; + std::optional m_next_command; public: explicit MultiPlayerMenu(ServiceProvider* service_provider, const ui::Layout& layout); diff --git a/src/scenes/online_lobby/online_lobby.cpp b/src/scenes/online_lobby/online_lobby.cpp index 8ade0c8b..9f2156a8 100644 --- a/src/scenes/online_lobby/online_lobby.cpp +++ b/src/scenes/online_lobby/online_lobby.cpp @@ -1,12 +1,15 @@ #include "online_lobby.hpp" + +#include +#include +#include + #include "graphics/window.hpp" #include "helper/constants.hpp" -#include "helper/errors.hpp" -#include "helper/magic_enum_wrapper.hpp" #include "helper/platform.hpp" -#include "helper/utils.hpp" #include "manager/music_manager.hpp" #include "manager/resource_manager.hpp" + #include "ui/components/textinput.hpp" #include "ui/layout.hpp" #include "ui/layouts/scroll_layout.hpp" @@ -28,7 +31,7 @@ namespace scenes { //TODO(Totto): after the settings have been reworked, make this url changeable! auto maybe_client = lobby::Client::get_client("http://127.0.0.1:5000"); if (maybe_client.has_value()) { - client = std::make_unique(std::move(maybe_client.value())); + m_client = std::make_unique(std::move(maybe_client.value())); } else { spdlog::error("Error in connecting to lobby client: {}", maybe_client.error()); } @@ -109,7 +112,7 @@ namespace scenes { UNREACHABLE(); } } - return UpdateResult{ SceneUpdate::StopUpdating, helper::nullopt }; + return UpdateResult{ SceneUpdate::StopUpdating, std::nullopt }; } void OnlineLobby::render(const ServiceProvider& service_provider) { diff --git a/src/scenes/online_lobby/online_lobby.hpp b/src/scenes/online_lobby/online_lobby.hpp index e391bc3a..78872e93 100644 --- a/src/scenes/online_lobby/online_lobby.hpp +++ b/src/scenes/online_lobby/online_lobby.hpp @@ -1,6 +1,7 @@ #pragma once #include "lobby/api.hpp" + #include "scenes/scene.hpp" #include "ui/components/label.hpp" #include "ui/components/text_button.hpp" @@ -15,8 +16,8 @@ namespace scenes { enum class Command : u8 { Play, Return }; ui::TileLayout m_main_layout; - helper::optional m_next_command; - std::unique_ptr client{ nullptr }; + std::optional m_next_command; + std::unique_ptr m_client{ nullptr }; public: explicit OnlineLobby(ServiceProvider* service_provider, const ui::Layout& layout); diff --git a/src/scenes/play_select_menu/play_select_menu.cpp b/src/scenes/play_select_menu/play_select_menu.cpp index ac1f8b91..b6c26b5b 100644 --- a/src/scenes/play_select_menu/play_select_menu.cpp +++ b/src/scenes/play_select_menu/play_select_menu.cpp @@ -78,21 +78,21 @@ namespace scenes { if (m_next_command.has_value()) { switch (m_next_command.value()) { case Command::SinglePlayer: - m_next_command = helper::nullopt; + m_next_command = std::nullopt; return UpdateResult{ SceneUpdate::StopUpdating, Scene::Switch{ SceneId::SinglePlayerGame, ui::FullScreenLayout{ m_service_provider->window() } } }; case Command::MultiPlayer: // perform a push and reset the command, so that the music keeps playing the entire time - m_next_command = helper::nullopt; + m_next_command = std::nullopt; return UpdateResult{ SceneUpdate::StopUpdating, Scene::Push{ SceneId::MultiPlayerModeSelectMenu, ui::FullScreenLayout{ m_service_provider->window() } } }; case Command::RecordingSelector: // perform a push and reset the command, so that the music keeps playing the entire time - m_next_command = helper::nullopt; + m_next_command = std::nullopt; return UpdateResult{ SceneUpdate::StopUpdating, Scene::Push{ SceneId::RecordingSelectorMenu, ui::FullScreenLayout{ m_service_provider->window() } } @@ -103,7 +103,7 @@ namespace scenes { UNREACHABLE(); } } - return UpdateResult{ SceneUpdate::StopUpdating, helper::nullopt }; + return UpdateResult{ SceneUpdate::StopUpdating, std::nullopt }; } void PlaySelectMenu::render(const ServiceProvider& service_provider) { diff --git a/src/scenes/play_select_menu/play_select_menu.hpp b/src/scenes/play_select_menu/play_select_menu.hpp index b362dd52..ad5bb577 100644 --- a/src/scenes/play_select_menu/play_select_menu.hpp +++ b/src/scenes/play_select_menu/play_select_menu.hpp @@ -12,7 +12,7 @@ namespace scenes { enum class Command : u8 { SinglePlayer, MultiPlayer, RecordingSelector, Return }; ui::GridLayout m_main_grid; - helper::optional m_next_command; + std::optional m_next_command; public: explicit PlaySelectMenu(ServiceProvider* service_provider, const ui::Layout& layout); diff --git a/src/scenes/recording_selector/recording_chooser.cpp b/src/scenes/recording_selector/recording_chooser.cpp index 85c193fc..6541469a 100644 --- a/src/scenes/recording_selector/recording_chooser.cpp +++ b/src/scenes/recording_selector/recording_chooser.cpp @@ -1,13 +1,12 @@ #include "recording_chooser.hpp" +#include #include "helper/nfd_include.hpp" #include "manager/event_dispatcher.hpp" #include "manager/font.hpp" #include "manager/resource_manager.hpp" -#include "recordings/recording.hpp" -#include "recordings/recording_reader.hpp" #include "ui/components/text_button.hpp" custom_ui::RecordingFileChooser::RecordingFileChooser( diff --git a/src/scenes/recording_selector/recording_chooser.hpp b/src/scenes/recording_selector/recording_chooser.hpp index 7b824c66..a80b5190 100644 --- a/src/scenes/recording_selector/recording_chooser.hpp +++ b/src/scenes/recording_selector/recording_chooser.hpp @@ -2,7 +2,8 @@ #pragma once -#include "helper/color_literals.hpp" +#include + #include "ui/components/label.hpp" #include "ui/focusable.hpp" #include "ui/hoverable.hpp" diff --git a/src/scenes/recording_selector/recording_component.cpp b/src/scenes/recording_selector/recording_component.cpp index 97d50ff0..17fc7421 100644 --- a/src/scenes/recording_selector/recording_component.cpp +++ b/src/scenes/recording_selector/recording_component.cpp @@ -1,10 +1,10 @@ +#include +#include -#include "recording_component.hpp" -#include "helper/date.hpp" -#include "helper/magic_enum_wrapper.hpp" #include "input/input.hpp" #include "manager/font.hpp" #include "manager/resource_manager.hpp" +#include "recording_component.hpp" #include "ui/widget.hpp" #include diff --git a/src/scenes/recording_selector/recording_component.hpp b/src/scenes/recording_selector/recording_component.hpp index 4e554b92..9773cddf 100644 --- a/src/scenes/recording_selector/recording_component.hpp +++ b/src/scenes/recording_selector/recording_component.hpp @@ -2,8 +2,9 @@ #pragma once -#include "helper/color_literals.hpp" -#include "recordings/recording.hpp" +#include +#include + #include "ui/components/label.hpp" #include "ui/focusable.hpp" #include "ui/hoverable.hpp" diff --git a/src/scenes/recording_selector/recording_selector.cpp b/src/scenes/recording_selector/recording_selector.cpp index 17c63795..373e504c 100644 --- a/src/scenes/recording_selector/recording_selector.cpp +++ b/src/scenes/recording_selector/recording_selector.cpp @@ -1,18 +1,19 @@ -#include "helper/platform.hpp" -#include "helper/utils.hpp" +#include +#include "helper/platform.hpp" #if defined(_HAVE_FILE_DIALOGS) #include "recording_chooser.hpp" #endif +#include + #include "graphics/window.hpp" #include "helper/constants.hpp" -#include "helper/optional.hpp" +#include "helper/graphic_utils.hpp" #include "manager/resource_manager.hpp" #include "recording_component.hpp" #include "recording_selector.hpp" -#include "recordings/recording_reader.hpp" #include "scenes/replay_game/replay_game.hpp" #include "ui/components/text_button.hpp" #include "ui/layout.hpp" @@ -87,7 +88,7 @@ namespace scenes { const auto recording_path = recording_component.value()->metadata().path; // action is a reference to a structure inside m_next_command, so resetting it means, we need to copy everything out of it - m_next_command = helper::nullopt; + m_next_command = std::nullopt; return UpdateResult{ SceneUpdate::StopUpdating, @@ -111,9 +112,9 @@ namespace scenes { add_all_recordings(); // action is a reference to a structure inside m_next_command, so resetting it means, we need to copy everything out of it - m_next_command = helper::nullopt; + m_next_command = std::nullopt; - return UpdateResult{ SceneUpdate::StopUpdating, helper::nullopt }; + return UpdateResult{ SceneUpdate::StopUpdating, std::nullopt }; } #endif @@ -123,7 +124,7 @@ namespace scenes { ); } - return UpdateResult{ SceneUpdate::StopUpdating, helper::nullopt }; + return UpdateResult{ SceneUpdate::StopUpdating, std::nullopt }; } void RecordingSelector::render(const ServiceProvider& service_provider) { diff --git a/src/scenes/recording_selector/recording_selector.hpp b/src/scenes/recording_selector/recording_selector.hpp index dcdd40c6..4f0861a1 100644 --- a/src/scenes/recording_selector/recording_selector.hpp +++ b/src/scenes/recording_selector/recording_selector.hpp @@ -33,8 +33,8 @@ namespace scenes { struct RecordingSelector : public Scene { private: ui::TileLayout m_main_layout; - helper::optional m_next_command{ helper::nullopt }; - std::vector m_chosen_paths{}; + std::optional m_next_command{ std::nullopt }; + std::vector m_chosen_paths; public: explicit RecordingSelector(ServiceProvider* service_provider, const ui::Layout& layout); diff --git a/src/scenes/replay_game/replay_game.cpp b/src/scenes/replay_game/replay_game.cpp index 8680a559..4a2362aa 100644 --- a/src/scenes/replay_game/replay_game.cpp +++ b/src/scenes/replay_game/replay_game.cpp @@ -1,6 +1,8 @@ #include "replay_game.hpp" #include "../single_player_game/game_over.hpp" #include "../single_player_game/pause.hpp" +#include "helper/constants.hpp" +#include "helper/graphic_utils.hpp" #include "helper/music_utils.hpp" #include "manager/music_manager.hpp" #include "scenes/scene.hpp" @@ -97,7 +99,7 @@ namespace scenes { if (m_next_scene.has_value()) { const auto next_scene = m_next_scene.value(); - m_next_scene = helper::nullopt; + m_next_scene = std::nullopt; for (auto& game : m_games) { game->set_paused(true); } @@ -122,7 +124,7 @@ namespace scenes { UNREACHABLE(); } } - return UpdateResult{ SceneUpdate::StopUpdating, helper::nullopt }; + return UpdateResult{ SceneUpdate::StopUpdating, std::nullopt }; } void ReplayGame::render(const ServiceProvider& service_provider) { diff --git a/src/scenes/replay_game/replay_game.hpp b/src/scenes/replay_game/replay_game.hpp index 0c6ad5cb..1cc0d44a 100644 --- a/src/scenes/replay_game/replay_game.hpp +++ b/src/scenes/replay_game/replay_game.hpp @@ -10,7 +10,7 @@ namespace scenes { enum class NextScene : u8 { Pause, Settings }; - helper::optional m_next_scene{}; + std::optional m_next_scene; std::vector> m_games; public: diff --git a/src/scenes/scene.cpp b/src/scenes/scene.cpp index a7f41891..b1e27f9c 100644 --- a/src/scenes/scene.cpp +++ b/src/scenes/scene.cpp @@ -1,17 +1,19 @@ -#include "scenes/scene.hpp" + +#if !defined(_ONLINE_MULTIPLAYER_NOT_SUPPORTED) +#include "online_lobby/online_lobby.hpp" +#endif + + #include "about_page/about_page.hpp" #include "main_menu/main_menu.hpp" #include "multiplayer_menu/multiplayer_menu.hpp" #include "play_select_menu/play_select_menu.hpp" #include "recording_selector/recording_selector.hpp" #include "replay_game/replay_game.hpp" +#include "scenes/scene.hpp" #include "settings_menu/settings_menu.hpp" #include "single_player_game/single_player_game.hpp" -#if !defined(_ONLINE_MULTIPLAYER_NOT_SUPPORTED) -#include "online_lobby/online_lobby.hpp" -#endif - namespace scenes { Scene::Scene(ServiceProvider* service_provider, const ui::Layout& layout) diff --git a/src/scenes/scene.hpp b/src/scenes/scene.hpp index 5f2b4ed4..a0481587 100644 --- a/src/scenes/scene.hpp +++ b/src/scenes/scene.hpp @@ -1,6 +1,6 @@ #pragma once -#include "helper/command_line_arguments.hpp" +#include "game/command_line_arguments.hpp" #include "input/input.hpp" #include "manager/event_listener.hpp" #include "manager/service_provider.hpp" @@ -8,7 +8,7 @@ #include "ui/layout.hpp" #include - +#include namespace scenes { enum class SceneUpdate : u8 { @@ -59,7 +59,7 @@ namespace scenes { struct Exit { }; using Change = std::variant; - using UpdateResult = std::pair>; + using UpdateResult = std::pair>; protected: ServiceProvider* m_service_provider; diff --git a/src/scenes/scene_id.hpp b/src/scenes/scene_id.hpp index 845470fc..8f2e18b9 100644 --- a/src/scenes/scene_id.hpp +++ b/src/scenes/scene_id.hpp @@ -1,6 +1,6 @@ #pragma once -#include "helper/types.hpp" +#include enum class SceneId : u8 { AboutPage, diff --git a/src/scenes/settings_menu/color_setting_row.cpp b/src/scenes/settings_menu/color_setting_row.cpp index c4cec4f9..c6a6316a 100644 --- a/src/scenes/settings_menu/color_setting_row.cpp +++ b/src/scenes/settings_menu/color_setting_row.cpp @@ -1,8 +1,9 @@ +#include +#include +#include + #include "color_setting_row.hpp" -#include "helper/errors.hpp" -#include "helper/magic_enum_wrapper.hpp" -#include "helper/utils.hpp" #include "input/input.hpp" #include "ui/components/label.hpp" #include "ui/focusable.hpp" @@ -97,7 +98,7 @@ detail::ColorPickerScene::ColorPickerScene( if (m_should_exit) { return UpdateResult{ scenes::SceneUpdate::StopUpdating, Scene::Pop{} }; } - return UpdateResult{ scenes::SceneUpdate::StopUpdating, helper::nullopt }; + return UpdateResult{ scenes::SceneUpdate::StopUpdating, std::nullopt }; } void detail::ColorPickerScene::render(const ServiceProvider& service_provider) { diff --git a/src/scenes/settings_menu/settings_menu.cpp b/src/scenes/settings_menu/settings_menu.cpp index f3f35b8b..ecfdeee1 100644 --- a/src/scenes/settings_menu/settings_menu.cpp +++ b/src/scenes/settings_menu/settings_menu.cpp @@ -1,11 +1,11 @@ -#include "settings_menu.hpp" +#include +#include + #include "color_setting_row.hpp" -#include "helper/color_literals.hpp" -#include "helper/optional.hpp" -#include "helper/utils.hpp" #include "manager/music_manager.hpp" #include "manager/resource_manager.hpp" #include "settings_details.hpp" +#include "settings_menu.hpp" #include "ui/components/label.hpp" #include "ui/components/slider.hpp" #include "ui/components/text_button.hpp" @@ -18,17 +18,16 @@ namespace scenes { using namespace details::settings::menu; SettingsMenu::SettingsMenu(ServiceProvider* service_provider, const ui::Layout& layout) - : SettingsMenu{ service_provider, layout, helper::nullopt } { } + : SettingsMenu{ service_provider, layout, std::nullopt } { } SettingsMenu::SettingsMenu( ServiceProvider* service_provider, const ui::Layout& layout, const std::shared_ptr& game_input ) - : SettingsMenu{ service_provider, layout, helper::optional>{ game_input } } { - } + : SettingsMenu{ service_provider, layout, std::optional>{ game_input } } { } - SettingsMenu::SettingsMenu(ServiceProvider* service_provider, const ui::Layout& layout, const helper::optional>& game_input) : Scene{service_provider, layout} + SettingsMenu::SettingsMenu(ServiceProvider* service_provider, const ui::Layout& layout, const std::optional>& game_input) : Scene{service_provider, layout} , m_main_layout{ utils::size_t_identity<3>(), 0, ui::Direction::Vertical, @@ -70,7 +69,7 @@ namespace scenes { return value.has_value() ? value.value() : 0.0F; }, [service_provider](double amount) { - const auto mapped_amount = amount <= 0.0F ? helper::nullopt : helper::optional{ amount }; + const auto mapped_amount = amount <= 0.0F ? std::nullopt : std::optional{ amount }; service_provider->music_manager().set_volume(mapped_amount, false, false); }, 0.05F, std::pair{ 0.6, 1.0 }, @@ -79,7 +78,7 @@ namespace scenes { service_provider->music_manager().add_volume_listener( listener_name, - [this, scroll_layout_index, slider_index](helper::optional) { + [this, scroll_layout_index, slider_index](std::optional) { auto* scroll_layout = this->m_main_layout.get(scroll_layout_index); scroll_layout->get(slider_index)->on_change(); } @@ -136,7 +135,7 @@ namespace scenes { auto change_scene = settings_details.value()->get_details_scene(); // action is a reference to a structure inside m_next_command, so resetting it means, we need to copy everything out of it - m_next_command = helper::nullopt; + m_next_command = std::nullopt; return UpdateResult{ SceneUpdate::StopUpdating, std::move(change_scene) }; } @@ -148,7 +147,7 @@ namespace scenes { ); } - return UpdateResult{ SceneUpdate::StopUpdating, helper::nullopt }; + return UpdateResult{ SceneUpdate::StopUpdating, std::nullopt }; } void SettingsMenu::render(const ServiceProvider& service_provider) { diff --git a/src/scenes/settings_menu/settings_menu.hpp b/src/scenes/settings_menu/settings_menu.hpp index 80b52ed4..425b10c4 100644 --- a/src/scenes/settings_menu/settings_menu.hpp +++ b/src/scenes/settings_menu/settings_menu.hpp @@ -1,6 +1,7 @@ #pragma once -#include "helper/color.hpp" +#include + #include "scenes/scene.hpp" #include "ui/layouts/tile_layout.hpp" #include "ui/widget.hpp" @@ -32,18 +33,18 @@ namespace scenes { struct SettingsMenu : public Scene { private: - helper::optional m_next_command{ helper::nullopt }; + std::optional m_next_command{ std::nullopt }; ui::TileLayout m_main_layout; //todo migrate to settings state std::vector m_colors; - helper::optional> m_game_input; + std::optional> m_game_input; const std::string listener_name = "settings_menu"; explicit SettingsMenu( ServiceProvider* service_provider, const ui::Layout& layout, - const helper::optional>& game_input + const std::optional>& game_input ); public: diff --git a/src/scenes/single_player_game/game_over.cpp b/src/scenes/single_player_game/game_over.cpp index 7b8c1e67..9b04d836 100644 --- a/src/scenes/single_player_game/game_over.cpp +++ b/src/scenes/single_player_game/game_over.cpp @@ -1,5 +1,6 @@ #include "game_over.hpp" #include "graphics/renderer.hpp" +#include "helper/graphic_utils.hpp" #include "helper/music_utils.hpp" #include "helper/platform.hpp" #include "input/input.hpp" @@ -41,7 +42,7 @@ namespace scenes { Scene::Switch{ SceneId::MainMenu, ui::FullScreenLayout{ m_service_provider->window() } } }; } - return UpdateResult{ SceneUpdate::StopUpdating, helper::nullopt }; + return UpdateResult{ SceneUpdate::StopUpdating, std::nullopt }; } void SinglePlayerGameOver::render(const ServiceProvider& service_provider) { diff --git a/src/scenes/single_player_game/meson.build b/src/scenes/single_player_game/meson.build index 39c173eb..ff8d9e88 100644 --- a/src/scenes/single_player_game/meson.build +++ b/src/scenes/single_player_game/meson.build @@ -1,8 +1,8 @@ graphics_src_files += files( - 'pause.cpp', - 'pause.hpp', 'game_over.cpp', 'game_over.hpp', + 'pause.cpp', + 'pause.hpp', 'single_player_game.cpp', 'single_player_game.hpp', ) diff --git a/src/scenes/single_player_game/pause.cpp b/src/scenes/single_player_game/pause.cpp index 59694e87..54c1cbde 100644 --- a/src/scenes/single_player_game/pause.cpp +++ b/src/scenes/single_player_game/pause.cpp @@ -35,7 +35,7 @@ namespace scenes { Scene::Switch{ SceneId::MainMenu, ui::FullScreenLayout{ m_service_provider->window() } } }; } - return UpdateResult{ SceneUpdate::StopUpdating, helper::nullopt }; + return UpdateResult{ SceneUpdate::StopUpdating, std::nullopt }; } void SinglePlayerPause::render(const ServiceProvider& service_provider) { diff --git a/src/scenes/single_player_game/single_player_game.cpp b/src/scenes/single_player_game/single_player_game.cpp index 3c7de69c..e1a23899 100644 --- a/src/scenes/single_player_game/single_player_game.cpp +++ b/src/scenes/single_player_game/single_player_game.cpp @@ -1,16 +1,19 @@ -#include "single_player_game.hpp" -#include "helper/date.hpp" -#include "helper/errors.hpp" +#include +#include +#include + +#include "helper/constants.hpp" +#include "helper/graphic_utils.hpp" #include "helper/music_utils.hpp" #include "helper/platform.hpp" #include "input/game_input.hpp" #include "input/input.hpp" -#include "magic_enum.hpp" #include "manager/music_manager.hpp" #include "scenes/scene.hpp" #include "scenes/settings_menu/settings_menu.hpp" #include "scenes/single_player_game/game_over.hpp" #include "scenes/single_player_game/pause.hpp" +#include "single_player_game.hpp" namespace scenes { @@ -81,7 +84,7 @@ namespace scenes { if (m_next_scene.has_value()) { const auto next_scene = m_next_scene.value(); - m_next_scene = helper::nullopt; + m_next_scene = std::nullopt; m_game->set_paused(true); switch (next_scene) { @@ -108,7 +111,7 @@ namespace scenes { UNREACHABLE(); } } - return UpdateResult{ SceneUpdate::StopUpdating, helper::nullopt }; + return UpdateResult{ SceneUpdate::StopUpdating, std::nullopt }; } void SinglePlayerGame::render(const ServiceProvider& service_provider) { diff --git a/src/scenes/single_player_game/single_player_game.hpp b/src/scenes/single_player_game/single_player_game.hpp index 254205c3..d7389579 100644 --- a/src/scenes/single_player_game/single_player_game.hpp +++ b/src/scenes/single_player_game/single_player_game.hpp @@ -11,7 +11,7 @@ namespace scenes { enum class NextScene : u8 { Pause, Settings }; - helper::optional m_next_scene{}; + std::optional m_next_scene; std::unique_ptr m_game; public: diff --git a/src/thirdparty/hash-library/meson.build b/src/thirdparty/hash-library/meson.build deleted file mode 100644 index a9f518dd..00000000 --- a/src/thirdparty/hash-library/meson.build +++ /dev/null @@ -1,10 +0,0 @@ -core_src_files += files( - 'sha256.cpp', - 'sha256.h', -) - - -core_lib += { - 'inc_dirs': [core_lib.get('inc_dirs'), include_directories('.')], -} - diff --git a/src/thirdparty/meson.build b/src/thirdparty/meson.build deleted file mode 100644 index 75d9d183..00000000 --- a/src/thirdparty/meson.build +++ /dev/null @@ -1,4 +0,0 @@ - - -subdir('hash-library') - diff --git a/src/ui/components/button.hpp b/src/ui/components/button.hpp index 8367fc43..e89ae33f 100644 --- a/src/ui/components/button.hpp +++ b/src/ui/components/button.hpp @@ -1,12 +1,13 @@ #pragma once +#include + #include #include #include #include "graphics/rect.hpp" #include "graphics/renderer.hpp" -#include "helper/color_literals.hpp" #include "input/input.hpp" #include "ui/focusable.hpp" #include "ui/hoverable.hpp" diff --git a/src/ui/components/color_picker.cpp b/src/ui/components/color_picker.cpp index 0ce5a0ef..d7bca826 100644 --- a/src/ui/components/color_picker.cpp +++ b/src/ui/components/color_picker.cpp @@ -1,11 +1,11 @@ +#include +#include +#include #include "color_picker.hpp" -#include "graphics/point.hpp" #include "graphics/rect.hpp" -#include "helper/color.hpp" #include "helper/graphic_utils.hpp" -#include "helper/utils.hpp" #include "input/input.hpp" #include "manager/resource_manager.hpp" #include "ui/components/textinput.hpp" diff --git a/src/ui/components/color_picker.hpp b/src/ui/components/color_picker.hpp index 89b96775..2b4935a9 100644 --- a/src/ui/components/color_picker.hpp +++ b/src/ui/components/color_picker.hpp @@ -1,9 +1,10 @@ #pragma once -#include "graphics/point.hpp" +#include +#include + #include "graphics/rect.hpp" -#include "helper/color.hpp" #include "ui/components/abstract_slider.hpp" #include "ui/components/image_button.hpp" #include "ui/components/textinput.hpp" diff --git a/src/ui/components/slider.cpp b/src/ui/components/slider.cpp index 8f07cadf..f5c22c2b 100644 --- a/src/ui/components/slider.cpp +++ b/src/ui/components/slider.cpp @@ -1,8 +1,8 @@ +#include +#include -#include "slider.hpp" #include "graphics/renderer.hpp" -#include "helper/color.hpp" -#include "helper/color_literals.hpp" +#include "slider.hpp" ui::Slider::Slider( u32 focus_id, diff --git a/src/ui/components/textinput.cpp b/src/ui/components/textinput.cpp index acdde28e..debd2271 100644 --- a/src/ui/components/textinput.cpp +++ b/src/ui/components/textinput.cpp @@ -1,10 +1,10 @@ +#include +#include -#include "textinput.hpp" #include "graphics/renderer.hpp" -#include "helper/color_literals.hpp" -#include "helper/errors.hpp" #include "manager/event_dispatcher.hpp" +#include "textinput.hpp" using namespace std::chrono_literals; diff --git a/src/ui/components/textinput.hpp b/src/ui/components/textinput.hpp index 0d0924cb..f3d4de6a 100644 --- a/src/ui/components/textinput.hpp +++ b/src/ui/components/textinput.hpp @@ -1,8 +1,9 @@ #pragma once +#include +#include + #include "graphics/texture.hpp" -#include "helper/color.hpp" -#include "helper/timer.hpp" #include "ui/focusable.hpp" #include "ui/hoverable.hpp" #include "ui/widget.hpp" diff --git a/src/ui/focusable.hpp b/src/ui/focusable.hpp index a5d6c233..ea144e7d 100644 --- a/src/ui/focusable.hpp +++ b/src/ui/focusable.hpp @@ -1,6 +1,6 @@ #pragma once -#include "helper/types.hpp" +#include #include diff --git a/src/ui/hoverable.hpp b/src/ui/hoverable.hpp index 1be0f48f..02a6f7f3 100644 --- a/src/ui/hoverable.hpp +++ b/src/ui/hoverable.hpp @@ -1,9 +1,10 @@ #pragma once +#include +#include +#include + #include "graphics/rect.hpp" -#include "helper/bool_wrapper.hpp" -#include "helper/types.hpp" -#include "helper/utils.hpp" #include "input/input.hpp" namespace ui { diff --git a/src/ui/layout.hpp b/src/ui/layout.hpp index e5b8e9c0..112f3ceb 100644 --- a/src/ui/layout.hpp +++ b/src/ui/layout.hpp @@ -1,9 +1,10 @@ #pragma once +#include +#include + #include "graphics/rect.hpp" #include "graphics/window.hpp" -#include "helper/types.hpp" -#include "helper/utils.hpp" #include diff --git a/src/ui/layouts/focus_layout.cpp b/src/ui/layouts/focus_layout.cpp index a7707994..55db9e98 100644 --- a/src/ui/layouts/focus_layout.cpp +++ b/src/ui/layouts/focus_layout.cpp @@ -1,6 +1,6 @@ #include "focus_layout.hpp" -#include "helper/optional.hpp" + #include "input/input.hpp" #include "ui/widget.hpp" @@ -127,11 +127,11 @@ ui::Widget::EventHandleResult ui::FocusLayout::handle_focus_change_events( return handled; } -[[nodiscard]] helper::optional -ui::FocusLayout::handle_event_result(const helper::optional& result, Widget* widget) { +[[nodiscard]] std::optional +ui::FocusLayout::handle_event_result(const std::optional& result, Widget* widget) { if (not result.has_value()) { - return helper::nullopt; + return std::nullopt; } auto value = result.value(); @@ -144,13 +144,13 @@ ui::FocusLayout::handle_event_result(const helper::optionalfocus_id(); if (m_focus_id.value() == widget_focus_id) { - return helper::nullopt; + return std::nullopt; } auto current_focusable = as_focusable(m_widgets.at(focusable_index_by_id(m_focus_id.value())).get()); @@ -165,11 +165,11 @@ ui::FocusLayout::handle_event_result(const helper::optionalfocus_id(); if (m_focus_id.value() != widget_focus_id) { - return helper::nullopt; + return std::nullopt; } // try to unfocus, in forward direction, if that fails request unfocus of they container / layout itself, also test backwards, if not wrapping! @@ -201,7 +201,7 @@ ui::FocusLayout::handle_event_result(const helper::optional + #include "ui/focusable.hpp" #include "ui/widget.hpp" @@ -25,8 +26,10 @@ namespace ui { FocusOptions m_options; protected: - helper::optional m_focus_id{}; - std::vector> m_widgets{}; + std::optional + m_focus_id; // NOLINT(misc-non-private-member-variables-in-classes,cppcoreguidelines-non-private-member-variables-in-classes) + std::vector> + m_widgets; // NOLINT(misc-non-private-member-variables-in-classes,cppcoreguidelines-non-private-member-variables-in-classes) public: explicit FocusLayout(const Layout& layout, u32 focus_id, FocusOptions options, bool is_top_level); @@ -114,8 +117,8 @@ namespace ui { const SDL_Event& event ); - [[nodiscard]] helper::optional - handle_event_result(const helper::optional& result, Widget* widget); + [[nodiscard]] std::optional + handle_event_result(const std::optional& result, Widget* widget); [[nodiscard]] u32 focusable_index_by_id(u32 id) const; diff --git a/src/ui/layouts/grid_layout.hpp b/src/ui/layouts/grid_layout.hpp index 9c7e6885..1790ac8c 100644 --- a/src/ui/layouts/grid_layout.hpp +++ b/src/ui/layouts/grid_layout.hpp @@ -1,8 +1,8 @@ #pragma once +#include #include "focus_layout.hpp" -#include "helper/types.hpp" namespace ui { struct GridLayout : public FocusLayout { diff --git a/src/ui/layouts/scroll_layout.cpp b/src/ui/layouts/scroll_layout.cpp index 19d5ed4b..6dd029be 100644 --- a/src/ui/layouts/scroll_layout.cpp +++ b/src/ui/layouts/scroll_layout.cpp @@ -1,7 +1,7 @@ +#include -#include "scroll_layout.hpp" -#include "helper/color_literals.hpp" #include "input/input.hpp" +#include "scroll_layout.hpp" ui::ItemSize::ItemSize(const u32 height, ItemSizeType type) : height{ height }, type{ type } { } @@ -39,7 +39,7 @@ ui::ScrollLayout::ScrollLayout( : FocusLayout{ layout, focus_id, FocusOptions{ is_top_level, is_top_level }, is_top_level}, // if on top, we support tab and wrap around, otherwise not gap{ gap }, - m_texture{ helper::nullopt }, + m_texture{ std::nullopt }, m_service_provider{ service_provider }, m_step_size{ static_cast(layout.get_rect().height() * 0.05) } { @@ -283,8 +283,8 @@ ui::Widget::EventHandleResult ui::ScrollLayout::handle_focus_change_events( void ui::ScrollLayout::clear_widgets() { m_widgets.clear(); - m_texture = helper::nullopt; - m_focus_id = helper::nullopt; + m_texture = std::nullopt; + m_focus_id = std::nullopt; recalculate_sizes(0); } diff --git a/src/ui/layouts/scroll_layout.hpp b/src/ui/layouts/scroll_layout.hpp index e2ae2303..d2a36f2f 100644 --- a/src/ui/layouts/scroll_layout.hpp +++ b/src/ui/layouts/scroll_layout.hpp @@ -1,12 +1,12 @@ #pragma once +#include + #include "focus_layout.hpp" #include "graphics/rect.hpp" #include "graphics/renderer.hpp" #include "graphics/texture.hpp" -#include "helper/optional.hpp" -#include "helper/types.hpp" #include @@ -44,7 +44,7 @@ namespace ui { struct ScrollLayout : public FocusLayout { private: Margin gap; - helper::optional m_texture; + std::optional m_texture; ServiceProvider* m_service_provider; shapes::URect main_rect; shapes::URect scrollbar_rect; diff --git a/src/ui/layouts/tile_layout.hpp b/src/ui/layouts/tile_layout.hpp index 1fa12c1f..da68fdcb 100644 --- a/src/ui/layouts/tile_layout.hpp +++ b/src/ui/layouts/tile_layout.hpp @@ -1,9 +1,9 @@ #pragma once +#include #include "focus_layout.hpp" #include "graphics/renderer.hpp" -#include "helper/types.hpp" #include "ui/focusable.hpp" #include "ui/hoverable.hpp" #include "ui/layouts/grid_layout.hpp" diff --git a/src/ui/widget.cpp b/src/ui/widget.cpp index b3db6555..672bc187 100644 --- a/src/ui/widget.cpp +++ b/src/ui/widget.cpp @@ -1,12 +1,13 @@ +#include + #include "widget.hpp" -#include "helper/utils.hpp" -[[nodiscard]] helper::optional ui::as_focusable(ui::Widget* const widget) { +[[nodiscard]] std::optional ui::as_focusable(ui::Widget* const widget) { return utils::is_child_class(widget); } -[[nodiscard]] helper::optional ui::as_hoverable(ui::Widget* const widget) { +[[nodiscard]] std::optional ui::as_hoverable(ui::Widget* const widget) { return utils::is_child_class(widget); } diff --git a/src/ui/widget.hpp b/src/ui/widget.hpp index 92cd6a6a..9dd33b9a 100644 --- a/src/ui/widget.hpp +++ b/src/ui/widget.hpp @@ -1,7 +1,7 @@ #pragma once -#include "helper/bool_wrapper.hpp" -#include "helper/optional.hpp" +#include + #include "manager/service_provider.hpp" #include "ui/focusable.hpp" #include "ui/hoverable.hpp" @@ -59,9 +59,9 @@ namespace ui { handle_event(const std::shared_ptr& input_manager, const SDL_Event& event) = 0; }; - [[nodiscard]] helper::optional as_focusable(Widget* widget); + [[nodiscard]] std::optional as_focusable(Widget* widget); - [[nodiscard]] helper::optional as_hoverable(Widget* widget); + [[nodiscard]] std::optional as_hoverable(Widget* widget); } // namespace ui diff --git a/subprojects/magic_enum.wrap b/subprojects/magic_enum.wrap index a1870879..cd0237cf 100644 --- a/subprojects/magic_enum.wrap +++ b/subprojects/magic_enum.wrap @@ -6,5 +6,7 @@ source_hash = 44ad80db5a72f5047e01d90e18315751d9ac90c0ab42cbea7a6f9ec66a4cd679 source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/magic_enum_0.9.5-1/magic_enum-v0.9.5.tar.gz wrapdb_version = 0.9.5-1 +diff_files = magic_enum-0.9.5_installed.diff + [provide] magic_enum = magic_enum_dep diff --git a/subprojects/nlohmann_json.wrap b/subprojects/nlohmann_json.wrap index bf5d7000..994dec25 100644 --- a/subprojects/nlohmann_json.wrap +++ b/subprojects/nlohmann_json.wrap @@ -6,5 +6,7 @@ source_filename = nlohmann_json-3.11.2.zip source_hash = e5c7a9f49a16814be27e4ed0ee900ecd0092bfb7dbfca65b5a421b774dccaaed wrapdb_version = 3.11.2-1 +diff_files = nlohmann_json-3.11.2_installable.diff + [provide] nlohmann_json = nlohmann_json_dep diff --git a/subprojects/packagefiles/discord_game_sdk/cpp/meson.build b/subprojects/packagefiles/discord_game_sdk/cpp/meson.build index 7bb508cd..af4886ba 100644 --- a/subprojects/packagefiles/discord_game_sdk/cpp/meson.build +++ b/subprojects/packagefiles/discord_game_sdk/cpp/meson.build @@ -1,4 +1,3 @@ - inc_dirs = include_directories('.') header_files = files( @@ -85,7 +84,6 @@ if c.get_id() == 'msvc' lib_name = 'discord_game_sdk.dll' endif - discord_lib_c = c.find_library( lib_name, dirs: [meson.project_source_root() / lib_dir], @@ -110,8 +108,7 @@ else objdump_result = run_command( objdump, - '-p', - meson.project_source_root() / lib_dir / dynamic_lib, + '-p', meson.project_source_root() / lib_dir / dynamic_lib, check: true, ).stdout().strip() @@ -123,11 +120,10 @@ else patchelf = find_program('patchelf') - #TODO: file issue to discord, to set SONAME + #TODO: file issue to discord, to set SONAME run_command( patchelf, - '--set-soname', - dynamic_lib_rename, + '--set-soname', dynamic_lib_rename, meson.project_source_root() / lib_dir / dynamic_lib, check: true, ) @@ -150,7 +146,6 @@ else endif - discord_lib = library( 'discord-game-sdk', src_files, @@ -165,4 +160,3 @@ discord_dep = declare_dependency( version: meson.project_version(), link_with: discord_lib, ) - diff --git a/subprojects/packagefiles/discord_game_sdk/examples/cpp/meson.build b/subprojects/packagefiles/discord_game_sdk/examples/cpp/meson.build index 2e6d27a4..f23b51ff 100644 --- a/subprojects/packagefiles/discord_game_sdk/examples/cpp/meson.build +++ b/subprojects/packagefiles/discord_game_sdk/examples/cpp/meson.build @@ -1,5 +1,3 @@ - - executable( 'main', files('main.cpp'), diff --git a/subprojects/packagefiles/discord_game_sdk/meson_options.txt b/subprojects/packagefiles/discord_game_sdk/meson_options.txt index 847cd6dc..fff7c896 100644 --- a/subprojects/packagefiles/discord_game_sdk/meson_options.txt +++ b/subprojects/packagefiles/discord_game_sdk/meson_options.txt @@ -1,5 +1,3 @@ - - option( 'examples', type: 'boolean', diff --git a/subprojects/packagefiles/expected-1.1.0_installable.diff b/subprojects/packagefiles/expected-1.1.0_installable.diff new file mode 100644 index 00000000..20cf408b --- /dev/null +++ b/subprojects/packagefiles/expected-1.1.0_installable.diff @@ -0,0 +1,14 @@ +diff --git a/meson.build b/meson.build +index cf5efb7..4882184 100644 +--- a/meson.build ++++ b/meson.build +@@ -12,3 +12,9 @@ tl_expected_dep = declare_dependency( + if meson.version().version_compare('>=0.54.0') + meson.override_dependency('tl-expected', tl_expected_dep) + endif ++ ++ ++install_headers( ++ files('include/tl/expected.hpp'), ++ subdir: 'tl', ++) diff --git a/subprojects/packagefiles/magic_enum-0.9.5_installed.diff b/subprojects/packagefiles/magic_enum-0.9.5_installed.diff new file mode 100644 index 00000000..c695f85e --- /dev/null +++ b/subprojects/packagefiles/magic_enum-0.9.5_installed.diff @@ -0,0 +1,32 @@ +diff --git a/meson.build b/meson.build +index c231c7f..66f96ac 100644 +--- a/meson.build ++++ b/meson.build +@@ -4,7 +4,7 @@ project( + version: '0.9.5', + ) + +-magic_enum_include = include_directories('include/magic_enum') ++magic_enum_include = include_directories('include') + + magic_enum_args = [] + +@@ -20,3 +20,18 @@ magic_enum_dep = declare_dependency( + if get_option('test') + subdir('test') + endif ++ ++install_headers( ++ files( ++ 'include/magic_enum/magic_enum.hpp', ++ 'include/magic_enum/magic_enum_all.hpp', ++ 'include/magic_enum/magic_enum_containers.hpp', ++ 'include/magic_enum/magic_enum_flags.hpp', ++ 'include/magic_enum/magic_enum_format.hpp', ++ 'include/magic_enum/magic_enum_fuse.hpp', ++ 'include/magic_enum/magic_enum_iostream.hpp', ++ 'include/magic_enum/magic_enum_switch.hpp', ++ 'include/magic_enum/magic_enum_utility.hpp', ++ ), ++ subdir: 'magic_enum', ++) diff --git a/subprojects/packagefiles/nlohmann_json-3.11.2_installable.diff b/subprojects/packagefiles/nlohmann_json-3.11.2_installable.diff new file mode 100644 index 00000000..8ab3d7f1 --- /dev/null +++ b/subprojects/packagefiles/nlohmann_json-3.11.2_installable.diff @@ -0,0 +1,20 @@ +diff --git a/meson.build b/meson.build +index e2fb86f..4ddae11 100644 +--- a/meson.build ++++ b/meson.build +@@ -12,13 +12,12 @@ nlohmann_json_multiple_headers = declare_dependency( + include_directories: include_directories('include') + ) + +-if not meson.is_subproject() ++ + install_headers('single_include/nlohmann/json.hpp', subdir: 'nlohmann') +-install_headers('single_include/nlohmann/json_fwd.hpp', subdir: 'nlohmann') + + pkgc = import('pkgconfig') + pkgc.generate(name: 'nlohmann_json', + version: meson.project_version(), + description: 'JSON for Modern C++' + ) +-endif ++ diff --git a/subprojects/tl-expected.wrap b/subprojects/tl-expected.wrap index 9559e649..ee1dd8a5 100644 --- a/subprojects/tl-expected.wrap +++ b/subprojects/tl-expected.wrap @@ -9,5 +9,7 @@ patch_hash = 8b1aa60a1ddd604494628f0c6b0ed569b9e1e2dc17dfdc09d5de5563ae8adfe3 source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/tl-expected_1.1.0-1/expected-1.1.0.tar.gz wrapdb_version = 1.1.0-1 +diff_files = expected-1.1.0_installable.diff + [provide] tl-expected = tl_expected_dep diff --git a/tests/core/color.cpp b/tests/core/color.cpp index 4f274ba5..38442a7f 100644 --- a/tests/core/color.cpp +++ b/tests/core/color.cpp @@ -1,6 +1,7 @@ -#include "helper/color.hpp" -#include "helper/magic_enum_wrapper.hpp" +#include +#include + #include "utils/helper.hpp" #include diff --git a/tests/core/meson.build b/tests/core/meson.build index 1964cd9d..3e51508e 100644 --- a/tests/core/meson.build +++ b/tests/core/meson.build @@ -1,3 +1 @@ - - core_test_src += files('color.cpp') diff --git a/tests/graphics/meson.build b/tests/graphics/meson.build index 50a19049..c1538b2d 100644 --- a/tests/graphics/meson.build +++ b/tests/graphics/meson.build @@ -1,3 +1 @@ - - graphics_test_src += files('sdl_key.cpp') diff --git a/tests/graphics/sdl_key.cpp b/tests/graphics/sdl_key.cpp index 5d9e2377..91d29d7f 100644 --- a/tests/graphics/sdl_key.cpp +++ b/tests/graphics/sdl_key.cpp @@ -1,6 +1,7 @@ +#include + #include "manager/sdl_key.hpp" -#include "helper/expected.hpp" #include "utils/helper.hpp" #include diff --git a/tests/utils/printer.hpp b/tests/utils/printer.hpp index 216a57f9..092f4e6d 100644 --- a/tests/utils/printer.hpp +++ b/tests/utils/printer.hpp @@ -2,8 +2,8 @@ #pragma once -#include "helper/expected.hpp" -#include "helper/optional.hpp" +#include + #include @@ -42,7 +42,7 @@ namespace #endif { - // make helper::optional printable + // make std::optional printable template void PrintTo(const optional& value, std::ostream* os) { //NOLINT(cert-dcl58-cpp) if (value.has_value()) { diff --git a/tools/dependencies/meson.build b/tools/dependencies/meson.build index fa88ca90..4e702877 100644 --- a/tools/dependencies/meson.build +++ b/tools/dependencies/meson.build @@ -1,4 +1,5 @@ only_allow_native_libs = false + if meson.is_cross_build() if host_machine.system() == 'switch' or host_machine.system() == '3ds' # we do not link to code that was compiled with gcc 10.1 / gcc 7.1, the code we link with is all compiled with gcc 13.2 @@ -26,92 +27,6 @@ if meson.is_cross_build() endif endif -sdl2_dep = dependency( - 'sdl2', - 'SDL2', - allow_fallback: false, - required: only_allow_native_libs, - version: '>=2.26.0', -) - -if sdl2_dep.found() - graphics_lib += { - 'deps': [graphics_lib.get('deps'), sdl2_dep], - } -else - sdl2_dep = dependency( - 'sdl2', - required: true, - default_options: {'test': false}, - version: '>=2.24.0', - ) - sdl2main_dep = dependency( - 'sdl2main', - required: true, - fallback: 'sdl2', - version: '>=2.24.0', - ) - - graphic_application_deps += sdl2main_dep - - graphics_lib += { - 'deps': [graphics_lib.get('deps'), sdl2_dep], - } -endif - -sdl2_ttf_dep = dependency( - 'sdl2_ttf', - 'SDL2_ttf', - allow_fallback: not only_allow_native_libs, - required: true, -) -graphics_lib += { - 'deps': [graphics_lib.get('deps'), sdl2_ttf_dep], -} - -sdl2_image_dep = dependency( - 'sdl2_image', - 'SDL2_image', - allow_fallback: not only_allow_native_libs, - required: true, -) -graphics_lib += { - 'deps': [graphics_lib.get('deps'), sdl2_image_dep], -} - -# a dirty thing atm, until mpg123 is ported to meson (maybe never...) -mpg123_dep = dependency( - 'mpg123', - allow_fallback: true, - required: false, -) -sdl2_mixer_flags = {'flac': 'enabled'} -sdl2_mixer_defines = ['-DAUDIO_WITH_FLAC_SUPPORT'] -if mpg123_dep.found() - sdl2_mixer_flags += {'mpg123': 'enabled'} - sdl2_mixer_defines += '-DAUDIO_WITH_MP3_SUPPORT' -else - mpg123_dep = cpp.find_library('mpg123', required: false) - if mpg123_dep.found() - sdl2_mixer_flags += {'mpg123': 'enabled'} - sdl2_mixer_defines += '-DAUDIO_WITH_MP3_SUPPORT' - - meson.override_dependency('mpg123', mpg123_dep) - endif -endif - -sdl2_mixer_dep = dependency( - 'sdl2_mixer', - 'SDL2_mixer', - allow_fallback: not only_allow_native_libs, - required: true, - default_options: sdl2_mixer_flags, -) -graphics_lib += { - 'deps': [graphics_lib.get('deps'), sdl2_mixer_dep], - 'compile_args': [graphics_lib.get('compile_args'), sdl2_mixer_defines], -} - fmt_use_header_only = false if ( @@ -119,9 +34,6 @@ if ( and (host_machine.system() == 'switch' or host_machine.system() == '3ds') ) fmt_use_header_only = true - # clang with libc++ creates some really long and confusing linker errors, so just use the header only library -elif cpp.get_id() == 'clang' and build_with_libcpp - fmt_use_header_only = true endif if fmt_use_header_only @@ -138,21 +50,6 @@ endif core_lib += {'deps': [core_lib.get('deps'), fmt_dep]} -spdlog_dep = dependency( - 'spdlog', - required: true, - default_options: {'tests': 'disabled'}, -) -graphics_lib += { - 'deps': [graphics_lib.get('deps'), spdlog_dep], -} - -if (meson.is_cross_build() and host_machine.system() == '3ds') - graphics_lib += { - 'compile_args': [graphics_lib.get('compile_args'), '-DSPDLOG_NO_TLS'], - } -endif - nlohmann_json_dep = dependency( 'nlohmann_json', required: true, @@ -172,6 +69,10 @@ int main() { ) if not have_std_expected + if not allow_tl_expected_fallback + error('Compiler doesn\'t support std::expected') + endif + message('Compiler doesn\'t support std::expected, using fallback') tl_exp_dep = dependency('tl-expected', required: true) @@ -179,8 +80,6 @@ if not have_std_expected 'compile_args': [core_lib.get('compile_args'), '-D_USE_TL_EXPECTED'], 'deps': [core_lib.get('deps'), tl_exp_dep], } -else - message('Compiler support std::expected, using that') endif # check std::optional support @@ -196,14 +95,7 @@ int main() { ) if not have_std_optional - message('Compiler doesn\'t support std::optional, using fallback') - tl_opt_dep = dependency('tl-optional', required: true) - core_lib += { - 'compile_args': [core_lib.get('compile_args'), '-D_USE_TL_OPTIONAL'], - 'deps': [core_lib.get('deps'), tl_opt_dep], - } -else - message('Compiler support std::optional, using that') + error('Compiler doesn\'t support std::optional') endif magic_enum_dep = dependency( @@ -213,128 +105,240 @@ magic_enum_dep = dependency( ) core_lib += {'deps': [core_lib.get('deps'), magic_enum_dep]} -argparse_dep = dependency('argparse', required: true) -core_lib += {'deps': [core_lib.get('deps'), argparse_dep]} +utf8cpp_dep = dependency( + 'utf8cpp', + required: true, + version: '>=4.0.0', +) +core_lib += {'deps': [core_lib.get('deps'), utf8cpp_dep]} + +is_flatpak_build = false -online_multiplayer_supported = true +if build_application -if ( - meson.is_cross_build() - and (host_machine.system() == 'switch' or host_machine.system() == '3ds') -) - online_multiplayer_supported = false + graphic_application_deps = [] + recordings_application_deps = [] - # TODO: use libcurl and - # https://github.com/uctakeoff/uc-curl - # or https://github.com/JosephP91/curlcpp + sdl2_dep = dependency( + 'sdl2', + 'SDL2', + allow_fallback: false, + required: only_allow_native_libs, + version: '>=2.26.0', + ) - core_lib += { - 'compile_args': [ - core_lib.get('compile_args'), - '-D_ONLINE_MULTIPLAYER_NOT_SUPPORTED', - ], + if sdl2_dep.found() + graphics_lib += { + 'deps': [graphics_lib.get('deps'), sdl2_dep], + } + else + sdl2_dep = dependency( + 'sdl2', + required: true, + default_options: {'test': false}, + version: '>=2.24.0', + ) + sdl2main_dep = dependency( + 'sdl2main', + required: true, + fallback: 'sdl2', + version: '>=2.24.0', + ) + + graphic_application_deps += sdl2main_dep + + graphics_lib += { + 'deps': [graphics_lib.get('deps'), sdl2_dep], + } + endif + + sdl2_ttf_dep = dependency( + 'sdl2_ttf', + 'SDL2_ttf', + allow_fallback: not only_allow_native_libs, + required: true, + ) + graphics_lib += { + 'deps': [graphics_lib.get('deps'), sdl2_ttf_dep], } -else - cpp_httlib_dep = dependency( - 'cpp-httplib', + + sdl2_image_dep = dependency( + 'sdl2_image', + 'SDL2_image', + allow_fallback: not only_allow_native_libs, required: true, - default_options: { - 'cpp-httplib_openssl': 'enabled', - 'cpp-httplib_zlib': 'enabled', - }, ) - core_lib += {'deps': [core_lib.get('deps'), cpp_httlib_dep]} -endif + graphics_lib += { + 'deps': [graphics_lib.get('deps'), sdl2_image_dep], + } -utf8cpp_dep = dependency( - 'utf8cpp', - required: true, - version: '>=4.0.0', -) -core_lib += {'deps': [core_lib.get('deps'), utf8cpp_dep]} + # a dirty thing atm, until mpg123 is ported to meson (maybe never...) + mpg123_dep = dependency( + 'mpg123', + allow_fallback: true, + required: false, + ) + sdl2_mixer_flags = {'flac': 'enabled'} + sdl2_mixer_defines = ['-DAUDIO_WITH_FLAC_SUPPORT'] + if mpg123_dep.found() + sdl2_mixer_flags += {'mpg123': 'enabled'} + sdl2_mixer_defines += '-DAUDIO_WITH_MP3_SUPPORT' + else + mpg123_dep = cpp.find_library('mpg123', required: false) + if mpg123_dep.found() + sdl2_mixer_flags += {'mpg123': 'enabled'} + sdl2_mixer_defines += '-DAUDIO_WITH_MP3_SUPPORT' -build_installer = get_option('build_installer') + meson.override_dependency('mpg123', mpg123_dep) + endif + endif -is_flatpak_build = false + sdl2_mixer_dep = dependency( + 'sdl2_mixer', + 'SDL2_mixer', + allow_fallback: not only_allow_native_libs, + required: true, + default_options: sdl2_mixer_flags, + ) + graphics_lib += { + 'deps': [graphics_lib.get('deps'), sdl2_mixer_dep], + 'compile_args': [graphics_lib.get('compile_args'), sdl2_mixer_defines], + } + + spdlog_dep = dependency( + 'spdlog', + required: true, + default_options: {'tests': 'disabled'}, + ) + graphics_lib += { + 'deps': [graphics_lib.get('deps'), spdlog_dep], + } + + if (meson.is_cross_build() and host_machine.system() == '3ds') + graphics_lib += { + 'compile_args': [graphics_lib.get('compile_args'), '-DSPDLOG_NO_TLS'], + } + endif + + online_multiplayer_supported = true -# some sanity checks for the installer -if build_installer - if get_option('buildtype') != 'release' - error( - 'buildtype needs to be \'release\', when building the installer, but was: ' - + get_option('buildtype'), + if ( + meson.is_cross_build() + and ( + host_machine.system() == 'switch' + or host_machine.system() == '3ds' ) + ) + online_multiplayer_supported = false + + # TODO: use libcurl and + # https://github.com/uctakeoff/uc-curl + # or https://github.com/JosephP91/curlcpp + + graphics_lib += { + 'compile_args': [ + graphics_lib.get('compile_args'), + '-D_ONLINE_MULTIPLAYER_NOT_SUPPORTED', + ], + } + else + cpp_httlib_dep = dependency( + 'cpp-httplib', + required: true, + default_options: { + 'cpp-httplib_openssl': 'enabled', + 'cpp-httplib_zlib': 'enabled', + }, + ) + graphics_lib += {'deps': [graphics_lib.get('deps'), cpp_httlib_dep]} endif - if host_machine.system() == 'linux' - if get_option('prefix') == '/app' - is_flatpak_build = true + build_installer = get_option('build_installer') + + is_flatpak_build = false + + # some sanity checks for the installer + if build_installer + if get_option('buildtype') != 'release' + error( + 'buildtype needs to be \'release\', when building the installer, but was: ' + + get_option('buildtype'), + ) + endif + + if host_machine.system() == 'linux' + if get_option('prefix') == '/app' + is_flatpak_build = true + else + error( + 'only support flatpak builds, when building the installer for linux', + ) + endif + elif host_machine.system() == 'windows' + message('Adding a windows installer target: \'windows_installer\'') else + # TODO: create a proper installer for macOS : https://mesonbuild.com/Creating-OSX-packages.html error( - 'only support flatpak builds, when building the installer for linux', + 'unsuported system for building the installer: ' + + host_machine.system(), ) + endif - elif host_machine.system() == 'windows' - message('Adding a windows installer target: \'windows_installer\'') - else - error( - 'unsuported system for building the installer: ' - + host_machine.system(), - ) + + core_lib += { + 'compile_args': [ + core_lib.get('compile_args'), + '-DBUILD_INSTALLER', + ], + } endif - core_lib += { - 'compile_args': [ - core_lib.get('compile_args'), - '-DBUILD_INSTALLER', - ], - } + if is_flatpak_build + app_name = 'com.github.mgerhold.OOPetris' + core_lib += { + 'compile_args': [core_lib.get('compile_args'), '-DFLATPAK_BUILD'], + } + endif -endif + have_file_dialogs = false + have_discord_sdk = false -if is_flatpak_build - app_name = 'com.github.mgerhold.OOPetris' - core_lib += { - 'compile_args': [ - core_lib.get('compile_args'), - '-DFLATPAK_BUILD' - ], - } -endif + nfde_dep = dependency( + 'nativefiledialog-extended', + required: not meson.is_cross_build(), + default_options: { + 'xdg-desktop-portal': is_flatpak_build ? 'enabled' : 'auto', + }, + ) + if nfde_dep.found() + have_file_dialogs = true + graphics_lib += { + 'compile_args': [ + graphics_lib.get('compile_args'), + '-D_HAVE_FILE_DIALOGS', + ], + 'deps': [graphics_lib.get('deps'), nfde_dep], + } + endif -have_file_dialogs = false -have_discord_sdk = false + discord_sdk_dep = dependency( + 'discord-game-sdk', + required: not meson.is_cross_build(), + ) + if discord_sdk_dep.found() + have_discord_sdk = true + graphics_lib += { + 'compile_args': [ + graphics_lib.get('compile_args'), + '-D_HAVE_DISCORD_SDK', + ], + 'deps': [graphics_lib.get('deps'), discord_sdk_dep], + } + endif -nfde_dep = dependency( - 'nativefiledialog-extended', - required: not meson.is_cross_build(), - default_options: { - 'xdg-desktop-portal': is_flatpak_build ? 'enabled' : 'auto', - }, -) -if nfde_dep.found() - have_file_dialogs = true - graphics_lib += { - 'compile_args': [ - graphics_lib.get('compile_args'), - '-D_HAVE_FILE_DIALOGS', - ], - 'deps': [graphics_lib.get('deps'), nfde_dep], - } -endif + argparse_dep = dependency('argparse', required: true) -discord_sdk_dep = dependency( - 'discord-game-sdk', - required: not meson.is_cross_build(), -) -if discord_sdk_dep.found() - have_discord_sdk = true - graphics_lib += { - 'compile_args': [ - graphics_lib.get('compile_args'), - '-D_HAVE_DISCORD_SDK', - ], - 'deps': [graphics_lib.get('deps'), discord_sdk_dep], - } + graphic_application_deps += argparse_dep + recordings_application_deps += argparse_dep endif diff --git a/tools/install/meson.build b/tools/install/meson.build index 5c99cdd4..69d8f1b3 100644 --- a/tools/install/meson.build +++ b/tools/install/meson.build @@ -1,87 +1,87 @@ +if build_application + + ## TODO: only install needed ones, since sometimes we only need e.g. flacs or mp3 and no icons etc. + ## install assets + install_subdir( + meson.project_source_root() / 'assets', + install_dir: 'share/oopetris', + exclude_files: ['oopetris.desktop.in', 'OOPetris.svg', 'recordings.magic'], + exclude_directories: ['icon'], + ) -## TODO: only install needed ones, since sometimes we only need e.g. flacs or mp3 and no icons etc. -## install assets -install_subdir( - meson.project_source_root() / 'assets', - install_dir: 'share/oopetris', - exclude_files: ['oopetris.desktop.in', 'OOPetris.svg', 'recordings.magic'], - exclude_directories: ['icon'], -) - -app_name = 'oopetris' -if is_flatpak_build - app_name = 'com.github.mgerhold.OOPetris' -endif + app_name = 'oopetris' + if is_flatpak_build + app_name = 'com.github.mgerhold.OOPetris' + endif -conf = configuration_data() -conf.set('APP_NAME', app_name) + conf = configuration_data() + conf.set('APP_NAME', app_name) -datadir = get_option('prefix') / get_option('datadir') + datadir = get_option('prefix') / get_option('datadir') + if host_machine.system() == 'linux' -if host_machine.system() == 'linux' + fs = import('fs') - fs = import('fs') + magic_dir = datadir / 'misc' + magic_file = magic_dir / 'magic' + oopetris_magic_file = (meson.project_source_root() / 'assets' / 'recordings.magic') - magic_dir = datadir / 'misc' - magic_file = magic_dir / 'magic' - oopetris_magic_file = ( - meson.project_source_root() / 'assets' / 'recordings.magic' - ) + if fs.exists(magic_file) - if fs.exists(magic_file) + cat_prog = find_program('cat') - cat_prog = find_program('cat') + custom_target( + 'magic_file_append', + command: [cat_prog, '@INPUT@'], + capture: true, + input: [oopetris_magic_file, magic_file], + output: 'magic', + install_dir: magic_dir, + ) - custom_target( - 'magic_file_append', - command: [cat_prog, '@INPUT@'], - capture: true, - input: [oopetris_magic_file, magic_file], - output: 'magic', - install_dir: magic_dir, - ) + else + + install_data( + oopetris_magic_file, + install_dir: magic_dir, + rename: ['magic'], + ) - else + endif + endif + + configure_file( + input: meson.project_source_root() / 'assets/oopetris.desktop.in', + output: app_name + '.desktop', + configuration: conf, + install: true, + install_dir: datadir / 'applications', + ) + logos = [ + '24x24.png', + '48x48.png', + '64x64.png', + '72x72.png', + '96x96.png', + '128x128.png', + '144x144.png', + '160x160.png', + '192x192.png', + '256x256.png', + '512x512.png', + 'scalable.svg', + ] + + foreach logo : logos + name = logo.split('.')[0] + ext = logo.split('.')[1] install_data( - oopetris_magic_file, - install_dir: magic_dir, - rename: ['magic'], + meson.project_source_root() / 'assets' / 'icon' / logo, + install_dir: datadir / 'icons' / 'hicolor' / name / 'apps', + rename: [app_name + '.' + ext], ) + endforeach - endif endif - -configure_file( - input: meson.project_source_root() / 'assets/oopetris.desktop.in', - output: app_name + '.desktop', - configuration: conf, - install: true, - install_dir: datadir / 'applications', -) - -logos = [ - '24x24.png', - '48x48.png', - '64x64.png', - '72x72.png', - '96x96.png', - '128x128.png', - '144x144.png', - '160x160.png', - '192x192.png', - '256x256.png', - '512x512.png', - 'scalable.svg', -] - -foreach logo : logos - name = logo.split('.')[0] - ext = logo.split('.')[1] - install_data( - meson.project_source_root() / 'assets' / 'icon' / logo, - install_dir: datadir / 'icons' / 'hicolor' / name / 'apps', - rename: [app_name + '.' + ext], - ) -endforeach diff --git a/tools/installer/setup.nsi b/tools/installer/setup.nsi index 279a4c04..88d15ace 100644 --- a/tools/installer/setup.nsi +++ b/tools/installer/setup.nsi @@ -120,7 +120,7 @@ Section "Core App" CoreApp ; install executable SetOutPath "$INSTDIR" - File /a "${PROJECT_BUILD_DIR}\${APPFILE}" + File /a "${PROJECT_BUILD_DIR}\src\executables\${APPFILE}" ; install dynamic libraries SetOutPath "$INSTDIR" @@ -214,7 +214,7 @@ SectionEnd Section "Additional Binaries" AdditionalBinaries ; install helper binaries SetOutPath "$INSTDIR\bin" - File /a "${PROJECT_BUILD_DIR}\oopetris_recordings_utility.exe" + File /a "${PROJECT_BUILD_DIR}\src\executables\oopetris_recordings_utility.exe" ; add the bin dir to the path EnVar::SetHKCU diff --git a/tools/options/meson.build b/tools/options/meson.build index a7e1a144..77687208 100644 --- a/tools/options/meson.build +++ b/tools/options/meson.build @@ -1,35 +1,36 @@ +oopetris_author = 'Coder2k' +oopetris_name = 'OOPetris' + core_lib = { - 'src_files': [], 'inc_dirs': [], 'compile_args': [ '-DOOPETRIS_VERSION=' + meson.project_version(), - '-DOOPETRIS_NAME=' + oopetris_name, - '-DOOPETRIS_AUTHOR=' + oopetris_author, ], 'deps': [], } recordings_lib = { - 'src_files': [], 'inc_dirs': [], 'compile_args': [], 'deps': [], } graphics_lib = { - 'src_files': [], 'inc_dirs': [], - 'compile_args': [], + 'compile_args': [ + '-DOOPETRIS_VERSION=' + meson.project_version(), + '-DOOPETRIS_NAME=' + oopetris_name, + '-DOOPETRIS_AUTHOR=' + oopetris_author, + ], 'deps': [], } -global_deps = [] -graphic_application_deps = [] - cpp = meson.get_compiler('cpp') build_with_libcpp = false +allow_tl_expected_fallback = false + if cpp.get_id() == 'gcc' add_project_arguments('-Wold-style-cast', language: ['cpp']) elif cpp.get_id() == 'clang' @@ -55,9 +56,39 @@ elif cpp.get_id() == 'clang' endif if build_with_libcpp - add_global_link_arguments('-stdlib=libc++', language: ['cpp']) - add_global_arguments('-stdlib=libc++', language: ['cpp']) - global_deps += [cpp.find_library('c++'), cpp.find_library('c++abi')] + + core_lib += { + 'compile_args': [core_lib.get('compile_args'), '-stdlib=libc++'], + 'deps': [ + core_lib.get('deps'), + cpp.find_library('c++'), + cpp.find_library('c++abi'), + ], + } + + if not meson.is_subproject() + add_global_link_arguments('-stdlib=libc++', language: ['cpp']) + add_global_arguments('-stdlib=libc++', language: ['cpp']) + else + add_project_link_arguments('-stdlib=libc++', language: ['cpp']) + add_project_arguments('-stdlib=libc++', language: ['cpp']) + + endif + + else + # TODO: once clang with libstdc++ (gcc c++ stdlib) supports std::expectedt, remove this special behaviour + allow_tl_expected_fallback = true endif endif + +build_application = true + +## only build, if we are at the root, not if this is used as subproject in e.g. wrap files +if meson.is_subproject() or get_option('only_build_libs') + build_application = false + + if get_option('build_installer') and get_option('only_build_libs') + error('Can\'t build installer when \'only_build_libs\' is enabled') + endif +endif diff --git a/wrapper/README.md b/wrapper/README.md new file mode 100644 index 00000000..5ca2d7bc --- /dev/null +++ b/wrapper/README.md @@ -0,0 +1,25 @@ +# OOPetris wrapper + +## What is this + +This are wrappers of some OOPEtris functionaility in other languages + +They currently wrap this: +- OOPetris Recordings + +Planned: +- OOPetris AI support + +## Languages + + +Current: +- Node.js + +Planned: +- Python +- Lua + +## Other + +For more information on how to get / build those, refer to the subfolders of the languages diff --git a/wrapper/javascript/.gitignore b/wrapper/javascript/.gitignore new file mode 100644 index 00000000..adcdd192 --- /dev/null +++ b/wrapper/javascript/.gitignore @@ -0,0 +1,8 @@ +/build/ + +node_modules/ + +package-lock.json + +/dist +/prebuilds/ diff --git a/wrapper/javascript/.npmignore b/wrapper/javascript/.npmignore new file mode 100644 index 00000000..daf03f64 --- /dev/null +++ b/wrapper/javascript/.npmignore @@ -0,0 +1,12 @@ +node_modules +lib/binding +build +tests +*.tgz +npm-debug.log +.npmignore +.gitignore +.helper +jest.config.ts +tsconfig.json +src/ts/ diff --git a/wrapper/javascript/.npmrc b/wrapper/javascript/.npmrc new file mode 100644 index 00000000..134dfd1e --- /dev/null +++ b/wrapper/javascript/.npmrc @@ -0,0 +1 @@ +registry=https://verdaccio.totto.lt/ diff --git a/wrapper/javascript/README.md b/wrapper/javascript/README.md new file mode 100644 index 00000000..29965db2 --- /dev/null +++ b/wrapper/javascript/README.md @@ -0,0 +1,23 @@ +# OOpetris Noe.js Wrapper + + +## What is this? + +This wraps oopetris functionality for Node.js + + + + + +## Platform support + +We officially support Linux, Windows and MacOS, if any error occurs regarding those platform, feel free to open an issue + +## How to obtain + +A prebuilt package is available and can be obtained, by using [Totto's private node package registry](https://verdaccio.totto.lt/) + + +## How to build it yourself + +This uses node-gyp to build the wrapper, you need to install `libboopetris_recordings` to be able to use this. diff --git a/wrapper/javascript/binding.gyp b/wrapper/javascript/binding.gyp new file mode 100644 index 00000000..39745b7b --- /dev/null +++ b/wrapper/javascript/binding.gyp @@ -0,0 +1,88 @@ +# type: ignore +{ + "targets": [ + { + "target_name": "oopetris", + "cflags_cc": [ + "-std=c++23", + "-Wall", + "-Wextra", + "-Wno-unused-parameter", + "-O3", + "-Werror", + "-Wpedantic", + "-fexceptions", + "-frtti", + "-Wno-cast-function-type", # since nan.h -> node.h has some warnings regarding that + " node.h has some warnings regarding that + " node.h has some warnings regarding that + ".a via some sed magic + ], + }, + }, + } + ], +} diff --git a/wrapper/javascript/jest.config.ts b/wrapper/javascript/jest.config.ts new file mode 100644 index 00000000..90a1038b --- /dev/null +++ b/wrapper/javascript/jest.config.ts @@ -0,0 +1,9 @@ +import type { JestConfigWithTsJest } from "ts-jest" + +const jestConfig: JestConfigWithTsJest = { + preset: "ts-jest", + testEnvironment: "node", + testTimeout: 5000, +} + +export default jestConfig diff --git a/wrapper/javascript/package.json b/wrapper/javascript/package.json new file mode 100644 index 00000000..70b52b1a --- /dev/null +++ b/wrapper/javascript/package.json @@ -0,0 +1,62 @@ +{ + "name": "oopetris", + "version": "1.0.1", + "description": "Node js wrapper for oopetris", + "gypfile": true, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist/", + "prebuilds/", + "src/cpp/", + "binding.gyp", + "package.json", + "README.md" + ], + "private": false, + "scripts": { + "install": "node-gyp-build", + "build": "npm run build:gyp && npm run compile", + "build:gyp": "prebuildify -t 18.20.3 -t 19.9.0 -t 20.13.1 -t 21.7.3 -t 22.2.0 --strip", + "build:debug": "prebuildify -t 18.20.3 -t 19.9.0 -t 20.13.1 -t 21.7.3 -t 22.2.0 --debug", + "compile": "npm run build:tsc", + "build:tsc": "tsc", + "test": "npx jest", + "build:test": "npm run build && npm run test", + "publish:package": "npm run build:test && npm publish --tag latest --access public" + }, + "keywords": [ + "oopetris", + "cpp", + "node-gyp" + ], + "author": { + "name": "Totto16", + "url": "https://github.com/Totto16" + }, + "license": "MIT", + "engines": { + "node": "^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0" + }, + "os": [ + "darwin", + "linux", + "win32" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/OpenBrickProtocolFoundation/oopetris.git" + }, + "dependencies": { + "node-gyp-build": "^4.8.1" + }, + "devDependencies": { + "@types/jest": "^29.5.12", + "jest": "^29.7.0", + "nan": "^2.19.0", + "prebuildify": "^6.0.2", + "ts-jest": "^29.1.3", + "ts-node": "^10.9.2", + "typescript": "^5.4.5" + } +} diff --git a/wrapper/javascript/src/cpp/wrapper.cpp b/wrapper/javascript/src/cpp/wrapper.cpp new file mode 100644 index 00000000..2e29ed87 --- /dev/null +++ b/wrapper/javascript/src/cpp/wrapper.cpp @@ -0,0 +1,148 @@ +#ifdef _WIN32 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#endif + +#if defined(__GNUC__) & !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtemplate-id-cdtor" +#endif + +#include + +#if defined(__GNUC__) & !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + +#include +#include + +NAN_METHOD(isRecordingFile) { + + if (info.Length() != 1) { + info.GetIsolate()->ThrowException(Nan::TypeError("Wrong number of arguments")); + return; + } + + if (!info[0]->IsString()) { + info.GetIsolate()->ThrowException(Nan::TypeError("First argument must be string")); + return; + } + + auto filePath = std::string{ *Nan::Utf8String(info[0]) }; + + if (not std::filesystem::exists(filePath)) { + + info.GetReturnValue().Set(Nan::False()); + return; + } + + auto parsed = recorder::RecordingReader::from_path(filePath); + + if (not parsed.has_value()) { + info.GetReturnValue().Set(Nan::False()); + return; + } + + info.GetReturnValue().Set(Nan::True()); + return; +} + +NAN_METHOD(getInformation) { + + if (info.Length() != 1) { + info.GetIsolate()->ThrowException(Nan::TypeError("Wrong number of arguments")); + return; + } + + if (!info[0]->IsString()) { + info.GetIsolate()->ThrowException(Nan::TypeError("First argument must be string")); + return; + } + + auto filePath = std::string{ *Nan::Utf8String(info[0]) }; + + if (not std::filesystem::exists(filePath)) { + std::string error = "File '"; + error += filePath; + error += "' doesn't exist!"; + + info.GetIsolate()->ThrowException(Nan::Error(Nan::New(error).ToLocalChecked())); + return; + } + + auto parsed = recorder::RecordingReader::from_path(filePath); + + if (not parsed.has_value()) { + std::string error = "An error occurred during parsing of the recording file '"; + error += filePath; + error += "': "; + error += parsed.error(); + + info.GetIsolate()->ThrowException(Nan::Error(Nan::New(error).ToLocalChecked())); + return; + } + + const auto recording_reader = std::move(parsed.value()); + + auto json_value = json::try_convert_to_json(recording_reader); + + if (not json_value.has_value()) { + std::string error = "An error occurred during converting to json:"; + error += json_value.error(); + + info.GetIsolate()->ThrowException(Nan::Error(Nan::New(error).ToLocalChecked())); + return; + } + + const auto result_string_json = json_value->dump(-1, ' ', false); + + v8::Local js_json_string = Nan::New(result_string_json).ToLocalChecked(); + + Nan::JSON NanJSON; + Nan::MaybeLocal result = NanJSON.Parse(js_json_string); + if (!result.IsEmpty()) { + v8::Local val = result.ToLocalChecked(); + info.GetReturnValue().Set(val); + return; + } + + info.GetIsolate()->ThrowException(Nan::Error("Failed to parse internal JSON structure!")); + return; +} + +NAN_MODULE_INIT(InitAll) { + Nan::Set( + target, Nan::New("isRecordingFile").ToLocalChecked(), + Nan::GetFunction(Nan::New(isRecordingFile)).ToLocalChecked() + ); + + Nan::Set( + target, Nan::New("getInformation").ToLocalChecked(), + Nan::GetFunction(Nan::New(getInformation)).ToLocalChecked() + ); + + Nan::Set(target, Nan::New("version").ToLocalChecked(), Nan::New(utils::version()).ToLocalChecked()); + + v8::Local properties = Nan::New(); + + std::vector> properties_vector{ + { "height", grid::height_in_tiles }, + { "width", grid::width_in_tiles } + }; + + v8::Local grid_properties = Nan::New(); + + for (const auto& [key, value] : properties_vector) { + v8::Local keyValue = Nan::New(key).ToLocalChecked(); + Nan::Set(grid_properties, keyValue, Nan::New(value)).Check(); + } + + Nan::Set(properties, Nan::New("gridProperties").ToLocalChecked(), grid_properties).Check(); + + Nan::Set(target, Nan::New("properties").ToLocalChecked(), properties); +} + +NODE_MODULE(RecordingsWrapper, InitAll) diff --git a/wrapper/javascript/src/ts/index.ts b/wrapper/javascript/src/ts/index.ts new file mode 100644 index 00000000..008d6bf4 --- /dev/null +++ b/wrapper/javascript/src/ts/index.ts @@ -0,0 +1,108 @@ +import fs from "fs" +import path from "path" + +const rootDir = path.join(__dirname, "..", "..") +const oopetris = require("node-gyp-build")(rootDir) + +export type AdditionalInformation = Record + +export type InputEvent = + | "RotateLeftPressed" + | "RotateRightPressed" + | "MoveLeftPressed" + | "MoveRightPressed" + | "MoveDownPressed" + | "DropPressed" + | "HoldPressed" + | "RotateLeftReleased" + | "RotateRightReleased" + | "MoveLeftReleased" + | "MoveRightReleased" + | "MoveDownReleased" + | "DropReleased" + | "HoldReleased" + +export type TetrionRecord = { + event: InputEvent + simulation_step_index: number + tetrion_index: number +} + +export type MinoPosition = { + x: number + y: number +} + +export type TetrominoType = "I" | "J" | "L" | "O" | "S" | "T" | "Z" + +export type Mino = { + position: MinoPosition + type: TetrominoType +} + +export type TetrionSnapshot = { + level: number + lines_cleared: number + mino_stack: Mino[] + score: number + simulation_step_index: number + tetrion_index: number +} + +export type TetrionHeader = { + seed: number + starting_level: number +} + +export type RecordingInformation = { + information: AdditionalInformation + records: TetrionRecord[] + snapshots: TetrionSnapshot[] + tetrion_headers: TetrionHeader[] + version: number +} + +export type GridProperties = { + height: number + width: number +} + +export type RecordingsProperties = { + gridProperties: GridProperties +} + +export class RecordingsUtility { + static isRecordingFile(file: string): boolean { + try { + // this throws, when file is not an string or not there at all, just be safe for JS land + return oopetris.isRecordingFile(file) + } catch (_err) { + return false + } + } + + static getInformation(file: string): null | RecordingInformation { + if (!fs.existsSync(file)) { + return null + } + + try { + if (!RecordingsUtility.isRecordingFile(file)) { + return null + } + + // this throws, when file is not an string, not there at all, or some other error regarding serialization from c++ land to JS land occurs, just be safe for JS land + return oopetris.getInformation(file) + } catch (_err) { + return null + } + } + + static get properties(): GridProperties { + return oopetris.properties + } + + static get version(): string { + return oopetris.version + } +} diff --git a/wrapper/javascript/tests/files/correct.rec b/wrapper/javascript/tests/files/correct.rec new file mode 100644 index 00000000..4d1a9cea Binary files /dev/null and b/wrapper/javascript/tests/files/correct.rec differ diff --git a/wrapper/javascript/tests/files/correct.serialized.json b/wrapper/javascript/tests/files/correct.serialized.json new file mode 100644 index 00000000..a2253665 --- /dev/null +++ b/wrapper/javascript/tests/files/correct.serialized.json @@ -0,0 +1,6725 @@ +{ + "information": { + "date": 1710635321, + "mode": "single_player", + "platform": "pc" + }, + "records": [ + { + "event": "MoveRightPressed", + "simulation_step_index": 123, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 133, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 136, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 145, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 150, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 157, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 161, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 181, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 186, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 190, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 198, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 208, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 214, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 222, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 252, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 257, + "tetrion_index": 0 + }, + { + "event": "MoveDownPressed", + "simulation_step_index": 265, + "tetrion_index": 0 + }, + { + "event": "MoveDownReleased", + "simulation_step_index": 273, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 278, + "tetrion_index": 0 + }, + { + "event": "MoveDownPressed", + "simulation_step_index": 288, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 293, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 308, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 313, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 318, + "tetrion_index": 0 + }, + { + "event": "MoveDownReleased", + "simulation_step_index": 325, + "tetrion_index": 0 + }, + { + "event": "MoveDownPressed", + "simulation_step_index": 342, + "tetrion_index": 0 + }, + { + "event": "MoveDownReleased", + "simulation_step_index": 350, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 354, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 356, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 357, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 361, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 365, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 365, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 371, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 371, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 373, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 376, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 378, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 378, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 383, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 383, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 383, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 388, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 388, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 389, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 393, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 393, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 393, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 398, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 400, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 400, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 402, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 405, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 432, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 437, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 442, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 446, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 450, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 457, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 457, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 485, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 488, + "tetrion_index": 0 + }, + { + "event": "HoldPressed", + "simulation_step_index": 542, + "tetrion_index": 0 + }, + { + "event": "HoldReleased", + "simulation_step_index": 548, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 569, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 592, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 599, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 621, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 632, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 638, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 638, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 645, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 645, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 667, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 689, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 697, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 697, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 728, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 735, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 737, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 751, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 756, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 763, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 769, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 770, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 776, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 779, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 781, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 789, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 802, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 807, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 813, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 819, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 833, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 839, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 845, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 852, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 870, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 878, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 895, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 897, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 902, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 913, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 920, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 936, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 938, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 960, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 967, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 969, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 971, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 986, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 992, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 1000, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 1009, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 1044, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 1050, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 1068, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 1074, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 1081, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 1090, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 1112, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 1116, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 1155, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 1165, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 1179, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 1188, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 1214, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 1222, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 1229, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 1238, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 1242, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 1250, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 1264, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 1270, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 1281, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 1287, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 1305, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 1314, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 1322, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 1337, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 1345, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 1351, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 1364, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 1371, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 1378, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 1384, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 1393, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 1400, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 1415, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 1420, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 1425, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 1440, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 1443, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 1454, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 1461, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 1468, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 1473, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 1483, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 1487, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 1491, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 1496, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 1502, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 1508, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 1513, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 1518, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 1523, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 1529, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 1539, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 1547, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 1553, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 1558, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 1562, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 1567, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 1575, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 1590, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 1597, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 1599, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 1602, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 1615, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 1619, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 1625, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 1639, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 1647, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 1663, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 1664, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 1671, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 1690, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 1692, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 1695, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 1698, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 1707, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 1716, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 1723, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 1723, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 1728, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 1730, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 1748, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 1753, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 1779, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 1785, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 1799, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 1802, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 1804, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 1824, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 1826, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 1831, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 1839, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 1846, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 1858, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 1859, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 1885, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 1889, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 1903, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 1908, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 1924, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 1931, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 1935, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 1941, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 1962, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 1979, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 1985, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 1994, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 2005, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 2007, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 2026, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 2031, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 2036, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 2043, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 2050, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 2056, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 2075, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 2079, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 2081, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 2088, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 2107, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 2117, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 2124, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 2130, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 2141, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 2149, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 2161, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 2168, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 2174, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 2180, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 2185, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 2196, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 2201, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 2202, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 2203, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 2223, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 2240, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 2247, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 2261, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 2267, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 2271, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 2277, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 2292, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 2299, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 2308, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 2314, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 2334, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 2343, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 2353, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 2359, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 2361, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 2368, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 2376, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 2384, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 2391, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 2411, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 2419, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 2430, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 2431, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 2437, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 2463, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 2464, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 2469, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 2487, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 2508, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 2515, + "tetrion_index": 0 + }, + { + "event": "HoldPressed", + "simulation_step_index": 2609, + "tetrion_index": 0 + }, + { + "event": "HoldReleased", + "simulation_step_index": 2617, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 2630, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 2651, + "tetrion_index": 0 + }, + { + "event": "MoveDownPressed", + "simulation_step_index": 2667, + "tetrion_index": 0 + }, + { + "event": "MoveDownReleased", + "simulation_step_index": 2702, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 2702, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 2718, + "tetrion_index": 0 + }, + { + "event": "MoveDownPressed", + "simulation_step_index": 2731, + "tetrion_index": 0 + }, + { + "event": "MoveDownReleased", + "simulation_step_index": 2738, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 2786, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 2811, + "tetrion_index": 0 + }, + { + "event": "MoveDownPressed", + "simulation_step_index": 2823, + "tetrion_index": 0 + }, + { + "event": "MoveDownReleased", + "simulation_step_index": 2832, + "tetrion_index": 0 + }, + { + "event": "MoveDownPressed", + "simulation_step_index": 2840, + "tetrion_index": 0 + }, + { + "event": "MoveDownReleased", + "simulation_step_index": 2846, + "tetrion_index": 0 + }, + { + "event": "MoveDownPressed", + "simulation_step_index": 2853, + "tetrion_index": 0 + }, + { + "event": "MoveDownReleased", + "simulation_step_index": 2859, + "tetrion_index": 0 + }, + { + "event": "MoveDownPressed", + "simulation_step_index": 2864, + "tetrion_index": 0 + }, + { + "event": "MoveDownReleased", + "simulation_step_index": 2869, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 2876, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 2885, + "tetrion_index": 0 + }, + { + "event": "MoveDownPressed", + "simulation_step_index": 2896, + "tetrion_index": 0 + }, + { + "event": "MoveDownReleased", + "simulation_step_index": 2903, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 2955, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 2960, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 2977, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 2983, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 2996, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 3003, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 3013, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 3020, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 3039, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 3051, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 3057, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 3072, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 3078, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 3088, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 3102, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 3109, + "tetrion_index": 0 + }, + { + "event": "HoldPressed", + "simulation_step_index": 3120, + "tetrion_index": 0 + }, + { + "event": "HoldReleased", + "simulation_step_index": 3127, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 3134, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 3142, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 3151, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 3160, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 3162, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 3168, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 3184, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 3192, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 3201, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 3230, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 3232, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 3240, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 3251, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 3268, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 3272, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 3278, + "tetrion_index": 0 + }, + { + "event": "HoldPressed", + "simulation_step_index": 3284, + "tetrion_index": 0 + }, + { + "event": "HoldReleased", + "simulation_step_index": 3291, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 3306, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 3318, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 3325, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 3332, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 3336, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 3341, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 3356, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 3385, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 3385, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 3390, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 3405, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 3411, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 3415, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 3430, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 3460, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 3467, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 3474, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 3481, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 3509, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 3517, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 3531, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 3538, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 3546, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 3552, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 3556, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 3576, + "tetrion_index": 0 + }, + { + "event": "HoldPressed", + "simulation_step_index": 3595, + "tetrion_index": 0 + }, + { + "event": "HoldReleased", + "simulation_step_index": 3603, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 3608, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 3617, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 3625, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 3636, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 3638, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 3643, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 3652, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 3661, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 3680, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 3689, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 3698, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 3705, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 3710, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 3715, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 3722, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 3729, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 3731, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 3737, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 3751, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 3760, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 3763, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 3768, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 3784, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 3789, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 3823, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 3829, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 3845, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 3852, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 3918, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 3924, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 3948, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 3953, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 3965, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 3971, + "tetrion_index": 0 + }, + { + "event": "HoldPressed", + "simulation_step_index": 4036, + "tetrion_index": 0 + }, + { + "event": "HoldReleased", + "simulation_step_index": 4043, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 4069, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 4075, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 4079, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 4090, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 4094, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 4096, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 4103, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 4108, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 4124, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 4129, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 4138, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 4144, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 4160, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 4181, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 4198, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 4206, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 4216, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 4223, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 4227, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 4237, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 4241, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 4246, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 4251, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 4258, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 4266, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 4272, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 4279, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 4289, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 4291, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 4298, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 4350, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 4357, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 4372, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 4379, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 4402, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 4407, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 4418, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 4422, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 4433, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 4439, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 4450, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 4458, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 4465, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 4473, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 4473, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 4481, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 4489, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 4501, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 4502, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 4505, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 4540, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 4547, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 4553, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 4571, + "tetrion_index": 0 + }, + { + "event": "HoldPressed", + "simulation_step_index": 4578, + "tetrion_index": 0 + }, + { + "event": "HoldReleased", + "simulation_step_index": 4584, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 4593, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 4596, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 4604, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 4620, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 4629, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 4636, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 4642, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 4642, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 4665, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 4671, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 4673, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 4688, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 4708, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 4715, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 4729, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 4733, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 4740, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 4744, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 4753, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 4766, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 4774, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 4779, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 4780, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 4788, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 4804, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 4812, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 4820, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 4831, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 4839, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 4844, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 4855, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 4863, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 4870, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 4876, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 4885, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 4892, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 4900, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 4908, + "tetrion_index": 0 + }, + { + "event": "HoldPressed", + "simulation_step_index": 4916, + "tetrion_index": 0 + }, + { + "event": "HoldReleased", + "simulation_step_index": 4925, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 4949, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 4956, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 4960, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 4968, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 4975, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 4980, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 4982, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 4986, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 4988, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 4995, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 5012, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 5019, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 5025, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 5039, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 5043, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 5043, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 5048, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 5056, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 5063, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 5073, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 5073, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 5080, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 5104, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 5111, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 5116, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 5120, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 5132, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 5138, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 5148, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 5154, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 5159, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 5166, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 5168, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 5177, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 5185, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 5199, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 5202, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 5205, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 5227, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 5239, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 5251, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 5272, + "tetrion_index": 0 + }, + { + "event": "HoldPressed", + "simulation_step_index": 5282, + "tetrion_index": 0 + }, + { + "event": "HoldReleased", + "simulation_step_index": 5290, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 5296, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 5308, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 5309, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 5315, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 5322, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 5327, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 5336, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 5341, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 5353, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 5359, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 5383, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 5395, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 5395, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 5401, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 5414, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 5427, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 5429, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 5433, + "tetrion_index": 0 + }, + { + "event": "HoldPressed", + "simulation_step_index": 5456, + "tetrion_index": 0 + }, + { + "event": "HoldReleased", + "simulation_step_index": 5463, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 5475, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 5506, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 5506, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 5513, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 5530, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 5538, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 5547, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 5559, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 5566, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 5573, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 5592, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 5600, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 5616, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 5621, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 5631, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 5637, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 5650, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 5657, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 5683, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 5690, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 5698, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 5707, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 5721, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 5731, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 5760, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 5766, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 5769, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 5778, + "tetrion_index": 0 + }, + { + "event": "HoldPressed", + "simulation_step_index": 5801, + "tetrion_index": 0 + }, + { + "event": "HoldReleased", + "simulation_step_index": 5807, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 5812, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 5837, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 5842, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 5849, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 5857, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 5864, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 5867, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 5875, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 5889, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 5896, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 5902, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 5913, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 5944, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 5950, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 5956, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 5976, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 5982, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 6001, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 6023, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 6023, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 6031, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 6045, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 6045, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 6052, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 6062, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 6072, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 6077, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 6092, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 6093, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 6098, + "tetrion_index": 0 + }, + { + "event": "RotateRightPressed", + "simulation_step_index": 6133, + "tetrion_index": 0 + }, + { + "event": "RotateRightReleased", + "simulation_step_index": 6141, + "tetrion_index": 0 + }, + { + "event": "MoveLeftPressed", + "simulation_step_index": 6142, + "tetrion_index": 0 + }, + { + "event": "MoveLeftReleased", + "simulation_step_index": 6149, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 6162, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 6169, + "tetrion_index": 0 + }, + { + "event": "MoveRightPressed", + "simulation_step_index": 6182, + "tetrion_index": 0 + }, + { + "event": "MoveRightReleased", + "simulation_step_index": 6189, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 6212, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 6218, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 6223, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 6227, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 6233, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 6236, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 6242, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 6246, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 6252, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 6255, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 6261, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 6264, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 6268, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 6272, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 6277, + "tetrion_index": 0 + }, + { + "event": "DropReleased", + "simulation_step_index": 6281, + "tetrion_index": 0 + }, + { + "event": "DropPressed", + "simulation_step_index": 6287, + "tetrion_index": 0 + } + ], + "snapshots": [ + { + "level": 0, + "lines_cleared": 0, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" } + ], + "score": 56, + "simulation_step_index": 181, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 0, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" } + ], + "score": 156, + "simulation_step_index": 356, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 0, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" } + ], + "score": 224, + "simulation_step_index": 371, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 0, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" } + ], + "score": 288, + "simulation_step_index": 383, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 0, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" } + ], + "score": 344, + "simulation_step_index": 393, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 0, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" } + ], + "score": 416, + "simulation_step_index": 432, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 0, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" } + ], + "score": 468, + "simulation_step_index": 442, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 0, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 0, "y": 11 }, "type": "J" }, + { "position": { "x": 0, "y": 12 }, "type": "J" }, + { "position": { "x": 1, "y": 12 }, "type": "J" }, + { "position": { "x": 2, "y": 12 }, "type": "J" } + ], + "score": 512, + "simulation_step_index": 450, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 0, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 0, "y": 11 }, "type": "J" }, + { "position": { "x": 0, "y": 12 }, "type": "J" }, + { "position": { "x": 1, "y": 12 }, "type": "J" }, + { "position": { "x": 2, "y": 12 }, "type": "J" }, + { "position": { "x": 3, "y": 12 }, "type": "I" }, + { "position": { "x": 4, "y": 12 }, "type": "I" }, + { "position": { "x": 5, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 12 }, "type": "I" } + ], + "score": 556, + "simulation_step_index": 485, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 0, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 0, "y": 11 }, "type": "J" }, + { "position": { "x": 0, "y": 12 }, "type": "J" }, + { "position": { "x": 1, "y": 12 }, "type": "J" }, + { "position": { "x": 2, "y": 12 }, "type": "J" }, + { "position": { "x": 3, "y": 12 }, "type": "I" }, + { "position": { "x": 4, "y": 12 }, "type": "I" }, + { "position": { "x": 5, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 12 }, "type": "I" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" } + ], + "score": 612, + "simulation_step_index": 621, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 0, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 0, "y": 11 }, "type": "J" }, + { "position": { "x": 0, "y": 12 }, "type": "J" }, + { "position": { "x": 1, "y": 12 }, "type": "J" }, + { "position": { "x": 2, "y": 12 }, "type": "J" }, + { "position": { "x": 3, "y": 12 }, "type": "I" }, + { "position": { "x": 4, "y": 12 }, "type": "I" }, + { "position": { "x": 5, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 12 }, "type": "I" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" } + ], + "score": 664, + "simulation_step_index": 689, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 0, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 0, "y": 11 }, "type": "J" }, + { "position": { "x": 0, "y": 12 }, "type": "J" }, + { "position": { "x": 1, "y": 12 }, "type": "J" }, + { "position": { "x": 2, "y": 12 }, "type": "J" }, + { "position": { "x": 3, "y": 12 }, "type": "I" }, + { "position": { "x": 4, "y": 12 }, "type": "I" }, + { "position": { "x": 5, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 12 }, "type": "I" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 12 }, "type": "T" }, + { "position": { "x": 7, "y": 13 }, "type": "T" }, + { "position": { "x": 8, "y": 13 }, "type": "T" }, + { "position": { "x": 7, "y": 14 }, "type": "T" } + ], + "score": 708, + "simulation_step_index": 763, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 1, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 0, "y": 12 }, "type": "J" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 13 }, "type": "T" }, + { "position": { "x": 8, "y": 13 }, "type": "T" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 8, "y": 11 }, "type": "L" }, + { "position": { "x": 8, "y": 12 }, "type": "L" } + ], + "score": 780, + "simulation_step_index": 895, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 1, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 0, "y": 12 }, "type": "J" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 13 }, "type": "T" }, + { "position": { "x": 8, "y": 13 }, "type": "T" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 8, "y": 11 }, "type": "L" }, + { "position": { "x": 8, "y": 12 }, "type": "L" }, + { "position": { "x": 1, "y": 11 }, "type": "T" }, + { "position": { "x": 1, "y": 12 }, "type": "T" }, + { "position": { "x": 2, "y": 12 }, "type": "T" }, + { "position": { "x": 1, "y": 13 }, "type": "T" } + ], + "score": 804, + "simulation_step_index": 1155, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 1, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 0, "y": 12 }, "type": "J" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 13 }, "type": "T" }, + { "position": { "x": 8, "y": 13 }, "type": "T" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 8, "y": 11 }, "type": "L" }, + { "position": { "x": 8, "y": 12 }, "type": "L" }, + { "position": { "x": 1, "y": 11 }, "type": "T" }, + { "position": { "x": 1, "y": 12 }, "type": "T" }, + { "position": { "x": 2, "y": 12 }, "type": "T" }, + { "position": { "x": 1, "y": 13 }, "type": "T" }, + { "position": { "x": 4, "y": 13 }, "type": "Z" }, + { "position": { "x": 5, "y": 13 }, "type": "Z" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" } + ], + "score": 852, + "simulation_step_index": 1214, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 1, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 0, "y": 12 }, "type": "J" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 13 }, "type": "T" }, + { "position": { "x": 8, "y": 13 }, "type": "T" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 8, "y": 11 }, "type": "L" }, + { "position": { "x": 8, "y": 12 }, "type": "L" }, + { "position": { "x": 1, "y": 11 }, "type": "T" }, + { "position": { "x": 1, "y": 12 }, "type": "T" }, + { "position": { "x": 2, "y": 12 }, "type": "T" }, + { "position": { "x": 1, "y": 13 }, "type": "T" }, + { "position": { "x": 4, "y": 13 }, "type": "Z" }, + { "position": { "x": 5, "y": 13 }, "type": "Z" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 5, "y": 11 }, "type": "S" }, + { "position": { "x": 5, "y": 12 }, "type": "S" }, + { "position": { "x": 6, "y": 12 }, "type": "S" }, + { "position": { "x": 6, "y": 13 }, "type": "S" } + ], + "score": 892, + "simulation_step_index": 1264, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 1, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 0, "y": 12 }, "type": "J" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 13 }, "type": "T" }, + { "position": { "x": 8, "y": 13 }, "type": "T" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 8, "y": 11 }, "type": "L" }, + { "position": { "x": 8, "y": 12 }, "type": "L" }, + { "position": { "x": 1, "y": 11 }, "type": "T" }, + { "position": { "x": 1, "y": 12 }, "type": "T" }, + { "position": { "x": 2, "y": 12 }, "type": "T" }, + { "position": { "x": 1, "y": 13 }, "type": "T" }, + { "position": { "x": 4, "y": 13 }, "type": "Z" }, + { "position": { "x": 5, "y": 13 }, "type": "Z" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 5, "y": 11 }, "type": "S" }, + { "position": { "x": 5, "y": 12 }, "type": "S" }, + { "position": { "x": 6, "y": 12 }, "type": "S" }, + { "position": { "x": 6, "y": 13 }, "type": "S" }, + { "position": { "x": 8, "y": 10 }, "type": "J" }, + { "position": { "x": 7, "y": 10 }, "type": "J" }, + { "position": { "x": 7, "y": 11 }, "type": "J" }, + { "position": { "x": 7, "y": 12 }, "type": "J" } + ], + "score": 924, + "simulation_step_index": 1364, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 1, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 0, "y": 12 }, "type": "J" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 13 }, "type": "T" }, + { "position": { "x": 8, "y": 13 }, "type": "T" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 8, "y": 11 }, "type": "L" }, + { "position": { "x": 8, "y": 12 }, "type": "L" }, + { "position": { "x": 1, "y": 11 }, "type": "T" }, + { "position": { "x": 1, "y": 12 }, "type": "T" }, + { "position": { "x": 2, "y": 12 }, "type": "T" }, + { "position": { "x": 1, "y": 13 }, "type": "T" }, + { "position": { "x": 4, "y": 13 }, "type": "Z" }, + { "position": { "x": 5, "y": 13 }, "type": "Z" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 5, "y": 11 }, "type": "S" }, + { "position": { "x": 5, "y": 12 }, "type": "S" }, + { "position": { "x": 6, "y": 12 }, "type": "S" }, + { "position": { "x": 6, "y": 13 }, "type": "S" }, + { "position": { "x": 8, "y": 10 }, "type": "J" }, + { "position": { "x": 7, "y": 10 }, "type": "J" }, + { "position": { "x": 7, "y": 11 }, "type": "J" }, + { "position": { "x": 7, "y": 12 }, "type": "J" }, + { "position": { "x": 3, "y": 10 }, "type": "L" }, + { "position": { "x": 3, "y": 11 }, "type": "L" }, + { "position": { "x": 3, "y": 12 }, "type": "L" }, + { "position": { "x": 4, "y": 12 }, "type": "L" } + ], + "score": 960, + "simulation_step_index": 1415, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 1, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 0, "y": 12 }, "type": "J" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 13 }, "type": "T" }, + { "position": { "x": 8, "y": 13 }, "type": "T" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 8, "y": 11 }, "type": "L" }, + { "position": { "x": 8, "y": 12 }, "type": "L" }, + { "position": { "x": 1, "y": 11 }, "type": "T" }, + { "position": { "x": 1, "y": 12 }, "type": "T" }, + { "position": { "x": 2, "y": 12 }, "type": "T" }, + { "position": { "x": 1, "y": 13 }, "type": "T" }, + { "position": { "x": 4, "y": 13 }, "type": "Z" }, + { "position": { "x": 5, "y": 13 }, "type": "Z" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 5, "y": 11 }, "type": "S" }, + { "position": { "x": 5, "y": 12 }, "type": "S" }, + { "position": { "x": 6, "y": 12 }, "type": "S" }, + { "position": { "x": 6, "y": 13 }, "type": "S" }, + { "position": { "x": 8, "y": 10 }, "type": "J" }, + { "position": { "x": 7, "y": 10 }, "type": "J" }, + { "position": { "x": 7, "y": 11 }, "type": "J" }, + { "position": { "x": 7, "y": 12 }, "type": "J" }, + { "position": { "x": 3, "y": 10 }, "type": "L" }, + { "position": { "x": 3, "y": 11 }, "type": "L" }, + { "position": { "x": 3, "y": 12 }, "type": "L" }, + { "position": { "x": 4, "y": 12 }, "type": "L" }, + { "position": { "x": 7, "y": 8 }, "type": "O" }, + { "position": { "x": 8, "y": 8 }, "type": "O" }, + { "position": { "x": 7, "y": 9 }, "type": "O" }, + { "position": { "x": 8, "y": 9 }, "type": "O" } + ], + "score": 984, + "simulation_step_index": 1518, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 2, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 13 }, "type": "T" }, + { "position": { "x": 8, "y": 13 }, "type": "T" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 8, "y": 12 }, "type": "L" }, + { "position": { "x": 1, "y": 12 }, "type": "T" }, + { "position": { "x": 1, "y": 13 }, "type": "T" }, + { "position": { "x": 4, "y": 13 }, "type": "Z" }, + { "position": { "x": 5, "y": 13 }, "type": "Z" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 5, "y": 12 }, "type": "S" }, + { "position": { "x": 6, "y": 13 }, "type": "S" }, + { "position": { "x": 8, "y": 11 }, "type": "J" }, + { "position": { "x": 7, "y": 11 }, "type": "J" }, + { "position": { "x": 7, "y": 12 }, "type": "J" }, + { "position": { "x": 3, "y": 11 }, "type": "L" }, + { "position": { "x": 3, "y": 12 }, "type": "L" }, + { "position": { "x": 7, "y": 9 }, "type": "O" }, + { "position": { "x": 8, "y": 9 }, "type": "O" }, + { "position": { "x": 7, "y": 10 }, "type": "O" }, + { "position": { "x": 8, "y": 10 }, "type": "O" }, + { "position": { "x": 9, "y": 11 }, "type": "I" }, + { "position": { "x": 9, "y": 12 }, "type": "I" }, + { "position": { "x": 9, "y": 13 }, "type": "I" } + ], + "score": 1064, + "simulation_step_index": 1553, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 2, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 13 }, "type": "Z" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 13 }, "type": "T" }, + { "position": { "x": 8, "y": 13 }, "type": "T" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 8, "y": 12 }, "type": "L" }, + { "position": { "x": 1, "y": 12 }, "type": "T" }, + { "position": { "x": 1, "y": 13 }, "type": "T" }, + { "position": { "x": 4, "y": 13 }, "type": "Z" }, + { "position": { "x": 5, "y": 13 }, "type": "Z" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 5, "y": 12 }, "type": "S" }, + { "position": { "x": 6, "y": 13 }, "type": "S" }, + { "position": { "x": 8, "y": 11 }, "type": "J" }, + { "position": { "x": 7, "y": 11 }, "type": "J" }, + { "position": { "x": 7, "y": 12 }, "type": "J" }, + { "position": { "x": 3, "y": 11 }, "type": "L" }, + { "position": { "x": 3, "y": 12 }, "type": "L" }, + { "position": { "x": 7, "y": 9 }, "type": "O" }, + { "position": { "x": 8, "y": 9 }, "type": "O" }, + { "position": { "x": 7, "y": 10 }, "type": "O" }, + { "position": { "x": 8, "y": 10 }, "type": "O" }, + { "position": { "x": 9, "y": 11 }, "type": "I" }, + { "position": { "x": 9, "y": 12 }, "type": "I" }, + { "position": { "x": 9, "y": 13 }, "type": "I" }, + { "position": { "x": 5, "y": 10 }, "type": "Z" }, + { "position": { "x": 5, "y": 11 }, "type": "Z" }, + { "position": { "x": 4, "y": 11 }, "type": "Z" }, + { "position": { "x": 4, "y": 12 }, "type": "Z" } + ], + "score": 1100, + "simulation_step_index": 1615, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 3, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 8, "y": 13 }, "type": "L" }, + { "position": { "x": 1, "y": 13 }, "type": "T" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 5, "y": 13 }, "type": "S" }, + { "position": { "x": 8, "y": 12 }, "type": "J" }, + { "position": { "x": 7, "y": 12 }, "type": "J" }, + { "position": { "x": 7, "y": 13 }, "type": "J" }, + { "position": { "x": 3, "y": 12 }, "type": "L" }, + { "position": { "x": 3, "y": 13 }, "type": "L" }, + { "position": { "x": 7, "y": 10 }, "type": "O" }, + { "position": { "x": 8, "y": 10 }, "type": "O" }, + { "position": { "x": 7, "y": 11 }, "type": "O" }, + { "position": { "x": 8, "y": 11 }, "type": "O" }, + { "position": { "x": 9, "y": 12 }, "type": "I" }, + { "position": { "x": 9, "y": 13 }, "type": "I" }, + { "position": { "x": 5, "y": 11 }, "type": "Z" }, + { "position": { "x": 5, "y": 12 }, "type": "Z" }, + { "position": { "x": 4, "y": 12 }, "type": "Z" }, + { "position": { "x": 4, "y": 13 }, "type": "Z" }, + { "position": { "x": 1, "y": 12 }, "type": "J" }, + { "position": { "x": 0, "y": 12 }, "type": "J" }, + { "position": { "x": 0, "y": 13 }, "type": "J" } + ], + "score": 1180, + "simulation_step_index": 1664, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 3, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 8, "y": 13 }, "type": "L" }, + { "position": { "x": 1, "y": 13 }, "type": "T" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 5, "y": 13 }, "type": "S" }, + { "position": { "x": 8, "y": 12 }, "type": "J" }, + { "position": { "x": 7, "y": 12 }, "type": "J" }, + { "position": { "x": 7, "y": 13 }, "type": "J" }, + { "position": { "x": 3, "y": 12 }, "type": "L" }, + { "position": { "x": 3, "y": 13 }, "type": "L" }, + { "position": { "x": 7, "y": 10 }, "type": "O" }, + { "position": { "x": 8, "y": 10 }, "type": "O" }, + { "position": { "x": 7, "y": 11 }, "type": "O" }, + { "position": { "x": 8, "y": 11 }, "type": "O" }, + { "position": { "x": 9, "y": 12 }, "type": "I" }, + { "position": { "x": 9, "y": 13 }, "type": "I" }, + { "position": { "x": 5, "y": 11 }, "type": "Z" }, + { "position": { "x": 5, "y": 12 }, "type": "Z" }, + { "position": { "x": 4, "y": 12 }, "type": "Z" }, + { "position": { "x": 4, "y": 13 }, "type": "Z" }, + { "position": { "x": 1, "y": 12 }, "type": "J" }, + { "position": { "x": 0, "y": 12 }, "type": "J" }, + { "position": { "x": 0, "y": 13 }, "type": "J" }, + { "position": { "x": 1, "y": 11 }, "type": "L" }, + { "position": { "x": 2, "y": 11 }, "type": "L" }, + { "position": { "x": 2, "y": 12 }, "type": "L" }, + { "position": { "x": 2, "y": 13 }, "type": "L" } + ], + "score": 1220, + "simulation_step_index": 1748, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 3, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 8, "y": 13 }, "type": "L" }, + { "position": { "x": 1, "y": 13 }, "type": "T" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 5, "y": 13 }, "type": "S" }, + { "position": { "x": 8, "y": 12 }, "type": "J" }, + { "position": { "x": 7, "y": 12 }, "type": "J" }, + { "position": { "x": 7, "y": 13 }, "type": "J" }, + { "position": { "x": 3, "y": 12 }, "type": "L" }, + { "position": { "x": 3, "y": 13 }, "type": "L" }, + { "position": { "x": 7, "y": 10 }, "type": "O" }, + { "position": { "x": 8, "y": 10 }, "type": "O" }, + { "position": { "x": 7, "y": 11 }, "type": "O" }, + { "position": { "x": 8, "y": 11 }, "type": "O" }, + { "position": { "x": 9, "y": 12 }, "type": "I" }, + { "position": { "x": 9, "y": 13 }, "type": "I" }, + { "position": { "x": 5, "y": 11 }, "type": "Z" }, + { "position": { "x": 5, "y": 12 }, "type": "Z" }, + { "position": { "x": 4, "y": 12 }, "type": "Z" }, + { "position": { "x": 4, "y": 13 }, "type": "Z" }, + { "position": { "x": 1, "y": 12 }, "type": "J" }, + { "position": { "x": 0, "y": 12 }, "type": "J" }, + { "position": { "x": 0, "y": 13 }, "type": "J" }, + { "position": { "x": 1, "y": 11 }, "type": "L" }, + { "position": { "x": 2, "y": 11 }, "type": "L" }, + { "position": { "x": 2, "y": 12 }, "type": "L" }, + { "position": { "x": 2, "y": 13 }, "type": "L" }, + { "position": { "x": 0, "y": 9 }, "type": "T" }, + { "position": { "x": 0, "y": 10 }, "type": "T" }, + { "position": { "x": 1, "y": 10 }, "type": "T" }, + { "position": { "x": 0, "y": 11 }, "type": "T" } + ], + "score": 1252, + "simulation_step_index": 1824, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 5, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 7, "y": 12 }, "type": "O" }, + { "position": { "x": 8, "y": 12 }, "type": "O" }, + { "position": { "x": 7, "y": 13 }, "type": "O" }, + { "position": { "x": 8, "y": 13 }, "type": "O" }, + { "position": { "x": 5, "y": 13 }, "type": "Z" }, + { "position": { "x": 1, "y": 13 }, "type": "L" }, + { "position": { "x": 2, "y": 13 }, "type": "L" }, + { "position": { "x": 0, "y": 11 }, "type": "T" }, + { "position": { "x": 0, "y": 12 }, "type": "T" }, + { "position": { "x": 1, "y": 12 }, "type": "T" }, + { "position": { "x": 0, "y": 13 }, "type": "T" }, + { "position": { "x": 6, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 13 }, "type": "I" } + ], + "score": 1388, + "simulation_step_index": 1885, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 5, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 7, "y": 12 }, "type": "O" }, + { "position": { "x": 8, "y": 12 }, "type": "O" }, + { "position": { "x": 7, "y": 13 }, "type": "O" }, + { "position": { "x": 8, "y": 13 }, "type": "O" }, + { "position": { "x": 5, "y": 13 }, "type": "Z" }, + { "position": { "x": 1, "y": 13 }, "type": "L" }, + { "position": { "x": 2, "y": 13 }, "type": "L" }, + { "position": { "x": 0, "y": 11 }, "type": "T" }, + { "position": { "x": 0, "y": 12 }, "type": "T" }, + { "position": { "x": 1, "y": 12 }, "type": "T" }, + { "position": { "x": 0, "y": 13 }, "type": "T" }, + { "position": { "x": 6, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 13 }, "type": "I" }, + { "position": { "x": 3, "y": 13 }, "type": "S" }, + { "position": { "x": 4, "y": 13 }, "type": "S" }, + { "position": { "x": 4, "y": 12 }, "type": "S" }, + { "position": { "x": 5, "y": 12 }, "type": "S" } + ], + "score": 1432, + "simulation_step_index": 1935, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 5, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 7, "y": 12 }, "type": "O" }, + { "position": { "x": 8, "y": 12 }, "type": "O" }, + { "position": { "x": 7, "y": 13 }, "type": "O" }, + { "position": { "x": 8, "y": 13 }, "type": "O" }, + { "position": { "x": 5, "y": 13 }, "type": "Z" }, + { "position": { "x": 1, "y": 13 }, "type": "L" }, + { "position": { "x": 2, "y": 13 }, "type": "L" }, + { "position": { "x": 0, "y": 11 }, "type": "T" }, + { "position": { "x": 0, "y": 12 }, "type": "T" }, + { "position": { "x": 1, "y": 12 }, "type": "T" }, + { "position": { "x": 0, "y": 13 }, "type": "T" }, + { "position": { "x": 6, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 13 }, "type": "I" }, + { "position": { "x": 3, "y": 13 }, "type": "S" }, + { "position": { "x": 4, "y": 13 }, "type": "S" }, + { "position": { "x": 4, "y": 12 }, "type": "S" }, + { "position": { "x": 5, "y": 12 }, "type": "S" }, + { "position": { "x": 6, "y": 10 }, "type": "O" }, + { "position": { "x": 7, "y": 10 }, "type": "O" }, + { "position": { "x": 6, "y": 11 }, "type": "O" }, + { "position": { "x": 7, "y": 11 }, "type": "O" } + ], + "score": 1468, + "simulation_step_index": 2026, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 5, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 7, "y": 12 }, "type": "O" }, + { "position": { "x": 8, "y": 12 }, "type": "O" }, + { "position": { "x": 7, "y": 13 }, "type": "O" }, + { "position": { "x": 8, "y": 13 }, "type": "O" }, + { "position": { "x": 5, "y": 13 }, "type": "Z" }, + { "position": { "x": 1, "y": 13 }, "type": "L" }, + { "position": { "x": 2, "y": 13 }, "type": "L" }, + { "position": { "x": 0, "y": 11 }, "type": "T" }, + { "position": { "x": 0, "y": 12 }, "type": "T" }, + { "position": { "x": 1, "y": 12 }, "type": "T" }, + { "position": { "x": 0, "y": 13 }, "type": "T" }, + { "position": { "x": 6, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 13 }, "type": "I" }, + { "position": { "x": 3, "y": 13 }, "type": "S" }, + { "position": { "x": 4, "y": 13 }, "type": "S" }, + { "position": { "x": 4, "y": 12 }, "type": "S" }, + { "position": { "x": 5, "y": 12 }, "type": "S" }, + { "position": { "x": 6, "y": 10 }, "type": "O" }, + { "position": { "x": 7, "y": 10 }, "type": "O" }, + { "position": { "x": 6, "y": 11 }, "type": "O" }, + { "position": { "x": 7, "y": 11 }, "type": "O" }, + { "position": { "x": 4, "y": 11 }, "type": "J" }, + { "position": { "x": 5, "y": 11 }, "type": "J" }, + { "position": { "x": 5, "y": 10 }, "type": "J" }, + { "position": { "x": 5, "y": 9 }, "type": "J" } + ], + "score": 1492, + "simulation_step_index": 2174, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 5, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 7, "y": 12 }, "type": "O" }, + { "position": { "x": 8, "y": 12 }, "type": "O" }, + { "position": { "x": 7, "y": 13 }, "type": "O" }, + { "position": { "x": 8, "y": 13 }, "type": "O" }, + { "position": { "x": 5, "y": 13 }, "type": "Z" }, + { "position": { "x": 1, "y": 13 }, "type": "L" }, + { "position": { "x": 2, "y": 13 }, "type": "L" }, + { "position": { "x": 0, "y": 11 }, "type": "T" }, + { "position": { "x": 0, "y": 12 }, "type": "T" }, + { "position": { "x": 1, "y": 12 }, "type": "T" }, + { "position": { "x": 0, "y": 13 }, "type": "T" }, + { "position": { "x": 6, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 13 }, "type": "I" }, + { "position": { "x": 3, "y": 13 }, "type": "S" }, + { "position": { "x": 4, "y": 13 }, "type": "S" }, + { "position": { "x": 4, "y": 12 }, "type": "S" }, + { "position": { "x": 5, "y": 12 }, "type": "S" }, + { "position": { "x": 6, "y": 10 }, "type": "O" }, + { "position": { "x": 7, "y": 10 }, "type": "O" }, + { "position": { "x": 6, "y": 11 }, "type": "O" }, + { "position": { "x": 7, "y": 11 }, "type": "O" }, + { "position": { "x": 4, "y": 11 }, "type": "J" }, + { "position": { "x": 5, "y": 11 }, "type": "J" }, + { "position": { "x": 5, "y": 10 }, "type": "J" }, + { "position": { "x": 5, "y": 9 }, "type": "J" }, + { "position": { "x": 1, "y": 11 }, "type": "Z" }, + { "position": { "x": 2, "y": 11 }, "type": "Z" }, + { "position": { "x": 2, "y": 12 }, "type": "Z" }, + { "position": { "x": 3, "y": 12 }, "type": "Z" } + ], + "score": 1524, + "simulation_step_index": 2271, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 5, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 7, "y": 12 }, "type": "O" }, + { "position": { "x": 8, "y": 12 }, "type": "O" }, + { "position": { "x": 7, "y": 13 }, "type": "O" }, + { "position": { "x": 8, "y": 13 }, "type": "O" }, + { "position": { "x": 5, "y": 13 }, "type": "Z" }, + { "position": { "x": 1, "y": 13 }, "type": "L" }, + { "position": { "x": 2, "y": 13 }, "type": "L" }, + { "position": { "x": 0, "y": 11 }, "type": "T" }, + { "position": { "x": 0, "y": 12 }, "type": "T" }, + { "position": { "x": 1, "y": 12 }, "type": "T" }, + { "position": { "x": 0, "y": 13 }, "type": "T" }, + { "position": { "x": 6, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 13 }, "type": "I" }, + { "position": { "x": 3, "y": 13 }, "type": "S" }, + { "position": { "x": 4, "y": 13 }, "type": "S" }, + { "position": { "x": 4, "y": 12 }, "type": "S" }, + { "position": { "x": 5, "y": 12 }, "type": "S" }, + { "position": { "x": 6, "y": 10 }, "type": "O" }, + { "position": { "x": 7, "y": 10 }, "type": "O" }, + { "position": { "x": 6, "y": 11 }, "type": "O" }, + { "position": { "x": 7, "y": 11 }, "type": "O" }, + { "position": { "x": 4, "y": 11 }, "type": "J" }, + { "position": { "x": 5, "y": 11 }, "type": "J" }, + { "position": { "x": 5, "y": 10 }, "type": "J" }, + { "position": { "x": 5, "y": 9 }, "type": "J" }, + { "position": { "x": 1, "y": 11 }, "type": "Z" }, + { "position": { "x": 2, "y": 11 }, "type": "Z" }, + { "position": { "x": 2, "y": 12 }, "type": "Z" }, + { "position": { "x": 3, "y": 12 }, "type": "Z" }, + { "position": { "x": 2, "y": 9 }, "type": "S" }, + { "position": { "x": 2, "y": 10 }, "type": "S" }, + { "position": { "x": 3, "y": 10 }, "type": "S" }, + { "position": { "x": 3, "y": 11 }, "type": "S" } + ], + "score": 1556, + "simulation_step_index": 2353, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 8, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 13 }, "type": "O" }, + { "position": { "x": 7, "y": 13 }, "type": "O" }, + { "position": { "x": 5, "y": 13 }, "type": "J" }, + { "position": { "x": 5, "y": 12 }, "type": "J" }, + { "position": { "x": 2, "y": 12 }, "type": "S" }, + { "position": { "x": 2, "y": 13 }, "type": "S" }, + { "position": { "x": 3, "y": 13 }, "type": "S" } + ], + "score": 1896, + "simulation_step_index": 2431, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 8, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 13 }, "type": "O" }, + { "position": { "x": 7, "y": 13 }, "type": "O" }, + { "position": { "x": 5, "y": 13 }, "type": "J" }, + { "position": { "x": 5, "y": 12 }, "type": "J" }, + { "position": { "x": 2, "y": 12 }, "type": "S" }, + { "position": { "x": 2, "y": 13 }, "type": "S" }, + { "position": { "x": 3, "y": 13 }, "type": "S" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" } + ], + "score": 1972, + "simulation_step_index": 2732, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 8, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 13 }, "type": "O" }, + { "position": { "x": 7, "y": 13 }, "type": "O" }, + { "position": { "x": 5, "y": 13 }, "type": "J" }, + { "position": { "x": 5, "y": 12 }, "type": "J" }, + { "position": { "x": 2, "y": 12 }, "type": "S" }, + { "position": { "x": 2, "y": 13 }, "type": "S" }, + { "position": { "x": 3, "y": 13 }, "type": "S" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 1, "y": 14 }, "type": "O" }, + { "position": { "x": 2, "y": 14 }, "type": "O" }, + { "position": { "x": 1, "y": 15 }, "type": "O" }, + { "position": { "x": 2, "y": 15 }, "type": "O" } + ], + "score": 2040, + "simulation_step_index": 2949, + "tetrion_index": 0 + }, + { + "level": 0, + "lines_cleared": 8, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 3, "y": 15 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 6, "y": 15 }, "type": "I" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 7, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 7, "y": 14 }, "type": "T" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 14 }, "type": "Z" }, + { "position": { "x": 6, "y": 13 }, "type": "O" }, + { "position": { "x": 7, "y": 13 }, "type": "O" }, + { "position": { "x": 5, "y": 13 }, "type": "J" }, + { "position": { "x": 5, "y": 12 }, "type": "J" }, + { "position": { "x": 2, "y": 12 }, "type": "S" }, + { "position": { "x": 2, "y": 13 }, "type": "S" }, + { "position": { "x": 3, "y": 13 }, "type": "S" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 1, "y": 14 }, "type": "O" }, + { "position": { "x": 2, "y": 14 }, "type": "O" }, + { "position": { "x": 1, "y": 15 }, "type": "O" }, + { "position": { "x": 2, "y": 15 }, "type": "O" }, + { "position": { "x": 4, "y": 11 }, "type": "T" }, + { "position": { "x": 4, "y": 12 }, "type": "T" }, + { "position": { "x": 3, "y": 12 }, "type": "T" }, + { "position": { "x": 4, "y": 13 }, "type": "T" } + ], + "score": 2084, + "simulation_step_index": 3013, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 10, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 6, "y": 15 }, "type": "O" }, + { "position": { "x": 7, "y": 15 }, "type": "O" }, + { "position": { "x": 5, "y": 15 }, "type": "J" }, + { "position": { "x": 5, "y": 14 }, "type": "J" }, + { "position": { "x": 2, "y": 14 }, "type": "S" }, + { "position": { "x": 2, "y": 15 }, "type": "S" }, + { "position": { "x": 3, "y": 15 }, "type": "S" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 4, "y": 13 }, "type": "T" }, + { "position": { "x": 4, "y": 14 }, "type": "T" }, + { "position": { "x": 3, "y": 14 }, "type": "T" }, + { "position": { "x": 4, "y": 15 }, "type": "T" }, + { "position": { "x": 0, "y": 16 }, "type": "I" }, + { "position": { "x": 0, "y": 17 }, "type": "I" } + ], + "score": 2340, + "simulation_step_index": 3160, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 10, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 6, "y": 15 }, "type": "O" }, + { "position": { "x": 7, "y": 15 }, "type": "O" }, + { "position": { "x": 5, "y": 15 }, "type": "J" }, + { "position": { "x": 5, "y": 14 }, "type": "J" }, + { "position": { "x": 2, "y": 14 }, "type": "S" }, + { "position": { "x": 2, "y": 15 }, "type": "S" }, + { "position": { "x": 3, "y": 15 }, "type": "S" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 4, "y": 13 }, "type": "T" }, + { "position": { "x": 4, "y": 14 }, "type": "T" }, + { "position": { "x": 3, "y": 14 }, "type": "T" }, + { "position": { "x": 4, "y": 15 }, "type": "T" }, + { "position": { "x": 0, "y": 16 }, "type": "I" }, + { "position": { "x": 0, "y": 17 }, "type": "I" }, + { "position": { "x": 7, "y": 14 }, "type": "Z" }, + { "position": { "x": 8, "y": 14 }, "type": "Z" }, + { "position": { "x": 8, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" } + ], + "score": 2392, + "simulation_step_index": 3232, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 10, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 6, "y": 15 }, "type": "O" }, + { "position": { "x": 7, "y": 15 }, "type": "O" }, + { "position": { "x": 5, "y": 15 }, "type": "J" }, + { "position": { "x": 5, "y": 14 }, "type": "J" }, + { "position": { "x": 2, "y": 14 }, "type": "S" }, + { "position": { "x": 2, "y": 15 }, "type": "S" }, + { "position": { "x": 3, "y": 15 }, "type": "S" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 4, "y": 13 }, "type": "T" }, + { "position": { "x": 4, "y": 14 }, "type": "T" }, + { "position": { "x": 3, "y": 14 }, "type": "T" }, + { "position": { "x": 4, "y": 15 }, "type": "T" }, + { "position": { "x": 0, "y": 16 }, "type": "I" }, + { "position": { "x": 0, "y": 17 }, "type": "I" }, + { "position": { "x": 7, "y": 14 }, "type": "Z" }, + { "position": { "x": 8, "y": 14 }, "type": "Z" }, + { "position": { "x": 8, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 8, "y": 12 }, "type": "S" }, + { "position": { "x": 8, "y": 13 }, "type": "S" }, + { "position": { "x": 9, "y": 13 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" } + ], + "score": 2432, + "simulation_step_index": 3385, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 10, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 6, "y": 15 }, "type": "O" }, + { "position": { "x": 7, "y": 15 }, "type": "O" }, + { "position": { "x": 5, "y": 15 }, "type": "J" }, + { "position": { "x": 5, "y": 14 }, "type": "J" }, + { "position": { "x": 2, "y": 14 }, "type": "S" }, + { "position": { "x": 2, "y": 15 }, "type": "S" }, + { "position": { "x": 3, "y": 15 }, "type": "S" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 4, "y": 13 }, "type": "T" }, + { "position": { "x": 4, "y": 14 }, "type": "T" }, + { "position": { "x": 3, "y": 14 }, "type": "T" }, + { "position": { "x": 4, "y": 15 }, "type": "T" }, + { "position": { "x": 0, "y": 16 }, "type": "I" }, + { "position": { "x": 0, "y": 17 }, "type": "I" }, + { "position": { "x": 7, "y": 14 }, "type": "Z" }, + { "position": { "x": 8, "y": 14 }, "type": "Z" }, + { "position": { "x": 8, "y": 15 }, "type": "Z" }, + { "position": { "x": 9, "y": 15 }, "type": "Z" }, + { "position": { "x": 8, "y": 12 }, "type": "S" }, + { "position": { "x": 8, "y": 13 }, "type": "S" }, + { "position": { "x": 9, "y": 13 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 2, "y": 13 }, "type": "J" }, + { "position": { "x": 1, "y": 13 }, "type": "J" }, + { "position": { "x": 1, "y": 14 }, "type": "J" }, + { "position": { "x": 1, "y": 15 }, "type": "J" } + ], + "score": 2476, + "simulation_step_index": 3474, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 11, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 5, "y": 15 }, "type": "J" }, + { "position": { "x": 2, "y": 15 }, "type": "S" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 4, "y": 14 }, "type": "T" }, + { "position": { "x": 4, "y": 15 }, "type": "T" }, + { "position": { "x": 3, "y": 15 }, "type": "T" }, + { "position": { "x": 0, "y": 16 }, "type": "I" }, + { "position": { "x": 0, "y": 17 }, "type": "I" }, + { "position": { "x": 7, "y": 15 }, "type": "Z" }, + { "position": { "x": 8, "y": 15 }, "type": "Z" }, + { "position": { "x": 8, "y": 13 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 15 }, "type": "S" }, + { "position": { "x": 2, "y": 14 }, "type": "J" }, + { "position": { "x": 1, "y": 14 }, "type": "J" }, + { "position": { "x": 1, "y": 15 }, "type": "J" }, + { "position": { "x": 0, "y": 13 }, "type": "I" }, + { "position": { "x": 0, "y": 14 }, "type": "I" }, + { "position": { "x": 0, "y": 15 }, "type": "I" } + ], + "score": 2604, + "simulation_step_index": 3636, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 11, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 5, "y": 15 }, "type": "J" }, + { "position": { "x": 2, "y": 15 }, "type": "S" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 4, "y": 14 }, "type": "T" }, + { "position": { "x": 4, "y": 15 }, "type": "T" }, + { "position": { "x": 3, "y": 15 }, "type": "T" }, + { "position": { "x": 0, "y": 16 }, "type": "I" }, + { "position": { "x": 0, "y": 17 }, "type": "I" }, + { "position": { "x": 7, "y": 15 }, "type": "Z" }, + { "position": { "x": 8, "y": 15 }, "type": "Z" }, + { "position": { "x": 8, "y": 13 }, "type": "S" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 14 }, "type": "S" }, + { "position": { "x": 9, "y": 15 }, "type": "S" }, + { "position": { "x": 2, "y": 14 }, "type": "J" }, + { "position": { "x": 1, "y": 14 }, "type": "J" }, + { "position": { "x": 1, "y": 15 }, "type": "J" }, + { "position": { "x": 0, "y": 13 }, "type": "I" }, + { "position": { "x": 0, "y": 14 }, "type": "I" }, + { "position": { "x": 0, "y": 15 }, "type": "I" }, + { "position": { "x": 1, "y": 12 }, "type": "O" }, + { "position": { "x": 2, "y": 12 }, "type": "O" }, + { "position": { "x": 1, "y": 13 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "O" } + ], + "score": 2648, + "simulation_step_index": 3698, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 12, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 4, "y": 15 }, "type": "T" }, + { "position": { "x": 0, "y": 16 }, "type": "I" }, + { "position": { "x": 0, "y": 17 }, "type": "I" }, + { "position": { "x": 8, "y": 14 }, "type": "S" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 9, "y": 15 }, "type": "S" }, + { "position": { "x": 2, "y": 15 }, "type": "J" }, + { "position": { "x": 1, "y": 15 }, "type": "J" }, + { "position": { "x": 0, "y": 14 }, "type": "I" }, + { "position": { "x": 0, "y": 15 }, "type": "I" }, + { "position": { "x": 1, "y": 13 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "O" }, + { "position": { "x": 1, "y": 14 }, "type": "O" }, + { "position": { "x": 2, "y": 14 }, "type": "O" }, + { "position": { "x": 5, "y": 15 }, "type": "T" }, + { "position": { "x": 6, "y": 15 }, "type": "T" }, + { "position": { "x": 7, "y": 15 }, "type": "T" } + ], + "score": 2776, + "simulation_step_index": 3784, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 13, + "mino_stack": [ + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 17 }, "type": "L" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 9, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 16 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 0, "y": 16 }, "type": "I" }, + { "position": { "x": 0, "y": 17 }, "type": "I" }, + { "position": { "x": 8, "y": 15 }, "type": "S" }, + { "position": { "x": 0, "y": 15 }, "type": "I" }, + { "position": { "x": 1, "y": 14 }, "type": "O" }, + { "position": { "x": 2, "y": 14 }, "type": "O" }, + { "position": { "x": 1, "y": 15 }, "type": "O" }, + { "position": { "x": 2, "y": 15 }, "type": "O" }, + { "position": { "x": 4, "y": 15 }, "type": "J" }, + { "position": { "x": 3, "y": 15 }, "type": "J" }, + { "position": { "x": 3, "y": 16 }, "type": "J" } + ], + "score": 2900, + "simulation_step_index": 3918, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 14, + "mino_stack": [ + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 9, "y": 17 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 0, "y": 17 }, "type": "I" }, + { "position": { "x": 8, "y": 16 }, "type": "S" }, + { "position": { "x": 0, "y": 16 }, "type": "I" }, + { "position": { "x": 1, "y": 15 }, "type": "O" }, + { "position": { "x": 2, "y": 15 }, "type": "O" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 4, "y": 16 }, "type": "J" }, + { "position": { "x": 3, "y": 16 }, "type": "J" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 7, "y": 16 }, "type": "L" }, + { "position": { "x": 7, "y": 17 }, "type": "L" } + ], + "score": 3032, + "simulation_step_index": 4138, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 14, + "mino_stack": [ + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 9, "y": 17 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 0, "y": 17 }, "type": "I" }, + { "position": { "x": 8, "y": 16 }, "type": "S" }, + { "position": { "x": 0, "y": 16 }, "type": "I" }, + { "position": { "x": 1, "y": 15 }, "type": "O" }, + { "position": { "x": 2, "y": 15 }, "type": "O" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 4, "y": 16 }, "type": "J" }, + { "position": { "x": 3, "y": 16 }, "type": "J" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 7, "y": 16 }, "type": "L" }, + { "position": { "x": 7, "y": 17 }, "type": "L" }, + { "position": { "x": 1, "y": 13 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "O" }, + { "position": { "x": 1, "y": 14 }, "type": "O" }, + { "position": { "x": 2, "y": 14 }, "type": "O" } + ], + "score": 3080, + "simulation_step_index": 4216, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 14, + "mino_stack": [ + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 9, "y": 17 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 0, "y": 17 }, "type": "I" }, + { "position": { "x": 8, "y": 16 }, "type": "S" }, + { "position": { "x": 0, "y": 16 }, "type": "I" }, + { "position": { "x": 1, "y": 15 }, "type": "O" }, + { "position": { "x": 2, "y": 15 }, "type": "O" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 4, "y": 16 }, "type": "J" }, + { "position": { "x": 3, "y": 16 }, "type": "J" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 7, "y": 16 }, "type": "L" }, + { "position": { "x": 7, "y": 17 }, "type": "L" }, + { "position": { "x": 1, "y": 13 }, "type": "O" }, + { "position": { "x": 2, "y": 13 }, "type": "O" }, + { "position": { "x": 1, "y": 14 }, "type": "O" }, + { "position": { "x": 2, "y": 14 }, "type": "O" }, + { "position": { "x": 9, "y": 14 }, "type": "T" }, + { "position": { "x": 9, "y": 15 }, "type": "T" }, + { "position": { "x": 8, "y": 15 }, "type": "T" }, + { "position": { "x": 9, "y": 16 }, "type": "T" } + ], + "score": 3132, + "simulation_step_index": 4291, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 15, + "mino_stack": [ + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 9, "y": 17 }, "type": "Z" }, + { "position": { "x": 8, "y": 17 }, "type": "Z" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 0, "y": 17 }, "type": "I" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 3, "y": 17 }, "type": "J" }, + { "position": { "x": 7, "y": 17 }, "type": "L" }, + { "position": { "x": 1, "y": 14 }, "type": "O" }, + { "position": { "x": 2, "y": 14 }, "type": "O" }, + { "position": { "x": 1, "y": 15 }, "type": "O" }, + { "position": { "x": 2, "y": 15 }, "type": "O" }, + { "position": { "x": 9, "y": 15 }, "type": "T" }, + { "position": { "x": 9, "y": 16 }, "type": "T" }, + { "position": { "x": 8, "y": 16 }, "type": "T" }, + { "position": { "x": 5, "y": 15 }, "type": "I" }, + { "position": { "x": 5, "y": 16 }, "type": "I" }, + { "position": { "x": 5, "y": 17 }, "type": "I" } + ], + "score": 3264, + "simulation_step_index": 4372, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 16, + "mino_stack": [ + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 1, "y": 15 }, "type": "O" }, + { "position": { "x": 2, "y": 15 }, "type": "O" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 9, "y": 16 }, "type": "T" }, + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 8, "y": 17 }, "type": "T" }, + { "position": { "x": 5, "y": 16 }, "type": "I" }, + { "position": { "x": 5, "y": 17 }, "type": "I" }, + { "position": { "x": 3, "y": 16 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "S" }, + { "position": { "x": 4, "y": 17 }, "type": "S" } + ], + "score": 3400, + "simulation_step_index": 4433, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 16, + "mino_stack": [ + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 1, "y": 15 }, "type": "O" }, + { "position": { "x": 2, "y": 15 }, "type": "O" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 9, "y": 16 }, "type": "T" }, + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 8, "y": 17 }, "type": "T" }, + { "position": { "x": 5, "y": 16 }, "type": "I" }, + { "position": { "x": 5, "y": 17 }, "type": "I" }, + { "position": { "x": 3, "y": 16 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "S" }, + { "position": { "x": 4, "y": 17 }, "type": "S" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 5, "y": 15 }, "type": "Z" }, + { "position": { "x": 4, "y": 15 }, "type": "Z" }, + { "position": { "x": 4, "y": 16 }, "type": "Z" } + ], + "score": 3448, + "simulation_step_index": 4665, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 16, + "mino_stack": [ + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 1, "y": 15 }, "type": "O" }, + { "position": { "x": 2, "y": 15 }, "type": "O" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 9, "y": 16 }, "type": "T" }, + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 8, "y": 17 }, "type": "T" }, + { "position": { "x": 5, "y": 16 }, "type": "I" }, + { "position": { "x": 5, "y": 17 }, "type": "I" }, + { "position": { "x": 3, "y": 16 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "S" }, + { "position": { "x": 4, "y": 17 }, "type": "S" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 5, "y": 15 }, "type": "Z" }, + { "position": { "x": 4, "y": 15 }, "type": "Z" }, + { "position": { "x": 4, "y": 16 }, "type": "Z" }, + { "position": { "x": 6, "y": 16 }, "type": "O" }, + { "position": { "x": 7, "y": 16 }, "type": "O" }, + { "position": { "x": 6, "y": 17 }, "type": "O" }, + { "position": { "x": 7, "y": 17 }, "type": "O" } + ], + "score": 3508, + "simulation_step_index": 4740, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 16, + "mino_stack": [ + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 1, "y": 15 }, "type": "O" }, + { "position": { "x": 2, "y": 15 }, "type": "O" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 9, "y": 16 }, "type": "T" }, + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 8, "y": 17 }, "type": "T" }, + { "position": { "x": 5, "y": 16 }, "type": "I" }, + { "position": { "x": 5, "y": 17 }, "type": "I" }, + { "position": { "x": 3, "y": 16 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "S" }, + { "position": { "x": 4, "y": 17 }, "type": "S" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 5, "y": 15 }, "type": "Z" }, + { "position": { "x": 4, "y": 15 }, "type": "Z" }, + { "position": { "x": 4, "y": 16 }, "type": "Z" }, + { "position": { "x": 6, "y": 16 }, "type": "O" }, + { "position": { "x": 7, "y": 16 }, "type": "O" }, + { "position": { "x": 6, "y": 17 }, "type": "O" }, + { "position": { "x": 7, "y": 17 }, "type": "O" }, + { "position": { "x": 5, "y": 13 }, "type": "L" }, + { "position": { "x": 6, "y": 13 }, "type": "L" }, + { "position": { "x": 6, "y": 14 }, "type": "L" }, + { "position": { "x": 6, "y": 15 }, "type": "L" } + ], + "score": 3552, + "simulation_step_index": 5012, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 16, + "mino_stack": [ + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 1, "y": 15 }, "type": "O" }, + { "position": { "x": 2, "y": 15 }, "type": "O" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 9, "y": 16 }, "type": "T" }, + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 8, "y": 17 }, "type": "T" }, + { "position": { "x": 5, "y": 16 }, "type": "I" }, + { "position": { "x": 5, "y": 17 }, "type": "I" }, + { "position": { "x": 3, "y": 16 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "S" }, + { "position": { "x": 4, "y": 17 }, "type": "S" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 5, "y": 15 }, "type": "Z" }, + { "position": { "x": 4, "y": 15 }, "type": "Z" }, + { "position": { "x": 4, "y": 16 }, "type": "Z" }, + { "position": { "x": 6, "y": 16 }, "type": "O" }, + { "position": { "x": 7, "y": 16 }, "type": "O" }, + { "position": { "x": 6, "y": 17 }, "type": "O" }, + { "position": { "x": 7, "y": 17 }, "type": "O" }, + { "position": { "x": 5, "y": 13 }, "type": "L" }, + { "position": { "x": 6, "y": 13 }, "type": "L" }, + { "position": { "x": 6, "y": 14 }, "type": "L" }, + { "position": { "x": 6, "y": 15 }, "type": "L" }, + { "position": { "x": 7, "y": 15 }, "type": "T" }, + { "position": { "x": 8, "y": 15 }, "type": "T" }, + { "position": { "x": 9, "y": 15 }, "type": "T" }, + { "position": { "x": 8, "y": 16 }, "type": "T" } + ], + "score": 3604, + "simulation_step_index": 5073, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 16, + "mino_stack": [ + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 1, "y": 15 }, "type": "O" }, + { "position": { "x": 2, "y": 15 }, "type": "O" }, + { "position": { "x": 1, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 9, "y": 16 }, "type": "T" }, + { "position": { "x": 9, "y": 17 }, "type": "T" }, + { "position": { "x": 8, "y": 17 }, "type": "T" }, + { "position": { "x": 5, "y": 16 }, "type": "I" }, + { "position": { "x": 5, "y": 17 }, "type": "I" }, + { "position": { "x": 3, "y": 16 }, "type": "S" }, + { "position": { "x": 3, "y": 17 }, "type": "S" }, + { "position": { "x": 4, "y": 17 }, "type": "S" }, + { "position": { "x": 5, "y": 14 }, "type": "Z" }, + { "position": { "x": 5, "y": 15 }, "type": "Z" }, + { "position": { "x": 4, "y": 15 }, "type": "Z" }, + { "position": { "x": 4, "y": 16 }, "type": "Z" }, + { "position": { "x": 6, "y": 16 }, "type": "O" }, + { "position": { "x": 7, "y": 16 }, "type": "O" }, + { "position": { "x": 6, "y": 17 }, "type": "O" }, + { "position": { "x": 7, "y": 17 }, "type": "O" }, + { "position": { "x": 5, "y": 13 }, "type": "L" }, + { "position": { "x": 6, "y": 13 }, "type": "L" }, + { "position": { "x": 6, "y": 14 }, "type": "L" }, + { "position": { "x": 6, "y": 15 }, "type": "L" }, + { "position": { "x": 7, "y": 15 }, "type": "T" }, + { "position": { "x": 8, "y": 15 }, "type": "T" }, + { "position": { "x": 9, "y": 15 }, "type": "T" }, + { "position": { "x": 8, "y": 16 }, "type": "T" }, + { "position": { "x": 4, "y": 13 }, "type": "Z" }, + { "position": { "x": 4, "y": 14 }, "type": "Z" }, + { "position": { "x": 3, "y": 14 }, "type": "Z" }, + { "position": { "x": 3, "y": 15 }, "type": "Z" } + ], + "score": 3652, + "simulation_step_index": 5159, + "tetrion_index": 0 + }, + { + "level": 1, + "lines_cleared": 19, + "mino_stack": [ + { "position": { "x": 9, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 18 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "S" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 4, "y": 18 }, "type": "J" }, + { "position": { "x": 5, "y": 18 }, "type": "J" }, + { "position": { "x": 0, "y": 18 }, "type": "O" }, + { "position": { "x": 1, "y": 18 }, "type": "O" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 5, "y": 17 }, "type": "Z" }, + { "position": { "x": 5, "y": 16 }, "type": "L" }, + { "position": { "x": 6, "y": 16 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 4, "y": 16 }, "type": "Z" }, + { "position": { "x": 4, "y": 17 }, "type": "Z" }, + { "position": { "x": 3, "y": 17 }, "type": "Z" }, + { "position": { "x": 0, "y": 17 }, "type": "I" } + ], + "score": 4308, + "simulation_step_index": 5199, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 20, + "mino_stack": [ + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 5, "y": 18 }, "type": "Z" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 18 }, "type": "L" }, + { "position": { "x": 4, "y": 17 }, "type": "Z" }, + { "position": { "x": 4, "y": 18 }, "type": "Z" }, + { "position": { "x": 3, "y": 18 }, "type": "Z" }, + { "position": { "x": 0, "y": 18 }, "type": "I" }, + { "position": { "x": 1, "y": 18 }, "type": "L" }, + { "position": { "x": 2, "y": 18 }, "type": "L" }, + { "position": { "x": 2, "y": 19 }, "type": "L" } + ], + "score": 4492, + "simulation_step_index": 5353, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 20, + "mino_stack": [ + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 5, "y": 18 }, "type": "Z" }, + { "position": { "x": 5, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 17 }, "type": "L" }, + { "position": { "x": 6, "y": 18 }, "type": "L" }, + { "position": { "x": 4, "y": 17 }, "type": "Z" }, + { "position": { "x": 4, "y": 18 }, "type": "Z" }, + { "position": { "x": 3, "y": 18 }, "type": "Z" }, + { "position": { "x": 0, "y": 18 }, "type": "I" }, + { "position": { "x": 1, "y": 18 }, "type": "L" }, + { "position": { "x": 2, "y": 18 }, "type": "L" }, + { "position": { "x": 2, "y": 19 }, "type": "L" }, + { "position": { "x": 7, "y": 19 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 8, "y": 18 }, "type": "S" }, + { "position": { "x": 9, "y": 18 }, "type": "S" } + ], + "score": 4560, + "simulation_step_index": 5506, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 21, + "mino_stack": [ + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 5, "y": 18 }, "type": "L" }, + { "position": { "x": 6, "y": 18 }, "type": "L" }, + { "position": { "x": 4, "y": 18 }, "type": "Z" }, + { "position": { "x": 2, "y": 19 }, "type": "L" }, + { "position": { "x": 7, "y": 19 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 7, "y": 17 }, "type": "T" }, + { "position": { "x": 7, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" } + ], + "score": 4736, + "simulation_step_index": 5592, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 21, + "mino_stack": [ + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 5, "y": 18 }, "type": "L" }, + { "position": { "x": 6, "y": 18 }, "type": "L" }, + { "position": { "x": 4, "y": 18 }, "type": "Z" }, + { "position": { "x": 2, "y": 19 }, "type": "L" }, + { "position": { "x": 7, "y": 19 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 7, "y": 17 }, "type": "T" }, + { "position": { "x": 7, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 1, "y": 18 }, "type": "J" }, + { "position": { "x": 2, "y": 18 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 3, "y": 19 }, "type": "J" } + ], + "score": 4796, + "simulation_step_index": 5889, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 21, + "mino_stack": [ + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 5, "y": 18 }, "type": "L" }, + { "position": { "x": 6, "y": 18 }, "type": "L" }, + { "position": { "x": 4, "y": 18 }, "type": "Z" }, + { "position": { "x": 2, "y": 19 }, "type": "L" }, + { "position": { "x": 7, "y": 19 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 7, "y": 17 }, "type": "T" }, + { "position": { "x": 7, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 1, "y": 18 }, "type": "J" }, + { "position": { "x": 2, "y": 18 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 3, "y": 19 }, "type": "J" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 3, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 3, "y": 17 }, "type": "O" } + ], + "score": 4856, + "simulation_step_index": 5944, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 21, + "mino_stack": [ + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 5, "y": 18 }, "type": "L" }, + { "position": { "x": 6, "y": 18 }, "type": "L" }, + { "position": { "x": 4, "y": 18 }, "type": "Z" }, + { "position": { "x": 2, "y": 19 }, "type": "L" }, + { "position": { "x": 7, "y": 19 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 7, "y": 17 }, "type": "T" }, + { "position": { "x": 7, "y": 18 }, "type": "T" }, + { "position": { "x": 8, "y": 18 }, "type": "T" }, + { "position": { "x": 1, "y": 18 }, "type": "J" }, + { "position": { "x": 2, "y": 18 }, "type": "J" }, + { "position": { "x": 3, "y": 18 }, "type": "J" }, + { "position": { "x": 3, "y": 19 }, "type": "J" }, + { "position": { "x": 2, "y": 16 }, "type": "O" }, + { "position": { "x": 3, "y": 16 }, "type": "O" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 3, "y": 17 }, "type": "O" }, + { "position": { "x": 8, "y": 16 }, "type": "S" }, + { "position": { "x": 8, "y": 17 }, "type": "S" }, + { "position": { "x": 9, "y": 17 }, "type": "S" }, + { "position": { "x": 9, "y": 18 }, "type": "S" } + ], + "score": 4912, + "simulation_step_index": 6045, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 22, + "mino_stack": [ + { "position": { "x": 9, "y": 19 }, "type": "T" }, + { "position": { "x": 5, "y": 19 }, "type": "S" }, + { "position": { "x": 6, "y": 19 }, "type": "S" }, + { "position": { "x": 0, "y": 19 }, "type": "O" }, + { "position": { "x": 1, "y": 19 }, "type": "O" }, + { "position": { "x": 2, "y": 19 }, "type": "L" }, + { "position": { "x": 7, "y": 19 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 7, "y": 18 }, "type": "T" }, + { "position": { "x": 3, "y": 19 }, "type": "J" }, + { "position": { "x": 2, "y": 17 }, "type": "O" }, + { "position": { "x": 3, "y": 17 }, "type": "O" }, + { "position": { "x": 2, "y": 18 }, "type": "O" }, + { "position": { "x": 3, "y": 18 }, "type": "O" }, + { "position": { "x": 8, "y": 17 }, "type": "S" }, + { "position": { "x": 8, "y": 18 }, "type": "S" }, + { "position": { "x": 9, "y": 18 }, "type": "S" }, + { "position": { "x": 1, "y": 17 }, "type": "Z" }, + { "position": { "x": 1, "y": 18 }, "type": "Z" }, + { "position": { "x": 0, "y": 18 }, "type": "Z" } + ], + "score": 5092, + "simulation_step_index": 6092, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 23, + "mino_stack": [ + { "position": { "x": 7, "y": 19 }, "type": "T" }, + { "position": { "x": 2, "y": 18 }, "type": "O" }, + { "position": { "x": 3, "y": 18 }, "type": "O" }, + { "position": { "x": 2, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 19 }, "type": "O" }, + { "position": { "x": 8, "y": 18 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 9, "y": 19 }, "type": "S" }, + { "position": { "x": 1, "y": 18 }, "type": "Z" }, + { "position": { "x": 1, "y": 19 }, "type": "Z" }, + { "position": { "x": 0, "y": 19 }, "type": "Z" }, + { "position": { "x": 4, "y": 17 }, "type": "I" }, + { "position": { "x": 4, "y": 18 }, "type": "I" }, + { "position": { "x": 4, "y": 19 }, "type": "I" } + ], + "score": 5272, + "simulation_step_index": 6162, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 23, + "mino_stack": [ + { "position": { "x": 7, "y": 19 }, "type": "T" }, + { "position": { "x": 2, "y": 18 }, "type": "O" }, + { "position": { "x": 3, "y": 18 }, "type": "O" }, + { "position": { "x": 2, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 19 }, "type": "O" }, + { "position": { "x": 8, "y": 18 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 9, "y": 19 }, "type": "S" }, + { "position": { "x": 1, "y": 18 }, "type": "Z" }, + { "position": { "x": 1, "y": 19 }, "type": "Z" }, + { "position": { "x": 0, "y": 19 }, "type": "Z" }, + { "position": { "x": 4, "y": 17 }, "type": "I" }, + { "position": { "x": 4, "y": 18 }, "type": "I" }, + { "position": { "x": 4, "y": 19 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "J" }, + { "position": { "x": 4, "y": 16 }, "type": "J" }, + { "position": { "x": 5, "y": 16 }, "type": "J" }, + { "position": { "x": 6, "y": 16 }, "type": "J" } + ], + "score": 5328, + "simulation_step_index": 6212, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 23, + "mino_stack": [ + { "position": { "x": 7, "y": 19 }, "type": "T" }, + { "position": { "x": 2, "y": 18 }, "type": "O" }, + { "position": { "x": 3, "y": 18 }, "type": "O" }, + { "position": { "x": 2, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 19 }, "type": "O" }, + { "position": { "x": 8, "y": 18 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 9, "y": 19 }, "type": "S" }, + { "position": { "x": 1, "y": 18 }, "type": "Z" }, + { "position": { "x": 1, "y": 19 }, "type": "Z" }, + { "position": { "x": 0, "y": 19 }, "type": "Z" }, + { "position": { "x": 4, "y": 17 }, "type": "I" }, + { "position": { "x": 4, "y": 18 }, "type": "I" }, + { "position": { "x": 4, "y": 19 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "J" }, + { "position": { "x": 4, "y": 16 }, "type": "J" }, + { "position": { "x": 5, "y": 16 }, "type": "J" }, + { "position": { "x": 6, "y": 16 }, "type": "J" }, + { "position": { "x": 3, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 13 }, "type": "S" }, + { "position": { "x": 5, "y": 13 }, "type": "S" } + ], + "score": 5380, + "simulation_step_index": 6223, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 23, + "mino_stack": [ + { "position": { "x": 7, "y": 19 }, "type": "T" }, + { "position": { "x": 2, "y": 18 }, "type": "O" }, + { "position": { "x": 3, "y": 18 }, "type": "O" }, + { "position": { "x": 2, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 19 }, "type": "O" }, + { "position": { "x": 8, "y": 18 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 9, "y": 19 }, "type": "S" }, + { "position": { "x": 1, "y": 18 }, "type": "Z" }, + { "position": { "x": 1, "y": 19 }, "type": "Z" }, + { "position": { "x": 0, "y": 19 }, "type": "Z" }, + { "position": { "x": 4, "y": 17 }, "type": "I" }, + { "position": { "x": 4, "y": 18 }, "type": "I" }, + { "position": { "x": 4, "y": 19 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "J" }, + { "position": { "x": 4, "y": 16 }, "type": "J" }, + { "position": { "x": 5, "y": 16 }, "type": "J" }, + { "position": { "x": 6, "y": 16 }, "type": "J" }, + { "position": { "x": 3, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 13 }, "type": "S" }, + { "position": { "x": 5, "y": 13 }, "type": "S" }, + { "position": { "x": 3, "y": 12 }, "type": "I" }, + { "position": { "x": 4, "y": 12 }, "type": "I" }, + { "position": { "x": 5, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 12 }, "type": "I" } + ], + "score": 5424, + "simulation_step_index": 6233, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 23, + "mino_stack": [ + { "position": { "x": 7, "y": 19 }, "type": "T" }, + { "position": { "x": 2, "y": 18 }, "type": "O" }, + { "position": { "x": 3, "y": 18 }, "type": "O" }, + { "position": { "x": 2, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 19 }, "type": "O" }, + { "position": { "x": 8, "y": 18 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 9, "y": 19 }, "type": "S" }, + { "position": { "x": 1, "y": 18 }, "type": "Z" }, + { "position": { "x": 1, "y": 19 }, "type": "Z" }, + { "position": { "x": 0, "y": 19 }, "type": "Z" }, + { "position": { "x": 4, "y": 17 }, "type": "I" }, + { "position": { "x": 4, "y": 18 }, "type": "I" }, + { "position": { "x": 4, "y": 19 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "J" }, + { "position": { "x": 4, "y": 16 }, "type": "J" }, + { "position": { "x": 5, "y": 16 }, "type": "J" }, + { "position": { "x": 6, "y": 16 }, "type": "J" }, + { "position": { "x": 3, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 13 }, "type": "S" }, + { "position": { "x": 5, "y": 13 }, "type": "S" }, + { "position": { "x": 3, "y": 12 }, "type": "I" }, + { "position": { "x": 4, "y": 12 }, "type": "I" }, + { "position": { "x": 5, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 12 }, "type": "I" }, + { "position": { "x": 3, "y": 11 }, "type": "T" }, + { "position": { "x": 4, "y": 11 }, "type": "T" }, + { "position": { "x": 4, "y": 10 }, "type": "T" }, + { "position": { "x": 5, "y": 11 }, "type": "T" } + ], + "score": 5464, + "simulation_step_index": 6242, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 23, + "mino_stack": [ + { "position": { "x": 7, "y": 19 }, "type": "T" }, + { "position": { "x": 2, "y": 18 }, "type": "O" }, + { "position": { "x": 3, "y": 18 }, "type": "O" }, + { "position": { "x": 2, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 19 }, "type": "O" }, + { "position": { "x": 8, "y": 18 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 9, "y": 19 }, "type": "S" }, + { "position": { "x": 1, "y": 18 }, "type": "Z" }, + { "position": { "x": 1, "y": 19 }, "type": "Z" }, + { "position": { "x": 0, "y": 19 }, "type": "Z" }, + { "position": { "x": 4, "y": 17 }, "type": "I" }, + { "position": { "x": 4, "y": 18 }, "type": "I" }, + { "position": { "x": 4, "y": 19 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "J" }, + { "position": { "x": 4, "y": 16 }, "type": "J" }, + { "position": { "x": 5, "y": 16 }, "type": "J" }, + { "position": { "x": 6, "y": 16 }, "type": "J" }, + { "position": { "x": 3, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 13 }, "type": "S" }, + { "position": { "x": 5, "y": 13 }, "type": "S" }, + { "position": { "x": 3, "y": 12 }, "type": "I" }, + { "position": { "x": 4, "y": 12 }, "type": "I" }, + { "position": { "x": 5, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 12 }, "type": "I" }, + { "position": { "x": 3, "y": 11 }, "type": "T" }, + { "position": { "x": 4, "y": 11 }, "type": "T" }, + { "position": { "x": 4, "y": 10 }, "type": "T" }, + { "position": { "x": 5, "y": 11 }, "type": "T" }, + { "position": { "x": 3, "y": 8 }, "type": "Z" }, + { "position": { "x": 4, "y": 8 }, "type": "Z" }, + { "position": { "x": 4, "y": 9 }, "type": "Z" }, + { "position": { "x": 5, "y": 9 }, "type": "Z" } + ], + "score": 5496, + "simulation_step_index": 6252, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 23, + "mino_stack": [ + { "position": { "x": 7, "y": 19 }, "type": "T" }, + { "position": { "x": 2, "y": 18 }, "type": "O" }, + { "position": { "x": 3, "y": 18 }, "type": "O" }, + { "position": { "x": 2, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 19 }, "type": "O" }, + { "position": { "x": 8, "y": 18 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 9, "y": 19 }, "type": "S" }, + { "position": { "x": 1, "y": 18 }, "type": "Z" }, + { "position": { "x": 1, "y": 19 }, "type": "Z" }, + { "position": { "x": 0, "y": 19 }, "type": "Z" }, + { "position": { "x": 4, "y": 17 }, "type": "I" }, + { "position": { "x": 4, "y": 18 }, "type": "I" }, + { "position": { "x": 4, "y": 19 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "J" }, + { "position": { "x": 4, "y": 16 }, "type": "J" }, + { "position": { "x": 5, "y": 16 }, "type": "J" }, + { "position": { "x": 6, "y": 16 }, "type": "J" }, + { "position": { "x": 3, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 13 }, "type": "S" }, + { "position": { "x": 5, "y": 13 }, "type": "S" }, + { "position": { "x": 3, "y": 12 }, "type": "I" }, + { "position": { "x": 4, "y": 12 }, "type": "I" }, + { "position": { "x": 5, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 12 }, "type": "I" }, + { "position": { "x": 3, "y": 11 }, "type": "T" }, + { "position": { "x": 4, "y": 11 }, "type": "T" }, + { "position": { "x": 4, "y": 10 }, "type": "T" }, + { "position": { "x": 5, "y": 11 }, "type": "T" }, + { "position": { "x": 3, "y": 8 }, "type": "Z" }, + { "position": { "x": 4, "y": 8 }, "type": "Z" }, + { "position": { "x": 4, "y": 9 }, "type": "Z" }, + { "position": { "x": 5, "y": 9 }, "type": "Z" }, + { "position": { "x": 3, "y": 7 }, "type": "L" }, + { "position": { "x": 4, "y": 7 }, "type": "L" }, + { "position": { "x": 5, "y": 7 }, "type": "L" }, + { "position": { "x": 5, "y": 6 }, "type": "L" } + ], + "score": 5520, + "simulation_step_index": 6261, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 23, + "mino_stack": [ + { "position": { "x": 7, "y": 19 }, "type": "T" }, + { "position": { "x": 2, "y": 18 }, "type": "O" }, + { "position": { "x": 3, "y": 18 }, "type": "O" }, + { "position": { "x": 2, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 19 }, "type": "O" }, + { "position": { "x": 8, "y": 18 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 9, "y": 19 }, "type": "S" }, + { "position": { "x": 1, "y": 18 }, "type": "Z" }, + { "position": { "x": 1, "y": 19 }, "type": "Z" }, + { "position": { "x": 0, "y": 19 }, "type": "Z" }, + { "position": { "x": 4, "y": 17 }, "type": "I" }, + { "position": { "x": 4, "y": 18 }, "type": "I" }, + { "position": { "x": 4, "y": 19 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "J" }, + { "position": { "x": 4, "y": 16 }, "type": "J" }, + { "position": { "x": 5, "y": 16 }, "type": "J" }, + { "position": { "x": 6, "y": 16 }, "type": "J" }, + { "position": { "x": 3, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 13 }, "type": "S" }, + { "position": { "x": 5, "y": 13 }, "type": "S" }, + { "position": { "x": 3, "y": 12 }, "type": "I" }, + { "position": { "x": 4, "y": 12 }, "type": "I" }, + { "position": { "x": 5, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 12 }, "type": "I" }, + { "position": { "x": 3, "y": 11 }, "type": "T" }, + { "position": { "x": 4, "y": 11 }, "type": "T" }, + { "position": { "x": 4, "y": 10 }, "type": "T" }, + { "position": { "x": 5, "y": 11 }, "type": "T" }, + { "position": { "x": 3, "y": 8 }, "type": "Z" }, + { "position": { "x": 4, "y": 8 }, "type": "Z" }, + { "position": { "x": 4, "y": 9 }, "type": "Z" }, + { "position": { "x": 5, "y": 9 }, "type": "Z" }, + { "position": { "x": 3, "y": 7 }, "type": "L" }, + { "position": { "x": 4, "y": 7 }, "type": "L" }, + { "position": { "x": 5, "y": 7 }, "type": "L" }, + { "position": { "x": 5, "y": 6 }, "type": "L" }, + { "position": { "x": 3, "y": 4 }, "type": "J" }, + { "position": { "x": 3, "y": 5 }, "type": "J" }, + { "position": { "x": 4, "y": 5 }, "type": "J" }, + { "position": { "x": 5, "y": 5 }, "type": "J" } + ], + "score": 5536, + "simulation_step_index": 6268, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 23, + "mino_stack": [ + { "position": { "x": 7, "y": 19 }, "type": "T" }, + { "position": { "x": 2, "y": 18 }, "type": "O" }, + { "position": { "x": 3, "y": 18 }, "type": "O" }, + { "position": { "x": 2, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 19 }, "type": "O" }, + { "position": { "x": 8, "y": 18 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 9, "y": 19 }, "type": "S" }, + { "position": { "x": 1, "y": 18 }, "type": "Z" }, + { "position": { "x": 1, "y": 19 }, "type": "Z" }, + { "position": { "x": 0, "y": 19 }, "type": "Z" }, + { "position": { "x": 4, "y": 17 }, "type": "I" }, + { "position": { "x": 4, "y": 18 }, "type": "I" }, + { "position": { "x": 4, "y": 19 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "J" }, + { "position": { "x": 4, "y": 16 }, "type": "J" }, + { "position": { "x": 5, "y": 16 }, "type": "J" }, + { "position": { "x": 6, "y": 16 }, "type": "J" }, + { "position": { "x": 3, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 13 }, "type": "S" }, + { "position": { "x": 5, "y": 13 }, "type": "S" }, + { "position": { "x": 3, "y": 12 }, "type": "I" }, + { "position": { "x": 4, "y": 12 }, "type": "I" }, + { "position": { "x": 5, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 12 }, "type": "I" }, + { "position": { "x": 3, "y": 11 }, "type": "T" }, + { "position": { "x": 4, "y": 11 }, "type": "T" }, + { "position": { "x": 4, "y": 10 }, "type": "T" }, + { "position": { "x": 5, "y": 11 }, "type": "T" }, + { "position": { "x": 3, "y": 8 }, "type": "Z" }, + { "position": { "x": 4, "y": 8 }, "type": "Z" }, + { "position": { "x": 4, "y": 9 }, "type": "Z" }, + { "position": { "x": 5, "y": 9 }, "type": "Z" }, + { "position": { "x": 3, "y": 7 }, "type": "L" }, + { "position": { "x": 4, "y": 7 }, "type": "L" }, + { "position": { "x": 5, "y": 7 }, "type": "L" }, + { "position": { "x": 5, "y": 6 }, "type": "L" }, + { "position": { "x": 3, "y": 4 }, "type": "J" }, + { "position": { "x": 3, "y": 5 }, "type": "J" }, + { "position": { "x": 4, "y": 5 }, "type": "J" }, + { "position": { "x": 5, "y": 5 }, "type": "J" }, + { "position": { "x": 4, "y": 3 }, "type": "O" }, + { "position": { "x": 5, "y": 3 }, "type": "O" }, + { "position": { "x": 4, "y": 4 }, "type": "O" }, + { "position": { "x": 5, "y": 4 }, "type": "O" } + ], + "score": 5548, + "simulation_step_index": 6277, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 23, + "mino_stack": [ + { "position": { "x": 7, "y": 19 }, "type": "T" }, + { "position": { "x": 2, "y": 18 }, "type": "O" }, + { "position": { "x": 3, "y": 18 }, "type": "O" }, + { "position": { "x": 2, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 19 }, "type": "O" }, + { "position": { "x": 8, "y": 18 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 9, "y": 19 }, "type": "S" }, + { "position": { "x": 1, "y": 18 }, "type": "Z" }, + { "position": { "x": 1, "y": 19 }, "type": "Z" }, + { "position": { "x": 0, "y": 19 }, "type": "Z" }, + { "position": { "x": 4, "y": 17 }, "type": "I" }, + { "position": { "x": 4, "y": 18 }, "type": "I" }, + { "position": { "x": 4, "y": 19 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "J" }, + { "position": { "x": 4, "y": 16 }, "type": "J" }, + { "position": { "x": 5, "y": 16 }, "type": "J" }, + { "position": { "x": 6, "y": 16 }, "type": "J" }, + { "position": { "x": 3, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 13 }, "type": "S" }, + { "position": { "x": 5, "y": 13 }, "type": "S" }, + { "position": { "x": 3, "y": 12 }, "type": "I" }, + { "position": { "x": 4, "y": 12 }, "type": "I" }, + { "position": { "x": 5, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 12 }, "type": "I" }, + { "position": { "x": 3, "y": 11 }, "type": "T" }, + { "position": { "x": 4, "y": 11 }, "type": "T" }, + { "position": { "x": 4, "y": 10 }, "type": "T" }, + { "position": { "x": 5, "y": 11 }, "type": "T" }, + { "position": { "x": 3, "y": 8 }, "type": "Z" }, + { "position": { "x": 4, "y": 8 }, "type": "Z" }, + { "position": { "x": 4, "y": 9 }, "type": "Z" }, + { "position": { "x": 5, "y": 9 }, "type": "Z" }, + { "position": { "x": 3, "y": 7 }, "type": "L" }, + { "position": { "x": 4, "y": 7 }, "type": "L" }, + { "position": { "x": 5, "y": 7 }, "type": "L" }, + { "position": { "x": 5, "y": 6 }, "type": "L" }, + { "position": { "x": 3, "y": 4 }, "type": "J" }, + { "position": { "x": 3, "y": 5 }, "type": "J" }, + { "position": { "x": 4, "y": 5 }, "type": "J" }, + { "position": { "x": 5, "y": 5 }, "type": "J" }, + { "position": { "x": 4, "y": 3 }, "type": "O" }, + { "position": { "x": 5, "y": 3 }, "type": "O" }, + { "position": { "x": 4, "y": 4 }, "type": "O" }, + { "position": { "x": 5, "y": 4 }, "type": "O" }, + { "position": { "x": 3, "y": 2 }, "type": "S" }, + { "position": { "x": 4, "y": 2 }, "type": "S" }, + { "position": { "x": 4, "y": 1 }, "type": "S" }, + { "position": { "x": 5, "y": 1 }, "type": "S" }, + { "position": { "x": 3, "y": 0 }, "type": "I" }, + { "position": { "x": 4, "y": 0 }, "type": "I" }, + { "position": { "x": 5, "y": 0 }, "type": "I" }, + { "position": { "x": 6, "y": 0 }, "type": "I" } + ], + "score": 5552, + "simulation_step_index": 6287, + "tetrion_index": 0 + }, + { + "level": 2, + "lines_cleared": 23, + "mino_stack": [ + { "position": { "x": 7, "y": 19 }, "type": "T" }, + { "position": { "x": 2, "y": 18 }, "type": "O" }, + { "position": { "x": 3, "y": 18 }, "type": "O" }, + { "position": { "x": 2, "y": 19 }, "type": "O" }, + { "position": { "x": 3, "y": 19 }, "type": "O" }, + { "position": { "x": 8, "y": 18 }, "type": "S" }, + { "position": { "x": 8, "y": 19 }, "type": "S" }, + { "position": { "x": 9, "y": 19 }, "type": "S" }, + { "position": { "x": 1, "y": 18 }, "type": "Z" }, + { "position": { "x": 1, "y": 19 }, "type": "Z" }, + { "position": { "x": 0, "y": 19 }, "type": "Z" }, + { "position": { "x": 4, "y": 17 }, "type": "I" }, + { "position": { "x": 4, "y": 18 }, "type": "I" }, + { "position": { "x": 4, "y": 19 }, "type": "I" }, + { "position": { "x": 4, "y": 15 }, "type": "J" }, + { "position": { "x": 4, "y": 16 }, "type": "J" }, + { "position": { "x": 5, "y": 16 }, "type": "J" }, + { "position": { "x": 6, "y": 16 }, "type": "J" }, + { "position": { "x": 3, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 14 }, "type": "S" }, + { "position": { "x": 4, "y": 13 }, "type": "S" }, + { "position": { "x": 5, "y": 13 }, "type": "S" }, + { "position": { "x": 3, "y": 12 }, "type": "I" }, + { "position": { "x": 4, "y": 12 }, "type": "I" }, + { "position": { "x": 5, "y": 12 }, "type": "I" }, + { "position": { "x": 6, "y": 12 }, "type": "I" }, + { "position": { "x": 3, "y": 11 }, "type": "T" }, + { "position": { "x": 4, "y": 11 }, "type": "T" }, + { "position": { "x": 4, "y": 10 }, "type": "T" }, + { "position": { "x": 5, "y": 11 }, "type": "T" }, + { "position": { "x": 3, "y": 8 }, "type": "Z" }, + { "position": { "x": 4, "y": 8 }, "type": "Z" }, + { "position": { "x": 4, "y": 9 }, "type": "Z" }, + { "position": { "x": 5, "y": 9 }, "type": "Z" }, + { "position": { "x": 3, "y": 7 }, "type": "L" }, + { "position": { "x": 4, "y": 7 }, "type": "L" }, + { "position": { "x": 5, "y": 7 }, "type": "L" }, + { "position": { "x": 5, "y": 6 }, "type": "L" }, + { "position": { "x": 3, "y": 4 }, "type": "J" }, + { "position": { "x": 3, "y": 5 }, "type": "J" }, + { "position": { "x": 4, "y": 5 }, "type": "J" }, + { "position": { "x": 5, "y": 5 }, "type": "J" }, + { "position": { "x": 4, "y": 3 }, "type": "O" }, + { "position": { "x": 5, "y": 3 }, "type": "O" }, + { "position": { "x": 4, "y": 4 }, "type": "O" }, + { "position": { "x": 5, "y": 4 }, "type": "O" }, + { "position": { "x": 3, "y": 2 }, "type": "S" }, + { "position": { "x": 4, "y": 2 }, "type": "S" }, + { "position": { "x": 4, "y": 1 }, "type": "S" }, + { "position": { "x": 5, "y": 1 }, "type": "S" }, + { "position": { "x": 3, "y": 0 }, "type": "I" }, + { "position": { "x": 4, "y": 0 }, "type": "I" }, + { "position": { "x": 5, "y": 0 }, "type": "I" }, + { "position": { "x": 6, "y": 0 }, "type": "I" } + ], + "score": 5552, + "simulation_step_index": 6287, + "tetrion_index": 0 + } + ], + "tetrion_headers": [{ "seed": 1710635321166006, "starting_level": 0 }], + "version": 1 +} diff --git a/wrapper/javascript/tests/files/incorrect.rec b/wrapper/javascript/tests/files/incorrect.rec new file mode 100644 index 00000000..9dcd1720 Binary files /dev/null and b/wrapper/javascript/tests/files/incorrect.rec differ diff --git a/wrapper/javascript/tests/raw.test.ts b/wrapper/javascript/tests/raw.test.ts new file mode 100644 index 00000000..06af3a94 --- /dev/null +++ b/wrapper/javascript/tests/raw.test.ts @@ -0,0 +1,127 @@ +import { expect } from "@jest/globals" +import path from "path" +import fs from "fs" + +function fail(reason = "fail was called in a test."): never { + throw new Error(reason) +} + +global.fail = fail + +function getFilePath(name: string): string { + return path.join(__dirname, `files`, name) +} + +const rootDir = path.join(__dirname, "..") +const oopetris = require("node-gyp-build")(rootDir) + +describe("isRecordingFile", () => { + it("should throw an error, when no first argument was given", async () => { + try { + oopetris.isRecordingFile() + + fail("it should not reach here") + } catch (e) { + expect((e as any).toString()).toEqual( + "TypeError: Wrong number of arguments" + ) + } + }) + + it("should throw an error, when the first argument is not a string", async () => { + try { + oopetris.isRecordingFile(1) + + fail("it should not reach here") + } catch (e) { + expect((e as any).toString()).toEqual( + "TypeError: First argument must be string" + ) + } + }) + + it("should return false, when the file doesn't exist", async () => { + const file = getFilePath("NON-EXISTENT.rec") + expect(fs.existsSync(file)).toBe(false) + + const result = oopetris.isRecordingFile(file) + expect(result).toBe(false) + }) +}) + +describe("getInformation", () => { + it("should throw an error, when no first argument was given", async () => { + try { + oopetris.getInformation() + + fail("it should not reach here") + } catch (e) { + expect((e as any).toString()).toEqual( + "TypeError: Wrong number of arguments" + ) + } + }) + + it("should throw an error, when the first argument is not a string", async () => { + try { + oopetris.getInformation(1) + + fail("it should not reach here") + } catch (e) { + expect((e as any).toString()).toEqual( + "TypeError: First argument must be string" + ) + } + }) + + it("should throw an error, when the file doesn't exist", async () => { + const file = getFilePath("NON-EXISTENT.rec") + expect(fs.existsSync(file)).toBe(false) + + try { + oopetris.getInformation(file) + + fail("it should not reach here") + } catch (e) { + expect((e as any).toString()).toEqual( + `Error: File '${file}' doesn't exist!` + ) + } + }) +}) + +describe("exported properties", () => { + it("should only have known properties", async () => { + const expectedKeys = [ + "isRecordingFile", + "getInformation", + "version", + "properties", + ] + + const keys = Object.keys(oopetris) + expect(keys).toStrictEqual(expectedKeys) + }) + + it("should have the expected properties", async () => { + const expectedProperties: Record = { + isRecordingFile: () => {}, + getInformation: () => {}, + version: "0.5.6", + properties: { gridProperties: { height: 20, width: 10 } }, + } + + for (const key in expectedProperties) { + const value = expectedProperties[key] + const rawValue = oopetris[key] + + if (typeof value === "string") { + expect(value).toBe(rawValue) + } else if (typeof value === "function") { + expect(rawValue).toStrictEqual(expect.any(Function)) + } else { + expect(value).toMatchObject(rawValue) + } + } + }) +}) diff --git a/wrapper/javascript/tests/test.spec.ts b/wrapper/javascript/tests/test.spec.ts new file mode 100644 index 00000000..7c088283 --- /dev/null +++ b/wrapper/javascript/tests/test.spec.ts @@ -0,0 +1,87 @@ +import { RecordingsUtility } from "../src/ts/index" +import { expect } from "@jest/globals" +import path from "path" +import fs from "fs" + +function fail(reason = "fail was called in a test."): never { + throw new Error(reason) +} + +global.fail = fail + +function getFilePath(name: string): string { + return path.join(__dirname, `files`, name) +} + +describe("isRecordingFile: works as expected", () => { + it("should return false for non existent file", async () => { + const file = getFilePath("NON-EXISTENT.rec") + + expect(fs.existsSync(file)).toBe(false) + + const result = RecordingsUtility.isRecordingFile(file) + expect(result).toBe(false) + }) + + it("should return false for incorrect file", async () => { + const file = getFilePath("incorrect.rec") + + expect(fs.existsSync(file)).toBe(true) + + const result = RecordingsUtility.isRecordingFile(file) + expect(result).toBe(false) + }) + + it("should return true for correct file", async () => { + const file = getFilePath("correct.rec") + + expect(fs.existsSync(file)).toBe(true) + + const result = RecordingsUtility.isRecordingFile(file) + expect(result).toBe(true) + }) +}) + +describe("getInformation: works as expected", () => { + it("should return null for non existent file", async () => { + const file = getFilePath("NON-EXISTENT.rec") + + expect(fs.existsSync(file)).toBe(false) + + const result = RecordingsUtility.getInformation(file) + expect(result).toBe(null) + }) + + it("should return null for incorrect file", async () => { + const file = getFilePath("incorrect.rec") + + expect(fs.existsSync(file)).toBe(true) + + const result = RecordingsUtility.getInformation(file) + expect(result).toBe(null) + }) + + it("should return an object for correct file", async () => { + const file = getFilePath("correct.rec") + + expect(fs.existsSync(file)).toBe(true) + + const result = RecordingsUtility.getInformation(file) + expect(result).not.toBe(null) + }) + + it("should return correct object for correct file", async () => { + const file = getFilePath("correct.rec") + expect(fs.existsSync(file)).toBe(true) + + const serializedFile = getFilePath("correct.serialized.json") + expect(fs.existsSync(serializedFile)).toBe(true) + + const correctResult = JSON.parse( + fs.readFileSync(serializedFile).toString() + ) + + const result = RecordingsUtility.getInformation(file) + expect(result).toMatchObject(correctResult) + }) +}) diff --git a/wrapper/javascript/tsconfig.json b/wrapper/javascript/tsconfig.json new file mode 100644 index 00000000..4137b627 --- /dev/null +++ b/wrapper/javascript/tsconfig.json @@ -0,0 +1,26 @@ +{ + "include": ["src/ts/*.ts"], + "exclude": ["node_modules"], + "compilerOptions": { + "target": "es2021", + "module": "CommonJS", + "moduleResolution": "node", + "lib": ["es2021"], + "outDir": "dist", + "allowJs": true, + "rootDir": "src/ts/", + "strict": true, + "noImplicitAny": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "declaration": true + }, + "rules": { + "triple-equals": true, + "curly": true, + "strict-boolean-expressions": true, + "no-unnecessary-condition": true + } +}