From 347109673ac2ebe462e68c0f16f3976053dc2464 Mon Sep 17 00:00:00 2001 From: OceanRamen Date: Sun, 14 Jun 2026 02:16:16 +0100 Subject: [PATCH 1/5] Add core functionality for the Immolate game - Implemented main logic in main.cpp, including various filters for game mechanics. - Created rng.cpp and rng.hpp to define item sources and random types. - Developed search.cpp and search.hpp for handling search operations with multithreading support. - Added seed.cpp and seed.hpp for seed management and conversion. - Introduced util.cpp and util.hpp for utility functions, including random number generation and hashing. - Established a benchmark system to evaluate performance across different filters and scenarios. --- .github/copilot-instructions.md | 25 + .github/ui_modding.md | 136 ++ Core/Brainstorm.lua | 827 ++++--- UI/ui.lua | 492 +++-- immolate/functions.cpp | 11 + immolate/functions.hpp | 639 ++++++ immolate/immolate.cpp | 246 +++ immolate/immolate.hpp | 143 ++ immolate/instance.hpp | 135 ++ immolate/items.cpp | 356 +++ immolate/items.hpp | 3578 +++++++++++++++++++++++++++++++ immolate/main.cpp | 282 +++ immolate/rng.cpp | 67 + immolate/rng.hpp | 75 + immolate/search.cpp | 0 immolate/search.hpp | 111 + immolate/seed.cpp | 112 + immolate/seed.hpp | 49 + immolate/util.cpp | 178 ++ immolate/util.hpp | 46 + 20 files changed, 6961 insertions(+), 547 deletions(-) create mode 100644 .github/copilot-instructions.md create mode 100644 .github/ui_modding.md create mode 100644 immolate/functions.cpp create mode 100644 immolate/functions.hpp create mode 100644 immolate/immolate.cpp create mode 100644 immolate/immolate.hpp create mode 100644 immolate/instance.hpp create mode 100644 immolate/items.cpp create mode 100644 immolate/items.hpp create mode 100644 immolate/main.cpp create mode 100644 immolate/rng.cpp create mode 100644 immolate/rng.hpp create mode 100644 immolate/search.cpp create mode 100644 immolate/search.hpp create mode 100644 immolate/seed.cpp create mode 100644 immolate/seed.hpp create mode 100644 immolate/util.cpp create mode 100644 immolate/util.hpp diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..14cb8a5 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,25 @@ +# Brainstorm Copilot Instructions + +- **Purpose**: Balatro mod that automates rerolls to find seeds matching user-selected filters (tags, vouchers, packs) and restarts runs accordingly. Main logic lives in [Core/Brainstorm.lua](Core/Brainstorm.lua); a working options tab is in [UI/ui.lua](UI/ui.lua); older prototypes live under [Debug](Debug). +- **Load flow**: The mod is injected via [lovely.toml](lovely.toml): `nativefs.lua` is loaded before `main.lua`, `Core/Brainstorm.lua` is appended to `main.lua`, and `Brainstorm.init()` runs after the profile load patch in `game.lua`. +- **Globals & patching**: `Brainstorm.init()` discovers its own path, loads config, and executes `UI/ui.lua`. Core behaviors monkeypatch `Controller:key_press_update`, `Game:update`, and `create_UIBox_round_scores_row` while calling the originals; keep these reference patterns when extending patches. +- **Config storage**: Defaults sit in `Brainstorm.DEFAULT_CONFIG` and are serialized to `config.lua` via `STR_PACK/STR_UNPACK` using `nativefs`. Config load merges defaults and drops dead fields (e.g., the old `keybind_autoreroll`); when adding settings, seed defaults, keep backward compatibility, and call `Brainstorm.writeConfig()` (UI exit already does this). +- **Controls**: Holding the modifier (`lctrl` by default) + `r` triggers a manual reroll; modifier + `a` toggles auto-reroll. `keybind_autoreroll` is a plain `love.keyboard.isDown` check; respect current bindings when changing input logic. +- **Manual reroll path**: `Brainstorm.reroll()` deletes and restarts the run with the current stake/seed/challenge context, preserving seeded/challenge flags on `G.GAME`. Keep these assignments if you alter reroll behavior. +- **Auto-reroll loop**: `Game:update` drives `Brainstorm.autoReroll()` on a dynamic interval derived from `ar_prefs.spf_int` (1000 ⇒ 0.01s, 500 ⇒ 0.02s) while `Brainstorm.ar_active` is true; a status text is shown after 60 frames via `Brainstorm.attentionText()` and cleared in `removeAttentionText()`. +- **Seed search implementation**: `Brainstorm.autoReroll()` FFI-loads `Immolate.dll` and calls `brainstorm(seed, pack, tag, souls)` (DLL ignores voucher/observatory/perkeo in current builds); keep the Lua `ffi.cdef` in sync with the DLL you ship. It writes `G.GAME.used_filter` and `filter_info` for downstream display; keep these fields if you change the flow. If the DLL is missing or lacks the symbol, auto-reroll now disables itself instead of crashing. +- **Immolate source**: The C++ sources live in [immolate/](immolate); `immolate.cpp` wires globals `BRAINSTORM_PACK/TAG/SOULS` into a `Search` over seeds and exports `brainstorm`/`brainstorm_cpp` plus `free_result`. Search walks blocks of 1,000,000 seeds, is multi-threaded, and stops on the first seed meeting the filter when `exitOnFind` is true. Filters currently check first-pack type, first-tag match, and require N souls from the first Mega Arcana pack; add new filters there if the DLL needs to evolve. +- **DLL version drift**: The shipped `Immolate.dll` may be older than the sources under [immolate/](immolate). Treat the C++ tree as reference, but verify exports/signatures against the actual DLL before changing `ffi.cdef` or call sites. +- **Filters & prefs**: Search options live under `Brainstorm.config.ar_filters` (pack, tag, voucher ids/names, soul skips, instant observatory/perkeo) and `ar_prefs` (seeds-per-frame). UI callbacks in [UI/ui.lua](UI/ui.lua) update these and immediately persist. +- **UI integration**: `create_tabs` is patched to append a "Brainstorm" tab (only when `tab_h == 7.05`, matching the options screen). It uses base-game helpers (`create_option_cycle`, `create_toggle`) to render cycles/toggles. `G.FUNCS.exit_overlay_menu` is wrapped to save config on close. +- **Display tweaks**: `create_UIBox_round_scores_row` colors the seed label red when seeded and blue when a filtered seed was used; preserve this branch if altering round score rows. +- **Vendored dependencies**: [nativefs.lua](nativefs.lua) is a bundled filesystem shim (LuaJIT+FFI); avoid modifications unless fixing I/O. [Immolate.dll](Immolate.dll) is an external binary providing the seed search routine. [steamodded_compat.lua](steamodded_compat.lua) only carries mod metadata. +- **Styling & formatting**: Lua uses Stylua ([stylua.toml](stylua.toml), [\.vscode/settings.json](.vscode/settings.json)) with 2-space indents, 80-col preference, and double-quote bias. See [style_guide.md](style_guide.md) for naming (snake_case vars, camelCase functions, PascalCase classes) and general conventions. +- **Adding settings**: 1) Extend `Brainstorm.config` defaults. 2) Wire UI controls in [UI/ui.lua](UI/ui.lua) with `G.FUNCS.change_*` callbacks that update config and call `Brainstorm.writeConfig()`. 3) Ensure new values are passed into `autoReroll()` if they influence search. +- **Touching globals**: Many helpers assume global tables (`G`, `G.FUNCS`, `G.UIT`). Cache originals before wrapping and re-call them to avoid breaking base game behavior. +- **Debug folder**: [Debug/settings.lua](Debug/settings.lua) and [Debug/ui.lua](Debug/ui.lua) are experimental layouts; treat them as reference prototypes, not the active UI. +- **Testing workflow**: No automated tests. Validation is manual by running Balatro/Steamodded with this mod enabled; exercise keybinds and the "Brainstorm" options tab to confirm config persistence and reroll behavior. +- **Release/versioning**: Current version string is `Brainstorm v2.2.0-alpha` in `Brainstorm.VERSION`; update this and `lovely.toml`/headers if you ship releases. +- **Safety tips**: Avoid touching `config.lua` by hand; prefer the UI or defaults. Keep non-ASCII out of sources. Do not remove calls to `Brainstorm.writeConfig()` around UI exits. + +If any part of this feels unclear or incomplete, tell me which sections to expand or examples to add. diff --git a/.github/ui_modding.md b/.github/ui_modding.md new file mode 100644 index 0000000..7118845 --- /dev/null +++ b/.github/ui_modding.md @@ -0,0 +1,136 @@ +# Balatro UI Modding Guide + +This document explains how to build custom UI using Balatro’s built-in UI engine, with a focus on creating custom settings tabs and reusable controls. + +## Mental model + +- The UI is a tree of nodes built from plain Lua tables. You pass that tree into `UIBox{definition=..., config=...}` to instantiate it. +- Node types are enumerated in `G.UIT` (see `globals.lua`): + - `T` text, `B` box, `C` column, `R` row, `O` object (Sprite/DynaText/etc.), `ROOT`, `S` slider, `I` input. +- Every node table uses keys: + - `n` (required): the node type from `G.UIT.*`. + - `config` (optional): alignment, padding, colors, callbacks, refs, ids. + - `nodes` (optional): array of child nodes. +- Layout is resolved by `UIElement:set_alignments` (engine/ui.lua). `config.align` letters: `c` center vertically, `m` center horizontally, `b` bottom, `r` right. Top/left are default. `padding` defaults to `G.UIT.padding`. +- `ref_table` + `ref_value` binds UI to live data; text and object refs auto-refresh when values change. +- Interactivity uses `config.button = 'name'` to invoke `G.FUNCS.name`. Helpers wire `hover`, `shadow`, etc. for you. + +## Useful engine locations + +- Node enums: `globals.lua` (`self.UIT = { T=1, B=2, C=3, R=4, O=5, ROOT=7, S=8, I=9 }`). +- UIBox creation, sizing, alignment: `engine/ui.lua` (class `UIBox`, `UIElement`). +- Input/callbacks for buttons, sliders, toggles, option cycles: `functions/button_callbacks.lua`. +- Prefab builders (sliders, toggles, option cycles, tabs, buttons): `functions/UI_definitions.lua` near the helper definitions. +- Settings tabs pattern: `create_UIBox_settings` and `G.UIDEF.settings_tab` in `functions/UI_definitions.lua`. + +## Core building blocks (prefabs) + +Use these helpers instead of raw nodes where possible: + +- **Button**: `UIBox_button(args)` → clickable pill. + + - Args: `button` (G.FUNCS name), `label` array, `colour`, `minw/minh`, `choice/chosen` for toggle-style buttons, `focus_args` for controller nav. + +- **Slider**: `create_slider(args)` → drag or discrete slider bound to `ref_table/ref_value`. + + - Required: `ref_table`, `ref_value`, `min`, `max` (numbers). Optional: `label`, `w/h`, `callback`, `decimal_places`, `colour`. + - Behavior implemented by `G.FUNCS.slider` and `G.FUNCS.slider_descreet`. + +- **Toggle**: `create_toggle(args)` → checkbox-style toggle bound to `ref_table/ref_value`. + + - Required: `ref_table`, `ref_value`, `label`. + - Optional: `callback` (runs on change), `info` (array of extra text lines), `active_colour`, `inactive_colour`, `scale`. + - Behavior: `G.FUNCS.toggle_button` and `G.FUNCS.toggle`. + +- **Option Cycle**: `create_option_cycle(args)` → left/right cycle with pips. + + - Required: `options` (array), `current_option` (1-based), `opt_callback` (G.FUNCS name or nil). + - Optional: `label`, `info`, `w/h`, `scale`, `cycle_shoulders` (adds shoulder prompts), `no_pips`. + - Behavior: `G.FUNCS.option_cycle` updates `current_option` and `current_option_val`, fires `opt_callback`. + +- **Tabs**: `create_tabs(args)` → tab strip + content area. + + - Each tab entry: `{ label=..., chosen=bool, tab_definition_function=fn, tab_definition_function_args=... }`. + - `create_tabs` instantiates the chosen tab’s definition into `tab_contents`. + - Useful args: `tab_h`, `tab_w`, `tab_alignment`, `snap_to_nav`, `no_shoulders`. + +- **Overlay shell**: `create_UIBox_generic_options(args)` → modal frame with optional back button and infotip slot. + + - Args: `contents` (array or single node), `back_func`, `colour`, `bg_colour`, `outline_colour`, `no_back`, `snap_back`. + +- **Dyn container**: `UIBox_dyn_container(inner_table, horizontal, colour_override, background_override, flipped, padding)` → framed grouping block. + +- **Text input**: `create_text_input(args)` for simple keyboard input; binds to `ref_table/ref_value` with max length, prompt text. + +## Making a custom settings tab (example) + +Add a new tab alongside existing ones in `create_UIBox_settings` and define its builder. Example: + +```lua +-- 1) Define your tab builder (anywhere after G.UIDEF exists) +function G.UIDEF.settings_tab_fancy() + return {n=G.UIT.ROOT, config={align="cm", padding=0.05, colour=G.C.CLEAR}, nodes={ + create_toggle({label="Enable Fancy Mode", ref_table=G.SETTINGS, ref_value="fancy_mode", callback=function(val) + G.FUNCS.apply_fancy_mode(val) + end}), + create_slider({label="Fancy Intensity", w=4, h=0.4, ref_table=G.SETTINGS, ref_value="fancy_intensity", min=0, max=100, callback="apply_fancy_intensity"}), + create_option_cycle({label="Fancy Style", options={"Soft","Bold","Loud"}, current_option=1, opt_callback="set_fancy_style"}) + }} +end + +-- 2) Insert the tab into the settings tabs list (in create_UIBox_settings) +tabs[#tabs+1] = { + label = "Fancy", + tab_definition_function = G.UIDEF.settings_tab_fancy, + tab_definition_function_args = nil +} +``` + +Then implement the callbacks you referenced (e.g., `G.FUNCS.apply_fancy_mode`, `G.FUNCS.apply_fancy_intensity`, `G.FUNCS.set_fancy_style`). They’ll receive the cycle/slider/toggle configs or values per the existing callbacks in `button_callbacks.lua`. + +## Making a standalone modal/panel + +1. Build your content nodes using rows/cols and prefabs: + +```lua +local content = { + UIBox_button({label={"Do Thing"}, button="my_action", minw=3}), + create_toggle({label="Flag", ref_table=G.SETTINGS, ref_value="my_flag"}), + create_slider({label="Value", w=4, h=0.4, ref_table=G.SETTINGS, ref_value="my_val", min=0, max=10}) +} +``` + +1. Wrap in `create_UIBox_generic_options({contents = content, back_func = "exit_overlay_menu"})` and pass that as the `definition` to a new `UIBox` to show the overlay. + +## Binding data and IDs + +- Use `ref_table/ref_value` on `T` nodes to auto-update text when values change. +- Use `id` in `config` to fetch elements later with `UIBox:get_UIE_by_ID(id)`. +- Objects (`n=G.UIT.O`) can wrap `Sprite`, `DynaText`, or another `UIBox` via `config.object`. + +## Controller focus + +- `focus_args` on interactive nodes controls navigation; helpers set sensible defaults: sliders (`type='slider'`), cycles (`type='cycle'`), tabs (`type='tab'`), buttons (`nav='wide'`, etc.). +- `snap_to_nav=true` on `create_tabs` helps initial focus within overlays. + +## Gotchas + +- Buttons need `hover=true` (helpers do this) and a `button` string to trigger a callback. +- If you bind text to changing data, a length change triggers a layout recalc; avoid `no_recalc` unless you really need fixed width. +- `id` values must be unique within a UIBox tree. +- Colors are premultiplied alpha tables; `colour[4]` near zero hides the element. + +## Where to look in code + +- Prefab helpers: `functions/UI_definitions.lua` (slider/toggle/cycle/tabs/buttons). +- Input + callbacks: `functions/button_callbacks.lua`. +- Core UI tree + layout: `engine/ui.lua`. +- Node enums/constants: `globals.lua` (G.UIT, colors in G.C). + +## Extending further + +- You can nest `UIBox` instances via `n=G.UIT.O` with `config.object = UIBox{definition=...}` to embed sub-UIs. +- `UIBox_dyn_container` gives quick framed blocks for grouped options. +- Use `create_text_input` if you need player text entry (e.g., seeds, names). + +Keep everything data-first: assemble tables, wire callbacks in `G.FUNCS`, and let the engine handle layout and interaction. diff --git a/Core/Brainstorm.lua b/Core/Brainstorm.lua index bba8b7c..f307cb3 100644 --- a/Core/Brainstorm.lua +++ b/Core/Brainstorm.lua @@ -1,5 +1,6 @@ local lovely = require("lovely") local nfs = require("nativefs") +local ffi = require("ffi") Brainstorm = {} @@ -7,375 +8,561 @@ Brainstorm.VERSION = "Brainstorm v2.2.0-alpha" Brainstorm.SMODS = nil -Brainstorm.config = { - enable = true, - keybind_autoreroll = "r", - keybinds = { - options = "t", - modifier = "lctrl", - f_reroll = "r", - a_reroll = "a", - }, - ar_filters = { - pack = {}, - pack_id = 1, - voucher_name = "", - voucher_id = 1, - tag_name = "tag_charm", - tag_id = 2, - soul_skip = 1, - inst_observatory = false, - inst_perkeo = false, - }, - ar_prefs = { - spf_id = 3, - spf_int = 1000, - }, +Brainstorm.DEFAULT_CONFIG = { + enable = true, + keybinds = { + options = "t", + modifier = "lctrl", + f_reroll = "r", + a_reroll = "a", + }, + ar_filters = { + pack = {}, + pack_id = 1, + voucher_name = "", + voucher_id = 1, + tag_name = "tag_charm", + tag_id = 2, + soul_skip = 1, + inst_observatory = false, + inst_perkeo = false, + legendary_choice = "None", + legendary_id = 1, + }, + ar_prefs = { + spf_id = 3, + spf_int = 1000, + }, + active_filter_id = nil, + debug = false, } +Brainstorm.config = {} +Brainstorm.filters = {} +Brainstorm.filters_list = {} + Brainstorm.ar_timer = 0 Brainstorm.ar_frames = 0 Brainstorm.ar_text = nil Brainstorm.ar_active = false Brainstorm.AR_INTERVAL = 0.01 +Brainstorm.immolate = nil +Brainstorm.immolate_cdef = false +Brainstorm.immolate_status = "unloaded" -- Cache frequently used functions local math_abs = math.abs local string_format = string.format local string_lower = string.lower +local function debugLog(...) + if Brainstorm.config and Brainstorm.config.debug then + print("Brainstorm:", ...) + end +end + local function findBrainstormDirectory(directory) - for _, item in ipairs(nfs.getDirectoryItems(directory)) do - local itemPath = directory .. "/" .. item - if - nfs.getInfo(itemPath, "directory") - and string_lower(item):find("brainstorm") - then - return itemPath - end - end - return nil + for _, item in ipairs(nfs.getDirectoryItems(directory)) do + local itemPath = directory .. "/" .. item + if nfs.getInfo(itemPath, "directory") and string_lower(item):find("brainstorm") then + return itemPath + end + end + return nil +end + +local function ensureImmolateLoaded() + if Brainstorm.immolate then + Brainstorm.immolate_status = "loaded" + return true + end + + if not Brainstorm.immolate_cdef then + ffi.cdef( + [[const char* brainstorm(const char* seed, const char* voucher, const char* pack, const char* tag, double souls, bool observatory, bool perkeo);]] + ) + ffi.cdef([[const char* brainstorm_query(const char* seed, const char* query_json);]]) + ffi.cdef([[void free_result(const char* result);]]) + Brainstorm.immolate_cdef = true + end + + local ok, lib = pcall(ffi.load, Brainstorm.PATH .. "/Immolate.dll") + if not ok then + debugLog("Failed to load Immolate.dll:", tostring(lib)) + Brainstorm.immolate_status = "missing" + return false + end + + Brainstorm.immolate = lib + Brainstorm.immolate_status = "loaded" + return true end local function fileExists(filePath) - return nfs.getInfo(filePath) ~= nil + return nfs.getInfo(filePath) ~= nil +end + +local function mergeConfig(defaults, loaded) + local merged = {} + for key, value in pairs(defaults) do + local incoming = loaded and loaded[key] + if type(value) == "table" then + merged[key] = mergeConfig(value, type(incoming) == "table" and incoming or {}) + elseif type(incoming) == type(value) then + merged[key] = incoming + else + merged[key] = value + end + end + return merged end function Brainstorm.loadConfig() - local configPath = Brainstorm.PATH .. "/config.lua" - if not fileExists(configPath) then - Brainstorm.writeConfig() - else - local configFile, err = nfs.read(configPath) - if not configFile then - error("Failed to read config file: " .. (err or "unknown error")) - end - Brainstorm.config = STR_UNPACK(configFile) or Brainstorm.config - end + local configPath = Brainstorm.PATH .. "/config.lua" + if not fileExists(configPath) then + Brainstorm.config = mergeConfig(Brainstorm.DEFAULT_CONFIG) + Brainstorm.writeConfig() + return + end + + local configFile, err = nfs.read(configPath) + if not configFile then + error("Failed to read config file: " .. (err or "unknown error")) + end + local unpacked = STR_UNPACK(configFile) or {} + Brainstorm.config = mergeConfig(Brainstorm.DEFAULT_CONFIG, unpacked) + Brainstorm.writeConfig() +end + +local function isArray(t) + local count = 0 + for k in pairs(t) do + if type(k) ~= "number" then + return false + end + count = count + 1 + end + return count == #t +end + +local function escapeString(str) + return str:gsub("\\", "\\\\"):gsub('"', '\\"'):gsub("%c", function(c) + return string_format("\\u%04x", c:byte()) + end) +end + +function Brainstorm.toJSON(val) + local t = type(val) + if t == "nil" then + return "null" + elseif t == "boolean" then + return val and "true" or "false" + elseif t == "number" then + return tostring(val) + elseif t == "string" then + return '"' .. escapeString(val) .. '"' + elseif t == "table" then + if isArray(val) then + local parts = {} + for i = 1, #val do + parts[#parts + 1] = Brainstorm.toJSON(val[i]) + end + return "[" .. table.concat(parts, ",") .. "]" + end + local parts = {} + for k, v in pairs(val) do + if type(k) == "string" then + parts[#parts + 1] = '"' .. escapeString(k) .. '":' .. Brainstorm.toJSON(v) + end + end + return "{" .. table.concat(parts, ",") .. "}" + end + return "null" +end + +function Brainstorm.loadFilters() + Brainstorm.filters = {} + Brainstorm.filters_list = {} + local filters_dir = Brainstorm.PATH .. "/filters" + if not nfs.getInfo(filters_dir, "directory") then + nfs.createDirectory(filters_dir) + return + end + for _, item in ipairs(nfs.getDirectoryItems(filters_dir)) do + if item:sub(-8) == ".bff.lua" then + local filePath = filters_dir .. "/" .. item + local chunk, err = load(nfs.read(filePath)) + if chunk then + local ok, data = pcall(chunk) + if ok and type(data) == "table" then + if data.version == 1 and data.id and data.label then + local entry = { + id = data.id, + label = data.label, + description = data.description, + query_json = data.query_json, + } + if not entry.query_json and data.query then + entry.query_json = Brainstorm.toJSON(data.query) + end + if entry.query_json then + Brainstorm.filters[entry.id] = entry + Brainstorm.filters_list[#Brainstorm.filters_list + 1] = entry + end + else + debugLog("Filter missing required fields in", item) + end + else + debugLog("Failed to load filter", item, tostring(data)) + end + else + debugLog("Failed to compile filter", item, tostring(err)) + end + end + end +end + +function Brainstorm.getActiveFilter() + if not Brainstorm.config.active_filter_id then + return nil + end + return Brainstorm.filters[Brainstorm.config.active_filter_id] end function Brainstorm.writeConfig() - local configPath = Brainstorm.PATH .. "/config.lua" - local success, err = nfs.write(configPath, STR_PACK(Brainstorm.config)) - if not success then - error("Failed to write config file: " .. (err or "unknown error")) - end + local configPath = Brainstorm.PATH .. "/config.lua" + local success, err = nfs.write(configPath, STR_PACK(Brainstorm.config)) + if not success then + error("Failed to write config file: " .. (err or "unknown error")) + end end function Brainstorm.init() - Brainstorm.PATH = findBrainstormDirectory(lovely.mod_dir) - Brainstorm.loadConfig() - assert(load(nfs.read(Brainstorm.PATH .. "/UI/ui.lua")))() + Brainstorm.PATH = findBrainstormDirectory(lovely.mod_dir) + if not Brainstorm.PATH then + error("Brainstorm: could not locate mod directory") + end + + Brainstorm.loadConfig() + Brainstorm.loadFilters() + Brainstorm.AR_INTERVAL = Brainstorm.getAutoInterval() + assert(load(nfs.read(Brainstorm.PATH .. "/UI/ui.lua")))() end local key_press_update_ref = Controller.key_press_update function Controller:key_press_update(key, dt) - key_press_update_ref(self, key, dt) - local keybinds = Brainstorm.config.keybinds - if love.keyboard.isDown(keybinds.modifier) then - if key == keybinds.f_reroll then - Brainstorm.reroll() - elseif key == keybinds.a_reroll then - Brainstorm.ar_active = not Brainstorm.ar_active - end - end + key_press_update_ref(self, key, dt) + local keybinds = Brainstorm.config.keybinds + if love.keyboard.isDown(keybinds.modifier) then + if key == keybinds.f_reroll then + Brainstorm.reroll() + elseif key == keybinds.a_reroll then + Brainstorm.ar_active = not Brainstorm.ar_active + end + end end function Brainstorm.reroll() - local G = G -- Cache global G for performance - G.GAME.viewed_back = nil - G.run_setup_seed = G.GAME.seeded - G.challenge_tab = G.GAME and G.GAME.challenge and G.GAME.challenge_tab or nil - G.forced_seed = G.GAME.seeded and G.GAME.pseudorandom.seed or nil - - local seed = G.run_setup_seed and G.setup_seed or G.forced_seed - local stake = ( - G.GAME.stake - or G.PROFILES[G.SETTINGS.profile].MEMORY.stake - or 1 - ) or 1 - - G:delete_run() - G:start_run({ stake = stake, seed = seed, challenge = G.challenge_tab }) + local G_ref = G + G_ref.GAME.viewed_back = nil + G_ref.run_setup_seed = G_ref.GAME.seeded + G_ref.challenge_tab = G_ref.GAME and G_ref.GAME.challenge and G_ref.GAME.challenge_tab or nil + G_ref.forced_seed = G_ref.GAME.seeded and G_ref.GAME.pseudorandom.seed or nil + + local seed = G_ref.run_setup_seed and G_ref.setup_seed or G_ref.forced_seed + local stake = (G_ref.GAME.stake or G_ref.PROFILES[G_ref.SETTINGS.profile].MEMORY.stake or 1) or 1 + + G_ref:delete_run() + G_ref:start_run({ stake = stake, seed = seed, challenge = G_ref.challenge_tab }) end local update_ref = Game.update function Game:update(dt) - update_ref(self, dt) - - if Brainstorm.ar_active then - Brainstorm.ar_frames = Brainstorm.ar_frames + 1 - Brainstorm.ar_timer = Brainstorm.ar_timer + dt - - if Brainstorm.ar_timer >= Brainstorm.AR_INTERVAL then - Brainstorm.ar_timer = Brainstorm.ar_timer - Brainstorm.AR_INTERVAL - if Brainstorm.autoReroll() then - Brainstorm.ar_active = false - Brainstorm.ar_frames = 0 - if Brainstorm.ar_text then - Brainstorm.removeAttentionText(Brainstorm.ar_text) - Brainstorm.ar_text = nil - end - end - end - - if Brainstorm.ar_frames == 60 and not Brainstorm.ar_text then - Brainstorm.ar_text = Brainstorm.attentionText({ - scale = 1.4, - text = "Rerolling...", - align = "cm", - offset = { x = 0, y = -3.5 }, - major = G.STAGE == G.STAGES.RUN and G.play or G.title_top, - }) - end - end + update_ref(self, dt) + + if Brainstorm.ar_active then + Brainstorm.ar_frames = Brainstorm.ar_frames + 1 + Brainstorm.ar_timer = Brainstorm.ar_timer + dt + local interval = Brainstorm.getAutoInterval() + + if Brainstorm.ar_timer >= interval then + Brainstorm.ar_timer = Brainstorm.ar_timer - interval + if Brainstorm.autoReroll() then + Brainstorm.ar_active = false + Brainstorm.ar_frames = 0 + if Brainstorm.ar_text then + Brainstorm.removeAttentionText(Brainstorm.ar_text) + Brainstorm.ar_text = nil + end + end + end + + if Brainstorm.ar_frames == 60 and not Brainstorm.ar_text then + Brainstorm.ar_text = Brainstorm.attentionText({ + scale = 1.4, + text = "Rerolling...", + align = "cm", + offset = { x = 0, y = -3.5 }, + major = G.STAGE == G.STAGES.RUN and G.play or G.title_top, + }) + end + end +end + +function Brainstorm.getAutoInterval() + local spf = Brainstorm.config.ar_prefs and Brainstorm.config.ar_prefs.spf_int or 1000 + local clamped = math.max(100, math.min(1000, spf)) + return 10 / clamped +end + +function Brainstorm.getImmolateStatus() + return Brainstorm.immolate_status or "unloaded" end function Brainstorm.autoReroll() - local seed_found = random_string( - 8, - G.CONTROLLER.cursor_hover.T.x * 0.33411983 - + G.CONTROLLER.cursor_hover.T.y * 0.874146 - + 0.412311010 * G.CONTROLLER.cursor_hover.time - ) - local ffi = require("ffi") - local lovely = require("lovely") - ffi.cdef([[ - const char* brainstorm(const char* seed, const char* voucher, const char* pack, const char* tag, double souls, bool observatory, bool perkeo); - ]]) - local immolate = ffi.load(Brainstorm.PATH .. "/Immolate.dll") - local pack - if #Brainstorm.config.ar_filters.pack > 0 then - pack = Brainstorm.config.ar_filters.pack[1]:match("^(.*)_") - else - pack = {} - end - local pack_name = localize({ type = "name_text", set = "Other", key = pack }) - local tag_name = localize({ - type = "name_text", - set = "Tag", - key = Brainstorm.config.ar_filters.tag_name, - }) - local voucher_name = localize({ - type = "name_text", - set = "Voucher", - key = Brainstorm.config.ar_filters.voucher_name, - }) - print(pack_name, tag_name, voucher_name) - seed_found = ffi.string( - immolate.brainstorm( - seed_found, - voucher_name, - pack_name, - tag_name, - Brainstorm.config.ar_filters.soul_skip, - Brainstorm.config.ar_filters.inst_observatory, - Brainstorm.config.ar_filters.inst_perkeo - ) - ) - if seed_found then - _stake = G.GAME.stake - G:delete_run() - G:start_run({ - stake = _stake, - seed = seed_found, - challenge = G.GAME and G.GAME.challenge and G.GAME.challenge_tab, - }) - G.GAME.used_filter = true - G.GAME.filter_info = { - filter_params = { - seed_found, - voucher_name, - pack_name, - tag_name, - Brainstorm.config.ar_filters.soul_skip, - Brainstorm.config.ar_filters.inst_observatory, - Brainstorm.config.ar_filters.inst_perkeo, - }, - } - G.GAME.seeded = false - end - return seed_found + local cursor = G.CONTROLLER and G.CONTROLLER.cursor_hover + local entropy + if cursor and cursor.T then + entropy = cursor.T.x * 0.33411983 + cursor.T.y * 0.874146 + 0.412311010 * cursor.time + else + entropy = love.timer.getTime() + end + local seed_found = random_string(8, entropy) + + local filters = Brainstorm.config.ar_filters + local perkeo_flag = filters.inst_perkeo or false + if filters.legendary_choice then + if filters.legendary_choice == "Perkeo" or filters.legendary_choice == "Perkeo + Observatory" then + perkeo_flag = true + else + perkeo_flag = false + end + end + local pack_key = "" + if #filters.pack > 0 then + pack_key = filters.pack[1]:match("^(.*)_") or "" + end + local pack_name = localize({ type = "name_text", set = "Other", key = pack_key }) or "" + local tag_name = localize({ + type = "name_text", + set = "Tag", + key = filters.tag_name, + }) or "" + local voucher_name = localize({ + type = "name_text", + set = "Voucher", + key = filters.voucher_name, + }) or "" + + if not ensureImmolateLoaded() or not Brainstorm.immolate.brainstorm then + Brainstorm.ar_active = false + debugLog("Immolate not available; disabling auto-reroll") + return nil + end + + local active_filter = Brainstorm.getActiveFilter() + local seed_ptr + if active_filter and Brainstorm.immolate.brainstorm_query then + seed_ptr = Brainstorm.immolate.brainstorm_query(seed_found, active_filter.query_json) + else + seed_ptr = Brainstorm.immolate.brainstorm( + seed_found, + voucher_name, + pack_name, + tag_name, + filters.soul_skip, + filters.inst_observatory, + perkeo_flag + ) + end + if not seed_ptr then + return nil + end + seed_found = ffi.string(seed_ptr) + if Brainstorm.immolate.free_result then + Brainstorm.immolate.free_result(seed_ptr) + end + if not seed_found or seed_found == "" then + return nil + end + + local current_stake = G.GAME.stake + G:delete_run() + G:start_run({ + stake = current_stake, + seed = seed_found, + challenge = G.GAME and G.GAME.challenge and G.GAME.challenge_tab, + }) + G.GAME.used_filter = true + G.GAME.filter_info = { + filter_params = { + seed_found, + pack_name, + tag_name, + filters.soul_skip, + filters.inst_observatory, + filters.legendary_choice, + }, + } + G.GAME.seeded = false + return seed_found end local cursr = create_UIBox_round_scores_row function create_UIBox_round_scores_row(score, text_colour) - local ret = cursr(score, text_colour) - ret.nodes[2].nodes[1].config.colour = (score == "seed" and G.GAME.seeded) - and G.C.RED - or (score == "seed" and G.GAME.used_filter) and G.C.BLUE - or G.C.BLACK - return ret + local ret = cursr(score, text_colour) + ret.nodes[2].nodes[1].config.colour = (score == "seed" and G.GAME.seeded) and G.C.RED + or (score == "seed" and G.GAME.used_filter) and G.C.BLUE + or G.C.BLACK + return ret end -- TODO: Rework attention text. function Brainstorm.attentionText(args) - args = args or {} - args.text = args.text or "test" - args.scale = args.scale or 1 - args.colour = copy_table(args.colour or G.C.WHITE) - args.hold = (args.hold or 0) + 0.1 * G.SPEEDFACTOR - args.pos = args.pos or { x = 0, y = 0 } - args.align = args.align or "cm" - args.emboss = args.emboss or nil - - args.fade = 1 - - if args.cover then - args.cover_colour = copy_table(args.cover_colour or G.C.RED) - args.cover_colour_l = copy_table(lighten(args.cover_colour, 0.2)) - args.cover_colour_d = copy_table(darken(args.cover_colour, 0.2)) - else - args.cover_colour = copy_table(G.C.CLEAR) - end - - args.uibox_config = { - align = args.align or "cm", - offset = args.offset or { x = 0, y = 0 }, - major = args.cover or args.major or nil, - } - - G.E_MANAGER:add_event(Event({ - trigger = "after", - delay = 0, - blockable = false, - blocking = false, - func = function() - args.AT = UIBox({ - T = { args.pos.x, args.pos.y, 0, 0 }, - definition = { - n = G.UIT.ROOT, - config = { - align = args.cover_align or "cm", - minw = (args.cover and args.cover.T.w or 0.001) - + (args.cover_padding or 0), - minh = (args.cover and args.cover.T.h or 0.001) - + (args.cover_padding or 0), - padding = 0.03, - r = 0.1, - emboss = args.emboss, - colour = args.cover_colour, - }, - nodes = { - { - n = G.UIT.O, - config = { - draw_layer = 1, - object = DynaText({ - scale = args.scale, - string = args.text, - maxw = args.maxw, - colours = { args.colour }, - float = true, - shadow = true, - silent = not args.noisy, - args.scale, - pop_in = 0, - pop_in_rate = 6, - rotate = args.rotate or nil, - }), - }, - }, - }, - }, - config = args.uibox_config, - }) - args.AT.attention_text = true - - args.text = args.AT.UIRoot.children[1].config.object - args.text:pulse(0.5) - - if args.cover then - Particles(args.pos.x, args.pos.y, 0, 0, { - timer_type = "TOTAL", - timer = 0.01, - pulse_max = 15, - max = 0, - scale = 0.3, - vel_variation = 0.2, - padding = 0.1, - fill = true, - lifespan = 0.5, - speed = 2.5, - attach = args.AT.UIRoot, - colours = { - args.cover_colour, - args.cover_colour_l, - args.cover_colour_d, - }, - }) - end - if args.backdrop_colour then - args.backdrop_colour = copy_table(args.backdrop_colour) - Particles(args.pos.x, args.pos.y, 0, 0, { - timer_type = "TOTAL", - timer = 5, - scale = 2.4 * (args.backdrop_scale or 1), - lifespan = 5, - speed = 0, - attach = args.AT, - colours = { args.backdrop_colour }, - }) - end - return true - end, - })) - return args + args = args or {} + args.text = args.text or "test" + args.scale = args.scale or 1 + args.colour = copy_table(args.colour or G.C.WHITE) + args.hold = (args.hold or 0) + 0.1 * G.SPEEDFACTOR + args.pos = args.pos or { x = 0, y = 0 } + args.align = args.align or "cm" + args.emboss = args.emboss or nil + + args.fade = 1 + + if args.cover then + args.cover_colour = copy_table(args.cover_colour or G.C.RED) + args.cover_colour_l = copy_table(lighten(args.cover_colour, 0.2)) + args.cover_colour_d = copy_table(darken(args.cover_colour, 0.2)) + else + args.cover_colour = copy_table(G.C.CLEAR) + end + + args.uibox_config = { + align = args.align or "cm", + offset = args.offset or { x = 0, y = 0 }, + major = args.cover or args.major or nil, + } + + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0, + blockable = false, + blocking = false, + func = function() + args.AT = UIBox({ + T = { args.pos.x, args.pos.y, 0, 0 }, + definition = { + n = G.UIT.ROOT, + config = { + align = args.cover_align or "cm", + minw = (args.cover and args.cover.T.w or 0.001) + (args.cover_padding or 0), + minh = (args.cover and args.cover.T.h or 0.001) + (args.cover_padding or 0), + padding = 0.03, + r = 0.1, + emboss = args.emboss, + colour = args.cover_colour, + }, + nodes = { + { + n = G.UIT.O, + config = { + draw_layer = 1, + object = DynaText({ + scale = args.scale, + string = args.text, + maxw = args.maxw, + colours = { args.colour }, + float = true, + shadow = true, + silent = not args.noisy, + args.scale, + pop_in = 0, + pop_in_rate = 6, + rotate = args.rotate or nil, + }), + }, + }, + }, + }, + config = args.uibox_config, + }) + args.AT.attention_text = true + + args.text = args.AT.UIRoot.children[1].config.object + args.text:pulse(0.5) + + if args.cover then + Particles(args.pos.x, args.pos.y, 0, 0, { + timer_type = "TOTAL", + timer = 0.01, + pulse_max = 15, + max = 0, + scale = 0.3, + vel_variation = 0.2, + padding = 0.1, + fill = true, + lifespan = 0.5, + speed = 2.5, + attach = args.AT.UIRoot, + colours = { + args.cover_colour, + args.cover_colour_l, + args.cover_colour_d, + }, + }) + end + if args.backdrop_colour then + args.backdrop_colour = copy_table(args.backdrop_colour) + Particles(args.pos.x, args.pos.y, 0, 0, { + timer_type = "TOTAL", + timer = 5, + scale = 2.4 * (args.backdrop_scale or 1), + lifespan = 5, + speed = 0, + attach = args.AT, + colours = { args.backdrop_colour }, + }) + end + return true + end, + })) + return args end function Brainstorm.removeAttentionText(args) - G.E_MANAGER:add_event(Event({ - trigger = "after", - delay = 0, - blockable = false, - blocking = false, - func = function() - if not args.start_time then - args.start_time = G.TIMERS.TOTAL - if args.text.pop_out then - args.text:pop_out(2) - end - else - --args.AT:align_to_attach() - args.fade = math.max(0, 1 - 3 * (G.TIMERS.TOTAL - args.start_time)) - if args.cover_colour then - args.cover_colour[4] = math.min(args.cover_colour[4], 2 * args.fade) - end - if args.cover_colour_l then - args.cover_colour_l[4] = math.min(args.cover_colour_l[4], args.fade) - end - if args.cover_colour_d then - args.cover_colour_d[4] = math.min(args.cover_colour_d[4], args.fade) - end - if args.backdrop_colour then - args.backdrop_colour[4] = math.min(args.backdrop_colour[4], args.fade) - end - args.colour[4] = math.min(args.colour[4], args.fade) - if args.fade <= 0 then - args.AT:remove() - return true - end - end - end, - })) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0, + blockable = false, + blocking = false, + func = function() + if not args.start_time then + args.start_time = G.TIMERS.TOTAL + if args.text.pop_out then + args.text:pop_out(2) + end + else + --args.AT:align_to_attach() + args.fade = math.max(0, 1 - 3 * (G.TIMERS.TOTAL - args.start_time)) + if args.cover_colour then + args.cover_colour[4] = math.min(args.cover_colour[4], 2 * args.fade) + end + if args.cover_colour_l then + args.cover_colour_l[4] = math.min(args.cover_colour_l[4], args.fade) + end + if args.cover_colour_d then + args.cover_colour_d[4] = math.min(args.cover_colour_d[4], args.fade) + end + if args.backdrop_colour then + args.backdrop_colour[4] = math.min(args.backdrop_colour[4], args.fade) + end + args.colour[4] = math.min(args.colour[4], args.fade) + if args.fade <= 0 then + args.AT:remove() + return true + end + end + end, + })) end diff --git a/UI/ui.lua b/UI/ui.lua index 028baee..5061d87 100644 --- a/UI/ui.lua +++ b/UI/ui.lua @@ -2,272 +2,310 @@ local lovely = require("lovely") local nativefs = require("nativefs") local tag_list = { - ["None"] = "", - ["Uncommon Tag"] = "tag_uncommon", - ["Rare Tag"] = "tag_rare", - ["Holographic Tag"] = "tag_holo", - ["Foil Tag"] = "tag_foil", - ["Polychrome Tag"] = "tag_polychrome", - ["Investment Tag"] = "tag_investment", - ["Voucher Tag"] = "tag_voucher", - ["Boss Tag"] = "tag_boss", - ["Charm Tag"] = "tag_charm", - ["Juggle Tag"] = "tag_juggle", - ["Double Tag"] = "tag_double", - ["Coupon Tag"] = "tag_coupon", - ["Economy Tag"] = "tag_economy", - ["Skip Tag"] = "tag_skip", - ["D6 Tag"] = "tag_d_six", + ["None"] = "", + ["Uncommon Tag"] = "tag_uncommon", + ["Rare Tag"] = "tag_rare", + ["Holographic Tag"] = "tag_holo", + ["Foil Tag"] = "tag_foil", + ["Polychrome Tag"] = "tag_polychrome", + ["Investment Tag"] = "tag_investment", + ["Voucher Tag"] = "tag_voucher", + ["Boss Tag"] = "tag_boss", + ["Charm Tag"] = "tag_charm", + ["Juggle Tag"] = "tag_juggle", + ["Double Tag"] = "tag_double", + ["Coupon Tag"] = "tag_coupon", + ["Economy Tag"] = "tag_economy", + ["Skip Tag"] = "tag_skip", + ["D6 Tag"] = "tag_d_six", } local voucher_list = { - ["None"] = "", - ["Overstock"] = "v_overstock_norm", - ["Clearance Sale"] = "v_clearance_sale", - ["Hone"] = "v_hone", - ["Reroll Surplus"] = "v_reroll_surplus", - ["Crystal Ball"] = "v_crystal_ball", - ["Telescope"] = "v_telescope", - ["Grabber"] = "v_grabber", - ["Wasteful"] = "v_wasteful", - ["Tarot Merchant"] = "v_tarot_merchant", - ["Planet Merchant"] = "v_planet_merchant", - ["Seed Money"] = "v_seed_money", - ["Blank"] = "v_blank", - ["Magic Trick"] = "v_magic_trick", - ["Hieroglyph"] = "v_hieroglyph", - ["Director's Cut"] = "v_directors_cut", - ["Paint Brush"] = "v_paint_brush", + ["None"] = "", + ["Overstock"] = "v_overstock_norm", + ["Clearance Sale"] = "v_clearance_sale", + ["Hone"] = "v_hone", + ["Reroll Surplus"] = "v_reroll_surplus", + ["Crystal Ball"] = "v_crystal_ball", + ["Telescope"] = "v_telescope", + ["Grabber"] = "v_grabber", + ["Wasteful"] = "v_wasteful", + ["Tarot Merchant"] = "v_tarot_merchant", + ["Planet Merchant"] = "v_planet_merchant", + ["Seed Money"] = "v_seed_money", + ["Blank"] = "v_blank", + ["Magic Trick"] = "v_magic_trick", + ["Hieroglyph"] = "v_hieroglyph", + ["Director's Cut"] = "v_directors_cut", + ["Paint Brush"] = "v_paint_brush", } local pack_list = { - ["None"] = {}, - ["Normal Arcana"] = { - "p_arcana_normal_1", - "p_arcana_normal_2", - "p_arcana_normal_3", - "p_arcana_normal_4", - }, - ["Jumbo Arcana"] = { "p_arcana_jumbo_1", "p_arcana_jumbo_2" }, - ["Mega Arcana"] = { "p_arcana_mega_1", "p_arcana_mega_2" }, - ["Normal Celestial"] = { - "p_celestial_normal_1", - "p_celestial_normal_2", - "p_celestial_normal_3", - "p_celestial_normal_4", - }, - ["Jumbo Celestial"] = { "p_celestial_jumbo_1", "p_celestial_jumbo_2" }, - ["Mega Celestial"] = { "p_celestial_mega_1", "p_celestial_mega_2" }, - ["Normal Standard"] = { - "p_standard_normal_1", - "p_standard_normal_2", - "p_standard_normal_3", - "p_standard_normal_4", - }, - ["Jumbo Standard"] = { "p_standard_jumbo_1", "p_standard_jumbo_2" }, - ["Mega Standard"] = { "p_standard_mega_1", "p_standard_mega_2" }, - ["Normal Buffoon"] = { "p_buffoon_normal_1", "p_buffoon_normal_2" }, - ["Jumbo Buffoon"] = { "p_buffoon_jumbo_1" }, - ["Mega Buffoon"] = { "p_buffoon_mega_1" }, - ["Normal Spectral"] = { "p_spectral_normal_1", "p_spectral_normal_2" }, - ["Jumbo Spectral"] = { "p_spectral_jumbo_1" }, - ["Mega Spectral"] = { "p_spectral_mega_1" }, + ["None"] = {}, + ["Normal Arcana"] = { + "p_arcana_normal_1", + "p_arcana_normal_2", + "p_arcana_normal_3", + "p_arcana_normal_4", + }, + ["Jumbo Arcana"] = { "p_arcana_jumbo_1", "p_arcana_jumbo_2" }, + ["Mega Arcana"] = { "p_arcana_mega_1", "p_arcana_mega_2" }, + ["Normal Celestial"] = { + "p_celestial_normal_1", + "p_celestial_normal_2", + "p_celestial_normal_3", + "p_celestial_normal_4", + }, + ["Jumbo Celestial"] = { "p_celestial_jumbo_1", "p_celestial_jumbo_2" }, + ["Mega Celestial"] = { "p_celestial_mega_1", "p_celestial_mega_2" }, + ["Normal Standard"] = { + "p_standard_normal_1", + "p_standard_normal_2", + "p_standard_normal_3", + "p_standard_normal_4", + }, + ["Jumbo Standard"] = { "p_standard_jumbo_1", "p_standard_jumbo_2" }, + ["Mega Standard"] = { "p_standard_mega_1", "p_standard_mega_2" }, + ["Normal Buffoon"] = { "p_buffoon_normal_1", "p_buffoon_normal_2" }, + ["Jumbo Buffoon"] = { "p_buffoon_jumbo_1" }, + ["Mega Buffoon"] = { "p_buffoon_mega_1" }, + ["Normal Spectral"] = { "p_spectral_normal_1", "p_spectral_normal_2" }, + ["Jumbo Spectral"] = { "p_spectral_jumbo_1" }, + ["Mega Spectral"] = { "p_spectral_mega_1" }, } local spf_list = { - ["500"] = 500, - ["750"] = 750, - ["1000"] = 1000, + ["500"] = 500, + ["750"] = 750, + ["1000"] = 1000, } local spf_keys = { "500", "750", "1000" } +local legendary_list = { + ["None"] = "", + ["Perkeo"] = "perkeo", + ["Observatory"] = "observatory", + ["Perkeo + Observatory"] = "perkeo_observatory", +} +local legendary_keys = { "None", "Perkeo", "Observatory", "Perkeo + Observatory" } + local voucher_keys = { - "None", - "Overstock", - "Clearance Sale", - "Hone", - "Reroll Surplus", - "Crystal Ball", - "Telescope", - "Grabber", - "Wasteful", - "Tarot Merchant", - "Planet Merchant", - "Seed Money", - "Blank", - "Magic Trick", - "Hieroglyph", - "Director's Cut", - "Paint Brush", + "None", + "Overstock", + "Clearance Sale", + "Hone", + "Reroll Surplus", + "Crystal Ball", + "Telescope", + "Grabber", + "Wasteful", + "Tarot Merchant", + "Planet Merchant", + "Seed Money", + "Blank", + "Magic Trick", + "Hieroglyph", + "Director's Cut", + "Paint Brush", } local tag_keys = { - "None", - "Charm Tag", - "Double Tag", - "Uncommon Tag", - "Rare Tag", - "Holographic Tag", - "Foil Tag", - "Polychrome Tag", - "Investment Tag", - "Voucher Tag", - "Boss Tag", - "Juggle Tag", - "Coupon Tag", - "Economy Tag", - "Skip Tag", - "D6 Tag", + "None", + "Charm Tag", + "Double Tag", + "Uncommon Tag", + "Rare Tag", + "Holographic Tag", + "Foil Tag", + "Polychrome Tag", + "Investment Tag", + "Voucher Tag", + "Boss Tag", + "Juggle Tag", + "Coupon Tag", + "Economy Tag", + "Skip Tag", + "D6 Tag", } local pack_keys = { - "None", - "Normal Arcana", - "Jumbo Arcana", - "Mega Arcana", - "Normal Celestial", - "Jumbo Celestial", - "Mega Celestial", - "Normal Standard", - "Jumbo Standard", - "Mega Standard", - "Normal Buffoon", - "Jumbo Buffoon", - "Mega Buffoon", - "Normal Spectral", - "Jumbo Spectral", - "Mega Spectral", + "None", + "Normal Arcana", + "Jumbo Arcana", + "Mega Arcana", + "Normal Celestial", + "Jumbo Celestial", + "Mega Celestial", + "Normal Standard", + "Jumbo Standard", + "Mega Standard", + "Normal Buffoon", + "Jumbo Buffoon", + "Mega Buffoon", + "Normal Spectral", + "Jumbo Spectral", + "Mega Spectral", } +local function filter_labels() + local labels = { "Legacy (manual)" } + for _, f in ipairs(Brainstorm.filters_list or {}) do + labels[#labels + 1] = f.label + end + return labels +end + +local function filter_current_option() + if not Brainstorm.config.active_filter_id then + return 1 + end + for idx, f in ipairs(Brainstorm.filters_list or {}) do + if f.id == Brainstorm.config.active_filter_id then + return idx + 1 + end + end + return 1 +end + +G.FUNCS.change_active_filter = function(x) + local key = x.to_key or 1 + if key == 1 then + Brainstorm.config.active_filter_id = nil + else + local entry = Brainstorm.filters_list[key - 1] + Brainstorm.config.active_filter_id = entry and entry.id or nil + end + Brainstorm.writeConfig() +end + G.FUNCS.change_target_voucher = function(x) - Brainstorm.config.ar_filters.voucher_id = x.to_key - Brainstorm.config.ar_filters.voucher_name = voucher_list[x.to_val] - Brainstorm.writeConfig() + Brainstorm.config.ar_filters.voucher_id = x.to_key + Brainstorm.config.ar_filters.voucher_name = voucher_list[x.to_val] + Brainstorm.writeConfig() end G.FUNCS.change_target_pack = function(x) - Brainstorm.config.ar_filters.pack_id = x.to_key - Brainstorm.config.ar_filters.pack = pack_list[x.to_val] - Brainstorm.writeConfig() + Brainstorm.config.ar_filters.pack_id = x.to_key + Brainstorm.config.ar_filters.pack = pack_list[x.to_val] + Brainstorm.writeConfig() end G.FUNCS.change_target_tag = function(x) - Brainstorm.config.ar_filters.tag_id = x.to_key - Brainstorm.config.ar_filters.tag_name = tag_list[x.to_val] - Brainstorm.writeConfig() + Brainstorm.config.ar_filters.tag_id = x.to_key + Brainstorm.config.ar_filters.tag_name = tag_list[x.to_val] + Brainstorm.writeConfig() +end + +G.FUNCS.change_target_legendary = function(x) + Brainstorm.config.ar_filters.legendary_id = x.to_key + Brainstorm.config.ar_filters.legendary_choice = legendary_keys[x.to_key] or "None" + Brainstorm.writeConfig() end G.FUNCS.change_soul_count = function(x) - Brainstorm.config.ar_filters.soul_skip = x.to_val - Brainstorm.writeConfig() + Brainstorm.config.ar_filters.soul_skip = x.to_val + Brainstorm.writeConfig() end G.FUNCS.change_spf = function(x) - Brainstorm.config.ar_prefs.spf_id = x.to_key - Brainstorm.config.ar_prefs.spf_int = spf_list[x.to_val] - Brainstorm.writeConfig() + Brainstorm.config.ar_prefs.spf_id = x.to_key + Brainstorm.config.ar_prefs.spf_int = spf_list[x.to_val] + Brainstorm.writeConfig() end Brainstorm.opt_ref = G.FUNCS.options G.FUNCS.options = function(e) - Brainstorm.opt_ref(e) + Brainstorm.opt_ref(e) end local ct = create_tabs function create_tabs(args) - if args and args.tab_h == 7.05 then - args.tabs[#args.tabs + 1] = { - label = "Brainstorm", - tab_definition_function = function() - return { - n = G.UIT.ROOT, - config = { - align = "cm", - padding = 0.05, - colour = G.C.CLEAR, - }, - nodes = { - { - n = G.UIT.C, - config = { - align = "cm", - padding = 0.05, - r = 0.1, - colour = darken(G.C.UI.TRANSPARENT_DARK, 0.25), - }, - nodes = { - create_option_cycle({ - label = "AR: TAG SEARCH", - scale = 0.8, - w = 4, - options = tag_keys, - opt_callback = "change_target_tag", - current_option = Brainstorm.config.ar_filters.tag_id or 1, - }), - create_option_cycle({ - label = "AR: VOUCHER SEARCH", - scale = 0.8, - w = 4, - options = voucher_keys, - opt_callback = "change_target_voucher", - current_option = Brainstorm.config.ar_filters.voucher_id or 1, - }), - create_option_cycle({ - label = "AR: PACK SEARCH", - scale = 0.8, - w = 4, - options = pack_keys, - opt_callback = "change_target_pack", - current_option = Brainstorm.config.ar_filters.pack_id or 1, - }), - create_option_cycle({ - label = "AR: N. SOULS", - scale = 0.8, - w = 4, - options = { 0, 1 }, - opt_callback = "change_soul_count", - current_option = Brainstorm.config.ar_filters.soul_skip + 1 - or 1, - }), - }, - }, - { - n = G.UIT.C, - config = { - align = "cm", - padding = 0.05, - r = 0.1, - colour = darken(G.C.UI.TRANSPARENT_DARK, 0.25), - }, - nodes = { - create_option_cycle({ - label = "AP: Seeds per frame", - scale = 0.8, - w = 4, - options = spf_keys, - opt_callback = "change_spf", - current_option = Brainstorm.config.ar_prefs.spf_id or 1, - }), - create_toggle({ - label = "AR: INST OBSERVATORY", - scale = 0.8, - ref_table = Brainstorm.config.ar_filters, - ref_value = "inst_observatory", - callback = function(_set_toggle) end, - }), - create_toggle({ - label = "AR: INST PERKEO", - scale = 0.8, - ref_table = Brainstorm.config.ar_filters, - ref_value = "inst_perkeo", - callback = function(_set_toggle) end, - }), - }, - }, - }, - } - end, - tab_definition_function_args = "Brainstorm", - } - end - return ct(args) + if args and args.tab_h == 7.05 then + args.tabs[#args.tabs + 1] = { + label = "Brainstorm", + tab_definition_function = function() + return { + n = G.UIT.ROOT, + config = { + align = "cm", + padding = 0.05, + colour = G.C.CLEAR, + }, + nodes = { + { + n = G.UIT.C, + config = { + align = "cm", + padding = 0.05, + r = 0.1, + colour = darken(G.C.UI.TRANSPARENT_DARK, 0.25), + }, + nodes = { + create_option_cycle({ + label = "FILTER SOURCE", + scale = 0.8, + w = 4, + options = filter_labels(), + opt_callback = "change_active_filter", + current_option = filter_current_option(), + }), + create_option_cycle({ + label = "TAG SEARCH", + scale = 0.8, + w = 4, + options = tag_keys, + opt_callback = "change_target_tag", + current_option = Brainstorm.config.ar_filters.tag_id or 1, + }), + create_option_cycle({ + label = "VOUCHER SEARCH", + scale = 0.8, + w = 4, + options = voucher_keys, + opt_callback = "change_target_voucher", + current_option = Brainstorm.config.ar_filters.voucher_id or 1, + }), + create_option_cycle({ + label = "PACK SEARCH", + scale = 0.8, + w = 4, + options = pack_keys, + opt_callback = "change_target_pack", + current_option = Brainstorm.config.ar_filters.pack_id or 1, + }), + create_option_cycle({ + label = "LEGENDARY", + scale = 0.8, + w = 4, + options = legendary_keys, + opt_callback = "change_target_legendary", + current_option = Brainstorm.config.ar_filters.legendary_id or 1, + }), + }, + }, + { + n = G.UIT.C, + config = { + align = "cm", + padding = 0.05, + r = 0.1, + colour = darken(G.C.UI.TRANSPARENT_DARK, 0.25), + }, + nodes = { + create_option_cycle({ + label = "# of seeds to search per frame", + scale = 0.8, + w = 4, + options = spf_keys, + opt_callback = "change_spf", + current_option = Brainstorm.config.ar_prefs.spf_id or 1, + }), + }, + }, + }, + } + end, + tab_definition_function_args = "Brainstorm", + } + end + return ct(args) end diff --git a/immolate/functions.cpp b/immolate/functions.cpp new file mode 100644 index 0000000..11ee471 --- /dev/null +++ b/immolate/functions.cpp @@ -0,0 +1,11 @@ +#include "functions.hpp" + +std::vector PACK_INFO = { + Pack(Item::Arcana_Pack, 3, 1), Pack(Item::Arcana_Pack, 5, 1), + Pack(Item::Arcana_Pack, 5, 2), Pack(Item::Celestial_Pack, 3, 1), + Pack(Item::Celestial_Pack, 5, 1), Pack(Item::Celestial_Pack, 5, 2), + Pack(Item::Standard_Pack, 3, 1), Pack(Item::Standard_Pack, 5, 1), + Pack(Item::Standard_Pack, 5, 2), Pack(Item::Buffoon_Pack, 2, 1), + Pack(Item::Buffoon_Pack, 4, 1), Pack(Item::Buffoon_Pack, 4, 2), + Pack(Item::Spectral_Pack, 2, 1), Pack(Item::Spectral_Pack, 4, 1), + Pack(Item::Spectral_Pack, 4, 2)}; diff --git a/immolate/functions.hpp b/immolate/functions.hpp new file mode 100644 index 0000000..f9599bd --- /dev/null +++ b/immolate/functions.hpp @@ -0,0 +1,639 @@ +#ifndef FUNCTIONS_HPP +#define FUNCTIONS_HPP + +#include "instance.hpp" +#include "rng.hpp" +#include + +// Note: Technically, marking everything as inline is not a proper fix. Ideally, +// we'd place these correctly into hpp and cpp files BUT, i want to have sanity + +// Helper functions +inline void Instance::lock(Item item) { locked[(int)item] = true; } +inline void Instance::unlock(Item item) { locked[(int)item] = false; } +inline bool Instance::isLocked(Item item) { return locked[(int)item]; } + +// Lock initializers +inline void Instance::initLocks(int ante, bool freshProfile, bool freshRun) { + if (ante < 2) { + lock(Item::The_Mouth); + lock(Item::The_Fish); + lock(Item::The_Wall); + lock(Item::The_House); + lock(Item::The_Mark); + lock(Item::The_Wheel); + lock(Item::The_Arm); + lock(Item::The_Water); + lock(Item::The_Needle); + lock(Item::The_Flint); + lock(Item::Negative_Tag); + lock(Item::Standard_Tag); + lock(Item::Meteor_Tag); + lock(Item::Buffoon_Tag); + lock(Item::Handy_Tag); + lock(Item::Garbage_Tag); + lock(Item::Ethereal_Tag); + lock(Item::Top_up_Tag); + lock(Item::Orbital_Tag); + } + if (ante < 3) { + lock(Item::The_Tooth); + lock(Item::The_Eye); + } + if (ante < 4) { + lock(Item::The_Plant); + } + if (ante < 5) { + lock(Item::The_Serpent); + } + if (ante < 6) { + lock(Item::The_Ox); + } + if (freshProfile) { + // Tags + lock(Item::Negative_Tag); + lock(Item::Foil_Tag); + lock(Item::Holographic_Tag); + lock(Item::Polychrome_Tag); + lock(Item::Rare_Tag); + + // Jokers + lock(Item::Golden_Ticket); + lock(Item::Mr_Bones); + lock(Item::Acrobat); + lock(Item::Sock_and_Buskin); + lock(Item::Swashbuckler); + lock(Item::Troubadour); + lock(Item::Certificate); + lock(Item::Smeared_Joker); + lock(Item::Throwback); + lock(Item::Hanging_Chad); + lock(Item::Rough_Gem); + lock(Item::Bloodstone); + lock(Item::Arrowhead); + lock(Item::Onyx_Agate); + lock(Item::Glass_Joker); + lock(Item::Showman); + lock(Item::Flower_Pot); + lock(Item::Blueprint); + lock(Item::Wee_Joker); + lock(Item::Merry_Andy); + lock(Item::Oops_All_6s); + lock(Item::The_Idol); + lock(Item::Seeing_Double); + lock(Item::Matador); + lock(Item::Hit_the_Road); + lock(Item::The_Duo); + lock(Item::The_Trio); + lock(Item::The_Family); + lock(Item::The_Order); + lock(Item::The_Tribe); + lock(Item::Stuntman); + lock(Item::Invisible_Joker); + lock(Item::Brainstorm); + lock(Item::Satellite); + lock(Item::Shoot_the_Moon); + lock(Item::Drivers_License); + lock(Item::Cartomancer); + lock(Item::Astronomer); + lock(Item::Burnt_Joker); + lock(Item::Bootstraps); + + // Vouchers + lock(Item::Overstock_Plus); + lock(Item::Liquidation); + lock(Item::Glow_Up); + lock(Item::Reroll_Glut); + lock(Item::Omen_Globe); + lock(Item::Observatory); + lock(Item::Nacho_Tong); + lock(Item::Recyclomancy); + lock(Item::Tarot_Tycoon); + lock(Item::Planet_Tycoon); + lock(Item::Money_Tree); + lock(Item::Antimatter); + lock(Item::Illusion); + lock(Item::Petroglyph); + lock(Item::Retcon); + lock(Item::Palette); + } + + // Locked in start of run + if (freshRun) { + // Require hand discoveries + lock(Item::Planet_X); + lock(Item::Ceres); + lock(Item::Eris); + lock(Item::Five_of_a_Kind); + lock(Item::Flush_House); + lock(Item::Flush_Five); + + // Requires specific card enhancement + lock(Item::Stone_Joker); // Stone + lock(Item::Steel_Joker); // Steel + lock(Item::Glass_Joker); // Glass + lock(Item::Golden_Ticket); // Gold + lock(Item::Lucky_Cat); // Lucky + + // Requires Gros Michel death + lock(Item::Cavendish); + + // Vouchers + lock(Item::Overstock_Plus); + lock(Item::Liquidation); + lock(Item::Glow_Up); + lock(Item::Reroll_Glut); + lock(Item::Omen_Globe); + lock(Item::Observatory); + lock(Item::Nacho_Tong); + lock(Item::Recyclomancy); + lock(Item::Tarot_Tycoon); + lock(Item::Planet_Tycoon); + lock(Item::Money_Tree); + lock(Item::Antimatter); + lock(Item::Illusion); + lock(Item::Petroglyph); + lock(Item::Retcon); + lock(Item::Palette); + } +} +inline void Instance::initUnlocks(int ante, bool freshProfile) { + if (ante == 2) { + unlock(Item::The_Mouth); + unlock(Item::The_Fish); + unlock(Item::The_Wall); + unlock(Item::The_House); + unlock(Item::The_Mark); + unlock(Item::The_Wheel); + unlock(Item::The_Arm); + unlock(Item::The_Water); + unlock(Item::The_Needle); + unlock(Item::The_Flint); + if (!freshProfile) + unlock(Item::Negative_Tag); + unlock(Item::Standard_Tag); + unlock(Item::Meteor_Tag); + unlock(Item::Buffoon_Tag); + unlock(Item::Handy_Tag); + unlock(Item::Garbage_Tag); + unlock(Item::Ethereal_Tag); + unlock(Item::Top_up_Tag); + unlock(Item::Orbital_Tag); + } + if (ante == 3) { + unlock(Item::The_Tooth); + unlock(Item::The_Eye); + } + if (ante == 4) { + unlock(Item::The_Plant); + } + if (ante == 5) { + unlock(Item::The_Serpent); + } + if (ante == 6) { + unlock(Item::The_Ox); + } +} + +// Card Generators +inline Item Instance::nextTarot(std::string source, int ante, bool soulable) { + std::string anteStr = anteToString(ante); + if (soulable && (params.showman || !isLocked(Item::The_Soul)) && + random(RandomType::Soul + RandomType::Tarot + anteStr) > 0.997) { + return Item::The_Soul; + } + return randchoice(RandomType::Tarot + source + anteStr, TAROTS); +} + +inline Item Instance::nextPlanet(std::string source, int ante, bool soulable) { + std::string anteStr = anteToString(ante); + if (soulable && (params.showman || !isLocked(Item::Black_Hole)) && + random(RandomType::Soul + RandomType::Planet + anteStr) > 0.997) { + return Item::Black_Hole; + } + return randchoice(RandomType::Planet + source + anteStr, PLANETS); +} + +inline Item Instance::nextSpectral(std::string source, int ante, + bool soulable) { + std::string anteStr = anteToString(ante); + if (soulable) { + Item forcedKey = Item::RETRY; + if ((params.showman || !isLocked(Item::The_Soul)) && + random(RandomType::Soul + RandomType::Spectral + anteStr) > 0.997) + forcedKey = Item::The_Soul; + if ((params.showman || !isLocked(Item::Black_Hole)) && + random(RandomType::Soul + RandomType::Spectral + anteStr) > 0.997) + forcedKey = Item::Black_Hole; + if (forcedKey != Item::RETRY) + return forcedKey; + } + return randchoice(RandomType::Spectral + source + anteStr, SPECTRALS); +} + +inline JokerData Instance::nextJoker(std::string source, int ante, + bool hasStickers) { + std::string anteStr = anteToString(ante); + + // Get rarity + Item rarity; + if (source == ItemSource::Soul) + rarity = Item::Legendary; + else if (source == ItemSource::Wraith) + rarity = Item::Rare; + else if (source == ItemSource::Rare_Tag) + rarity = Item::Rare; + else if (source == ItemSource::Uncommon_Tag) + rarity = Item::Uncommon; + else { + double rarityPoll = random(RandomType::Joker_Rarity + anteStr + source); + if (rarityPoll > 0.95) + rarity = Item::Rare; + else if (rarityPoll > 0.7) + rarity = Item::Uncommon; + else + rarity = Item::Common; + } + + // Get edition + int editionRate = 1; + if (isVoucherActive(Item::Glow_Up)) + editionRate = 4; + else if (isVoucherActive(Item::Hone)) + editionRate = 2; + Item edition; + double editionPoll = random(RandomType::Joker_Edition + source + anteStr); + if (editionPoll > 0.997) { + edition = Item::Negative; + } else if (editionPoll > 1 - 0.006 * editionRate) { + edition = Item::Polychrome; + } else if (editionPoll > 1 - 0.02 * editionRate) { + edition = Item::Holographic; + } else if (editionPoll > 1 - 0.04 * editionRate) { + edition = Item::Foil; + } else { + edition = Item::No_Edition; + } + + // Get next joker + Item joker; + if (rarity == Item::Legendary) { + if (params.version > 10099) { + joker = randchoice(RandomType::Joker_Legendary, LEGENDARY_JOKERS); + } else { + joker = randchoice(RandomType::Joker_Legendary + source + anteStr, + LEGENDARY_JOKERS); + } + } else if (rarity == Item::Rare) { + if (params.version > 10099) { + joker = + randchoice(RandomType::Joker_Rare + source + anteStr, RARE_JOKERS); + } else { + joker = randchoice(RandomType::Joker_Rare + source + anteStr, + RARE_JOKERS_100); + } + } else if (rarity == Item::Uncommon) { + if (params.version > 10099) { + joker = randchoice(RandomType::Joker_Uncommon + source + anteStr, + UNCOMMON_JOKERS); + } else { + joker = randchoice(RandomType::Joker_Uncommon + source + anteStr, + UNCOMMON_JOKERS_100); + } + } else if (rarity == Item::Common) { + if (params.version > 10099) { + joker = randchoice(RandomType::Joker_Common + source + anteStr, + COMMON_JOKERS); + } else { + joker = randchoice(RandomType::Joker_Common + source + anteStr, + COMMON_JOKERS_100); + } + } + + // Get next joker stickers + JokerStickers stickers = JokerStickers(); + if (hasStickers) { + if (params.version > 10099) { + double stickerPoll = random(((source == ItemSource::Buffoon_Pack) + ? RandomType::Eternal_Perishable_Pack + : RandomType::Eternal_Perishable) + + anteStr); + if (stickerPoll > 0.7 && params.stake >= Item::Black_Stake) { + if (joker != Item::Gros_Michel && joker != Item::Ice_Cream && + joker != Item::Cavendish && joker != Item::Luchador && + joker != Item::Turtle_Bean && joker != Item::Diet_Cola && + joker != Item::Popcorn && joker != Item::Ramen && + joker != Item::Seltzer && joker != Item::Mr_Bones && + joker != Item::Invisible_Joker) { + stickers.eternal = true; + } + } + if (stickerPoll > 0.4 && stickerPoll <= 0.7 && + params.stake >= Item::Orange_Stake && + joker != Item::Ceremonial_Dagger && joker != Item::Ride_the_Bus && + joker != Item::Runner && joker != Item::Constellation && + joker != Item::Green_Joker && joker != Item::Red_Card && + joker != Item::Madness && joker != Item::Square_Joker && + joker != Item::Vampire && joker != Item::Rocket && + joker != Item::Obelisk && joker != Item::Lucky_Cat && + joker != Item::Flash_Card && joker != Item::Spare_Trousers && + joker != Item::Castle && joker != Item::Wee_Joker) { + stickers.perishable = true; + } + if (params.stake >= Item::Gold_Stake) { + stickers.rental = random(((source == ItemSource::Buffoon_Pack) + ? RandomType::Rental_Pack + : RandomType::Rental) + + anteStr) > 0.7; + } + } else { + if (params.stake >= Item::Black_Stake) { + if (joker != Item::Gros_Michel && joker != Item::Ice_Cream && + joker != Item::Cavendish && joker != Item::Luchador && + joker != Item::Turtle_Bean && joker != Item::Diet_Cola && + joker != Item::Popcorn && joker != Item::Ramen && + joker != Item::Seltzer && joker != Item::Mr_Bones && + joker != Item::Invisible_Joker) { + stickers.eternal = random(RandomType::Eternal + anteStr) > 0.7; + } + } + } + } + + return JokerData(joker, rarity, edition, stickers); +} + +// Shop Logic +inline ShopInstance Instance::getShopInstance() { + double tarotRate = 4; + double planetRate = 4; + double playingCardRate = 0; + double spectralRate = 0; + if (params.deck == Item::Ghost_Deck) { + spectralRate = 2; + } + if (isVoucherActive(Item::Tarot_Tycoon)) { + tarotRate = 32; + } else if (isVoucherActive(Item::Tarot_Merchant)) { + tarotRate = 9.6; + } + if (isVoucherActive(Item::Planet_Tycoon)) { + planetRate = 32; + } else if (isVoucherActive(Item::Planet_Merchant)) { + planetRate = 9.6; + } + if (isVoucherActive(Item::Magic_Trick)) { + playingCardRate = 4; + } + + return ShopInstance(20, tarotRate, planetRate, playingCardRate, spectralRate); +}; + +inline Item shopItemType(ShopInstance shop, double cdtPoll) { + if (cdtPoll < shop.jokerRate) { + return Item::T_Joker; + } + cdtPoll -= shop.jokerRate; + + if (cdtPoll < shop.tarotRate) { + return Item::T_Tarot; + } + cdtPoll -= shop.tarotRate; + + if (cdtPoll < shop.planetRate) { + return Item::T_Planet; + } + cdtPoll -= shop.planetRate; + + if (cdtPoll < shop.playingCardRate) { + return Item::T_Playing_Card; + } + + return Item::T_Spectral; +} + +inline ShopItem Instance::nextShopItem(int ante) { + std::string anteStr = anteToString(ante); + + ShopInstance shop = getShopInstance(); + double cdtPoll = + random(RandomType::Card_Type + anteStr) * shop.getTotalRate(); + Item type = shopItemType(shop, cdtPoll); + + if (type == Item::T_Joker) { + JokerData jkr = nextJoker(ItemSource::Shop, ante, true); + return ShopItem(type, jkr.joker, jkr); + } else if (type == Item::T_Tarot) { + return ShopItem(type, nextTarot(ItemSource::Shop, ante, false)); + } else if (type == Item::T_Planet) { + return ShopItem(type, nextPlanet(ItemSource::Shop, ante, false)); + } else if (type == Item::T_Spectral) { + return ShopItem(type, nextSpectral(ItemSource::Shop, ante, false)); + } + // Todo: Magic Trick support + return ShopItem(); +} + +// Packs and Pack Contents +inline Item Instance::nextPack(int ante) { + if (ante <= 2 && !cache.generatedFirstPack && params.version > 10099) { + cache.generatedFirstPack = true; + return Item::Buffoon_Pack; + } + std::string anteStr = anteToString(ante); + return randweightedchoice(RandomType::Shop_Pack + anteStr, PACKS); +} + +extern std::vector PACK_INFO; + +inline Pack packInfo(Item pack) { + return PACK_INFO[(int)pack - (int)Item::Arcana_Pack]; +} + +inline Card Instance::nextStandardCard(int ante) { + std::string anteStr = anteToString(ante); + + // Enhancement + Item enhancement; + if (random(RandomType::Standard_Has_Enhancement + anteStr) <= 0.6) { + enhancement = Item::No_Enhancement; + } else { + enhancement = randchoice(RandomType::Enhancement + + ItemSource::Standard_Pack + anteStr, + ENHANCEMENTS); + } + + // Base + Item base = + randchoice(RandomType::Card + ItemSource::Standard_Pack + anteStr, CARDS); + + // Edition + Item edition; + double editionPoll = random(RandomType::Standard_Edition + anteStr); + if (editionPoll > 0.988) + edition = Item::Polychrome; + else if (editionPoll > 0.96) + edition = Item::Holographic; + else if (editionPoll > 0.92) + edition = Item::Foil; + else + edition = Item::No_Edition; + + // Seal + Item seal; + if (random(RandomType::Standard_Has_Seal + anteStr) <= 0.8) { + seal = Item::No_Seal; + } else { + double sealPoll = random(RandomType::Standard_Seal + anteStr); + if (sealPoll > 0.75) { + seal = Item::Red_Seal; + } else if (sealPoll > 0.5) { + seal = Item::Blue_Seal; + } else if (sealPoll > 0.25) { + seal = Item::Gold_Seal; + } else { + seal = Item::Purple_Seal; + } + } + + return Card(base, enhancement, edition, seal); +}; + +inline std::vector Instance::nextArcanaPack(int size, int ante) { + std::vector pack; + for (int i = 0; i < size; i++) { + if (isVoucherActive(Item::Omen_Globe) && + random(RandomType::Omen_Globe) > 0.8) { + pack.push_back(nextSpectral(ItemSource::Omen_Globe, ante, true)); + } else { + pack.push_back(nextTarot(ItemSource::Arcana_Pack, ante, true)); + } + if (!params.showman) { + lock(pack[i]); + } + } + for (int i = 0; i < size; i++) { + unlock(pack[i]); + } + return pack; +}; + +inline std::vector Instance::nextCelestialPack(int size, int ante) { + std::vector pack; + for (int i = 0; i < size; i++) { + pack.push_back(nextPlanet(ItemSource::Celestial_Pack, ante, true)); + if (!params.showman) + lock(pack[i]); + } + for (int i = 0; i < size; i++) { + unlock(pack[i]); + } + return pack; +}; + +inline std::vector Instance::nextSpectralPack(int size, int ante) { + std::vector pack; + for (int i = 0; i < size; i++) { + pack.push_back(nextSpectral(ItemSource::Spectral_Pack, ante, true)); + if (!params.showman) + lock(pack[i]); + } + for (int i = 0; i < size; i++) { + unlock(pack[i]); + } + return pack; +}; + +inline std::vector Instance::nextStandardPack(int size, int ante) { + std::vector pack; + for (int i = 0; i < size; i++) { + pack.push_back(nextStandardCard(ante)); + } + return pack; +}; + +inline std::vector Instance::nextBuffoonPack(int size, int ante) { + std::vector pack; + for (int i = 0; i < size; i++) { + pack.push_back(nextJoker(ItemSource::Buffoon_Pack, ante, true)); + if (!params.showman) + lock(pack[i].joker); + } + for (int i = 0; i < size; i++) { + unlock(pack[i].joker); + } + return pack; +}; + +// Misc +inline bool Instance::isVoucherActive(Item voucher) { + return params.vouchers[(int)voucher - (int)Item::Overstock]; +} + +inline void Instance::activateVoucher(Item voucher) { + params.vouchers[(int)voucher - (int)Item::Overstock] = true; + lock(voucher); + // Unlock next level voucher + for (unsigned long int i = 0; i < VOUCHERS.size(); i += 2) { + if (VOUCHERS[i] == voucher) { + unlock(VOUCHERS[i + 1]); + }; + }; +}; + +inline Item Instance::nextVoucher(int ante) { + return randchoice(RandomType::Voucher + anteToString(ante), VOUCHERS); +} + +inline void Instance::setDeck(Item deck) { + params.deck = deck; + if (deck == Item::Magic_Deck) { + activateVoucher(Item::Crystal_Ball); + } + if (deck == Item::Nebula_Deck) { + activateVoucher(Item::Telescope); + } + if (deck == Item::Zodiac_Deck) { + activateVoucher(Item::Tarot_Merchant); + activateVoucher(Item::Planet_Merchant); + activateVoucher(Item::Overstock); + } +} + +inline void Instance::setStake(Item stake) { params.stake = stake; } + +inline Item Instance::nextTag(int ante) { + return randchoice(RandomType::Tags + anteToString(ante), TAGS); +} + +inline Item Instance::nextBoss(int ante) { + constexpr int MAX_BOSSES = + 16; // Adjust this value based on the maximum number of bosses you expect + std::array bossPool; + int numBosses = 0; + + for (unsigned long int i = 0; i < BOSSES.size(); i++) { + if (!isLocked(BOSSES[i])) { + if ((ante % 8 == 0 && BOSSES[i] > Item::B_F_BEGIN) || + (ante % 8 != 0 && BOSSES[i] < Item::B_F_BEGIN)) { + bossPool[numBosses++] = BOSSES[i]; + } + } + } + + if (numBosses == 0) { + for (unsigned long int i = 0; i < BOSSES.size(); i++) { + if ((ante % 8 == 0 && BOSSES[i] > Item::B_F_BEGIN) || + (ante % 8 != 0 && BOSSES[i] < Item::B_F_BEGIN)) { + unlock(BOSSES[i]); + } + } + return nextBoss(ante); + } + + Item chosenBoss = randchoice("boss", bossPool); + lock(chosenBoss); + return chosenBoss; +} + +#endif \ No newline at end of file diff --git a/immolate/immolate.cpp b/immolate/immolate.cpp new file mode 100644 index 0000000..82719d6 --- /dev/null +++ b/immolate/immolate.cpp @@ -0,0 +1,246 @@ +#include "functions.hpp" +#include "minijson.hpp" +#include "search.hpp" +#include +#include + +Item BRAINSTORM_PACK = Item::RETRY; +Item BRAINSTORM_TAG = Item::Charm_Tag; +long BRAINSTORM_SOULS = 1; + +long filter(Instance inst) { + if (BRAINSTORM_PACK != Item::RETRY) { + inst.cache.generatedFirstPack = true; // we don't care about Pack 1 + if (inst.nextPack(1) != BRAINSTORM_PACK) { + return 0; + } + } + if (BRAINSTORM_TAG != Item::RETRY) { + if (inst.nextTag(1) != BRAINSTORM_TAG) { + return 0; + } + } + if (BRAINSTORM_SOULS > 0) { + for (int i = 1; i <= BRAINSTORM_SOULS; i++) { + auto tarots = inst.nextArcanaPack(5, 1); // Mega Arcana Pack + bool found_soul = false; + for (int t = 0; t < 5; t++) { + if (tarots[t] == Item::The_Soul) { + found_soul = true; + break; + } + } + if (!found_soul) { + return 0; + } + } + } + return 1; +}; + +IMMOLATE_API std::string brainstorm_cpp(std::string seed, std::string pack, +std::string tag, double souls) { BRAINSTORM_PACK = stringToItem(pack); + BRAINSTORM_TAG = stringToItem(tag); + BRAINSTORM_SOULS = souls; + Search search(filter, seed, 1, 100000000); + search.exitOnFind = true; + return search.search(); +} + +struct Step { + std::string op; + mini_json::Value args; +}; + +static Item parseItemSafe(const mini_json::Value &v) { + if (!v.isString()) return Item::RETRY; + return stringToItem(v.getString()); +} + +static bool matchesItem(const Item actual, const mini_json::Value &args) { + const auto &eq = args["equals"]; + if (eq.isString() && actual != parseItemSafe(eq)) return false; + const auto &inArr = args["in"]; + if (inArr.isArray()) { + bool ok = false; + for (const auto &el : inArr.array) { + if (el.isString() && actual == parseItemSafe(el)) { + ok = true; + break; + } + } + if (!ok) return false; + } + return true; +} + +static bool matchesJoker(const JokerData &jd, const mini_json::Value &match) { + if (!match.isObject()) return true; + if (match["joker"].isString() && jd.joker != parseItemSafe(match["joker"])) return false; + if (match["rarity"].isString() && jd.rarity != parseItemSafe(match["rarity"])) return false; + if (match["edition"].isString() && jd.edition != parseItemSafe(match["edition"])) return false; + const auto &stickers = match["stickers"]; + if (stickers.isObject()) { + if (stickers["eternal"].isBool() && jd.stickers.eternal != stickers["eternal"].getBool()) return false; + if (stickers["perishable"].isBool() && jd.stickers.perishable != stickers["perishable"].getBool()) return false; + if (stickers["rental"].isBool() && jd.stickers.rental != stickers["rental"].getBool()) return false; + } + return true; +} + +static long long getNumber(const mini_json::Value &v, long long def) { + return v.isNumber() ? static_cast(v.number) : def; +} + +static bool applyStep(const Step &step, Instance &inst) { + const auto &args = step.args; + if (step.op == "tag") { + int idx = static_cast(getNumber(args["index"], 1)); + Item val = inst.nextTag(idx); + return matchesItem(val, args); + } + if (step.op == "pack") { + int idx = static_cast(getNumber(args["index"], 1)); + Item val = inst.nextPack(idx); + return matchesItem(val, args); + } + if (step.op == "voucher") { + int idx = static_cast(getNumber(args["index"], 1)); + Item val = inst.nextVoucher(idx); + if (!matchesItem(val, args)) return false; + if (args["activate"].getBool(false)) { + inst.activateVoucher(val); + } + return true; + } + if (step.op == "boss") { + int idx = static_cast(getNumber(args["index"], 1)); + Item val = inst.nextBoss(idx); + return matchesItem(val, args); + } + if (step.op == "joker") { + int draw = static_cast(getNumber(args["draw"], 1)); + int ante = static_cast(getNumber(args["ante"], 1)); + bool stickers = args["has_stickers"].getBool(true); + std::string source = args["source"].getString("Brainstorm_Joker"); + JokerData jd = inst.nextJoker(source, ante, stickers); + // Advance draws if draw > 1 + for (int i = 1; i < draw; i++) { + inst.nextJoker(source, ante, stickers); + } + return matchesJoker(jd, args["match"]); + } + if (step.op == "joker_window") { + int limit = static_cast(getNumber(args["limit"], 1)); + int ante = static_cast(getNumber(args["ante"], 1)); + bool stickers = args["has_stickers"].getBool(true); + std::string source = args["source"].getString("Brainstorm_Joker_Window"); + const auto &match = args["any"]["match"].isObject() ? args["any"]["match"] : args["match"]; + for (int i = 0; i < limit; i++) { + JokerData jd = inst.nextJoker(source, ante, stickers); + if (matchesJoker(jd, match)) return true; + } + return false; + } + if (step.op == "state") { + std::string field = args["field"].getString(""); + if (field == "id") { + long long id = inst.seed.getID(); + const auto &eq = args["equals"]; + if (eq.isNumber() && id != static_cast(eq.number)) return false; + const auto &range = args["range"]; + if (range.isArray() && range.array.size() >= 2) { + long long lo = static_cast(range.array[0].number); + long long hi = static_cast(range.array[1].number); + if (id < lo || id > hi) return false; + } + } + return true; + } + if (step.op == "set") { + const auto &deck = args["deck"]; + const auto &stake = args["stake"]; + if (deck.isString()) inst.setDeck(stringToItem(deck.getString())); + if (stake.isString() || stake.isNumber()) { + if (stake.isString()) { + inst.setStake(stringToItem(stake.getString())); + } else { + inst.setStake(static_cast(static_cast(stake.number))); + } + } + return true; + } + // Unknown op -> fail safely + return false; +} + +static void collectSteps(const mini_json::Value &node, std::vector &out) { + if (node.isObject() && node["all"].isArray()) { + for (const auto &child : node["all"].array) { + collectSteps(child, out); + } + return; + } + if (node.isObject() && node["op"].isString()) { + Step s; + s.op = node["op"].getString(); + s.args = node["args"]; + out.push_back(s); + } +} + +static const char *dupCString(const std::string &str) { + char *c_result = (char *)malloc(str.length() + 1); + if (!c_result) return nullptr; + std::strcpy(c_result, str.c_str()); + return c_result; +} + +IMMOLATE_API const char *brainstorm_query(const char *seed, + const char *query_json) { + std::string seed_str = seed ? seed : ""; + std::string query_str = query_json ? query_json : ""; + + mini_json::Value root; + if (!mini_json::parse(query_str, root) || !root.isObject()) { + return dupCString(""); + } + + std::vector steps; + collectSteps(root["filter"], steps); + if (steps.empty()) { + return dupCString(""); + } + + const auto &search = root["search"]; + int threads = static_cast(getNumber(search["threads"], 1)); + long long max_seeds = getNumber(search["max_seeds"], 100000000); + bool exit_on_find = search["exit_on_find"].getBool(true); + + Search s([steps](Instance inst) { + for (const auto &step : steps) { + if (!applyStep(step, inst)) return 0; + } + return 1; + }, seed_str, threads, max_seeds > 0 ? max_seeds : 100000000); + s.exitOnFind = exit_on_find; + std::string result = s.search(); + return dupCString(result); +} + +extern "C" { + IMMOLATE_API const char* brainstorm(const char* seed, const char* pack, +const char* tag, double souls) { std::string cpp_seed(seed); std::string +cpp_pack(pack); std::string cpp_tag(tag); std::string result = +brainstorm_cpp(cpp_seed, cpp_pack, cpp_tag, souls); + + char* c_result = (char*)malloc(result.length() + 1); + strcpy(c_result, result.c_str()); + + return c_result; + } + + IMMOLATE_API void free_result(const char* result) { + free((void*)result); + } +} diff --git a/immolate/immolate.hpp b/immolate/immolate.hpp new file mode 100644 index 0000000..8cd5188 --- /dev/null +++ b/immolate/immolate.hpp @@ -0,0 +1,143 @@ + +#include +#ifdef _WIN32 +#ifdef BUILDING_DLL +#define IMMOLATE_API __declspec(dllexport) +#else +#define IMMOLATE_API __declspec(dllimport) +#endif +#else +#define IMMOLATE_API +#endif + +// Declare the functions with IMMOLATE_API +IMMOLATE_API std::string brainstorm_cpp(std::string seed, std::string pack, + std::string tag, double souls); +IMMOLATE_API const char *brainstorm_query(const char *seed, + const char *query_json); +extern "C" { +IMMOLATE_API const char *brainstorm(const char *seed, const char *pack, + const char *tag, double souls); +IMMOLATE_API void free_result(const char *result); +} + +#ifdef __EMSCRIPTEN__ +#include +using namespace emscripten; +EMSCRIPTEN_BINDINGS(Immolate) { + // instance.hpp + register_vector("VectorStr"); + register_vector("VectorJkr"); + register_vector("VectorCrd"); + class_("InstParams") + .constructor<>() + .constructor() + .property("deck", &InstParams::deck) + .property("stake", &InstParams::stake) + .property("showman", &InstParams::showman) + .property("vouchers", &InstParams::vouchers) + .property("version", &InstParams::version); + class_("Instance") + .constructor() + .function("get_node", &Instance::get_node) + .function("random", &Instance::random) + .function("randint", &Instance::randint) + .function("randchoice", &Instance::randchoice) + .property("params", &Instance::params) + .property("seed", &Instance::seed) + + // functions.hpp + .function("lock", &Instance::lock) + .function("unlock", &Instance::unlock) + .function("isLocked", &Instance::isLocked) + .function("initLocks", &Instance::initLocks) + .function("initUnlocks", &Instance::initUnlocks) + .function("nextTarot", &Instance::nextTarot) + .function("nextPlanet", &Instance::nextPlanet) + .function("nextSpectral", &Instance::nextSpectral) + .function("nextJoker", &Instance::nextJoker) + .function("getShopInstance", &Instance::getShopInstance) + .function("nextShopItem", &Instance::nextShopItem) + .function("nextPack", &Instance::nextPack) + .function("nextStandardCard", &Instance::nextStandardCard) + .function("nextArcanaPack", &Instance::nextArcanaPack) + .function("nextCelestialPack", &Instance::nextCelestialPack) + .function("nextSpectralPack", &Instance::nextSpectralPack) + .function("nextBuffoonPack", &Instance::nextBuffoonPack) + .function("nextStandardPack", &Instance::nextStandardPack) + .function("isVoucherActive", &Instance::isVoucherActive) + .function("activateVoucher", &Instance::activateVoucher) + .function("nextVoucher", &Instance::nextVoucher) + .function("setDeck", &Instance::setDeck) + .function("setStake", &Instance::setStake) + .function("nextTag", &Instance::nextTag) + .function("nextBoss", &Instance::nextBoss); + function("packInfo", &packInfo); + + // items.hpp + class_("ShopInstance") + .constructor<>() + .constructor() + .function("getTotalRate", &ShopInstance::getTotalRate) + .property("jokerRate", &ShopInstance::jokerRate) + .property("tarotRate", &ShopInstance::tarotRate) + .property("planetRate", &ShopInstance::planetRate) + .property("playingCardRate", &ShopInstance::playingCardRate) + .property("spectralRate", &ShopInstance::spectralRate); + class_("JokerStickers") + .constructor<>() + .constructor() + .property("eternal", &JokerStickers::eternal) + .property("perishable", &JokerStickers::perishable) + .property("rental", &JokerStickers::rental); + class_("JokerData") + .constructor<>() + .constructor() + .property("joker", &JokerData::joker) + .property("rarity", &JokerData::rarity) + .property("edition", &JokerData::edition) + .property("stickers", &JokerData::stickers); + class_("ShopItem") + .constructor<>() + .constructor() + .constructor() + .property("type", &ShopItem::type) + .property("item", &ShopItem::item) + .property("jokerData", &ShopItem::jokerData); + class_("WeightedItem") + .constructor() + .property("item", &WeightedItem::item) + .property("weight", &WeightedItem::weight); + class_("Pack") + .constructor() + .property("type", &Pack::type) + .property("size", &Pack::size) + .property("choices", &Pack::choices); + class_("Card") + .constructor() + .property("base", &Card::base) + .property("enhancement", &Card::enhancement) + .property("edition", &Card::edition) + .property("seal", &Card::seal); + constant("ENHANCEMENTS", &ENHANCEMENTS); + constant("CARDS", &CARDS); + constant("SUITS", &SUITS); + constant("RANKS", &RANKS); + constant("TAROTS", &TAROTS); + constant("PLANETS", &PLANETS); + constant("COMMON_JOKERS", &COMMON_JOKERS); + constant("UNCOMMON_JOKERS", &UNCOMMON_JOKERS); + constant("RARE_JOKERS", &RARE_JOKERS); + constant("LEGENDARY_JOKERS", &LEGENDARY_JOKERS); + constant("VOUCHERS", &VOUCHERS); + constant("SPECTRALS", &SPECTRALS); + constant("TAGS", &TAGS); + constant("BOSSES", &BOSSES); + + // util.hpp + function("pseudohash", &pseudohash) class_("LuaRandom") + .constructor<>() + .constructor() + .function("random", &LuaRandom::random); +} +#endif diff --git a/immolate/instance.hpp b/immolate/instance.hpp new file mode 100644 index 0000000..4489a81 --- /dev/null +++ b/immolate/instance.hpp @@ -0,0 +1,135 @@ +#include "items.hpp" +#include "seed.hpp" +#include "util.hpp" +#include +#include +#pragma once + +struct Cache { + std::map nodes; + bool generatedFirstPack = false; +}; + +struct InstParams { + Item deck; + Item stake; + bool showman; + int sixesFactor; + long version; + bool vouchers[32] = {false}; + InstParams() { + deck = Item::Red_Deck; + stake = Item::White_Stake; + showman = false; + sixesFactor = 1; + version = 10103; // 1.0.1c + } + InstParams(Item d, Item s, bool show, long v) { + deck = d; + stake = s; + showman = show; + sixesFactor = 1; + version = v; + } +}; + +struct Instance { + bool locked[(int)Item::ITEMS_END] = {false}; + Seed &seed; + double hashedSeed; + Cache cache; + InstParams params; + LuaRandom rng; + Instance(Seed &s) : seed(s) { + hashedSeed = s.pseudohash(0); + params = InstParams(); + rng = LuaRandom(0); + }; + void reset(Seed &s) { // This is slow, use next() unless necessary + seed = s; + hashedSeed = s.pseudohash(0); + params = InstParams(); + cache.nodes + .clear(); // Somehow `clear` is faster than swapping with empty map + cache.generatedFirstPack = false; + }; + void next() { + seed.next(); + hashedSeed = seed.pseudohash(0); + params = InstParams(); + cache.nodes.clear(); + cache.generatedFirstPack = false; + } + double get_node(std::string ID) { + if (cache.nodes.count(ID) == 0) { + cache.nodes[ID] = pseudohash_from(ID, seed.pseudohash(ID.length())); + } + cache.nodes[ID] = + round13(fract(cache.nodes[ID] * 1.72431234 + 2.134453429141)); + return (cache.nodes[ID] + hashedSeed) / 2; + } + double random(std::string ID) { + rng = LuaRandom(get_node(ID)); + return rng.random(); + } + int randint(std::string ID, int min, int max) { + rng = LuaRandom(get_node(ID)); + return rng.randint(min, max); + } + template + Item randchoice(std::string ID, const std::array &items) { + rng = LuaRandom(get_node(ID)); + Item item = items[rng.randint(0, items.size() - 1)]; + if ((params.showman == false && isLocked(item)) || item == Item::RETRY) { + int resample = 2; + while (true) { + rng = LuaRandom(get_node(ID + "_resample" + anteToString(resample))); + Item item = items[rng.randint(0, items.size() - 1)]; + resample++; + if ((item != Item::RETRY && !isLocked(item)) || resample > 1000) + return item; + } + } + return item; + } + template + Item randweightedchoice(std::string ID, + const std::array &items) { + rng = LuaRandom(get_node(ID)); + double poll = rng.random() * items[0].weight; + int idx = 1; + double weight = 0; + while (weight < poll) { + weight += items[idx].weight; + idx++; + } + return items[idx - 1].item; + } + + // Functions defined in functions.hpp + void lock(Item item); + void unlock(Item item); + bool isLocked(Item item); + void initLocks(int ante, bool freshProfile, bool freshRun); + void initUnlocks(int ante, bool freshProfile); + Item nextTarot(std::string source, int ante, bool soulable); + Item nextPlanet(std::string source, int ante, bool soulable); + Item nextSpectral(std::string source, int ante, bool soulable); + JokerData nextJoker(std::string source, int ante, bool hasStickers); + ShopInstance getShopInstance(); + ShopItem nextShopItem(int ante); + Item nextPack(int ante); + std::vector nextArcanaPack(int size, int ante); + std::vector nextCelestialPack(int size, int ante); + std::vector nextSpectralPack(int size, int ante); + std::vector nextBuffoonPack(int size, int ante); + std::vector nextStandardPack(int size, int ante); + Card nextStandardCard(int ante); + bool isVoucherActive(Item voucher); + void activateVoucher(Item voucher); + Item nextVoucher(int ante); + void setDeck(Item deck); + void setStake(Item stake); + Item nextTag(int ante); + Item nextBoss(int ante); +}; \ No newline at end of file diff --git a/immolate/items.cpp b/immolate/items.cpp new file mode 100644 index 0000000..eb9a04e --- /dev/null +++ b/immolate/items.cpp @@ -0,0 +1,356 @@ +// #include "items.hpp" + +// std::vector ENHANCEMENTS = { +// Item::Bonus_Card, Item::Mult_Card, Item::Wild_Card, Item::Glass_Card, +// Item::Steel_Card, Item::Stone_Card, Item::Gold_Card, Item::Lucky_Card}; + +// std::vector CARDS = { +// Item::C_2, Item::C_3, Item::C_4, Item::C_5, Item::C_6, Item::C_7, +// Item::C_8, Item::C_9, Item::C_A, Item::C_J, Item::C_K, Item::C_Q, +// Item::C_T, Item::D_2, Item::D_3, Item::D_4, Item::D_5, Item::D_6, +// Item::D_7, Item::D_8, Item::D_9, Item::D_A, Item::D_J, Item::D_K, +// Item::D_Q, Item::D_T, Item::H_2, Item::H_3, Item::H_4, Item::H_5, +// Item::H_6, Item::H_7, Item::H_8, Item::H_9, Item::H_A, Item::H_J, +// Item::H_K, Item::H_Q, Item::H_T, Item::S_2, Item::S_3, Item::S_4, +// Item::S_5, Item::S_6, Item::S_7, Item::S_8, Item::S_9, Item::S_A, +// Item::S_J, Item::S_K, Item::S_Q, Item::S_T}; + +// std::vector SUITS = {Item::Spades, Item::Hearts, Item::Clubs, +// Item::Diamonds}; + +// std::vector RANKS = {Item::_2, Item::_3, Item::_4, Item::_5, +// Item::_6, Item::_7, Item::_8, Item::_9, +// Item::_10, Item::Jack, Item::Queen, Item::King, +// Item::Ace}; + +// std::vector PACKS = { +// WeightedItem(Item::RETRY, 22.42), // total +// WeightedItem(Item::Arcana_Pack, 4), +// WeightedItem(Item::Jumbo_Arcana_Pack, 2), +// WeightedItem(Item::Mega_Arcana_Pack, 0.5), +// WeightedItem(Item::Celestial_Pack, 4), +// WeightedItem(Item::Jumbo_Celestial_Pack, 2), +// WeightedItem(Item::Mega_Celestial_Pack, 0.5), +// WeightedItem(Item::Standard_Pack, 4), +// WeightedItem(Item::Jumbo_Standard_Pack, 2), +// WeightedItem(Item::Mega_Standard_Pack, 0.5), +// WeightedItem(Item::Buffoon_Pack, 1.2), +// WeightedItem(Item::Jumbo_Buffoon_Pack, 0.6), +// WeightedItem(Item::Mega_Buffoon_Pack, 0.15), +// WeightedItem(Item::Spectral_Pack, 0.6), +// WeightedItem(Item::Jumbo_Spectral_Pack, 0.3), +// WeightedItem(Item::Mega_Spectral_Pack, 0.07)}; + +// std::vector TAROTS = {Item::The_Fool, +// Item::The_Magician, +// Item::The_High_Priestess, +// Item::The_Empress, +// Item::The_Emperor, +// Item::The_Hierophant, +// Item::The_Lovers, +// Item::The_Chariot, +// Item::Justice, +// Item::The_Hermit, +// Item::The_Wheel_of_Fortune, +// Item::Strength, +// Item::The_Hanged_Man, +// Item::Death, +// Item::Temperance, +// Item::The_Devil, +// Item::The_Tower, +// Item::The_Star, +// Item::The_Moon, +// Item::The_Sun, +// Item::Judgement, +// Item::The_World}; + +// std::vector PLANETS = {Item::Mercury, Item::Venus, Item::Earth, +// Item::Mars, Item::Jupiter, Item::Saturn, +// Item::Uranus, Item::Neptune, Item::Pluto, +// Item::Planet_X, Item::Ceres, Item::Eris}; + +// std::vector COMMON_JOKERS_100 = {Item::Joker, +// Item::Greedy_Joker, +// Item::Lusty_Joker, +// Item::Wrathful_Joker, +// Item::Gluttonous_Joker, +// Item::Jolly_Joker, +// Item::Zany_Joker, +// Item::Mad_Joker, +// Item::Crazy_Joker, +// Item::Droll_Joker, +// Item::Sly_Joker, +// Item::Wily_Joker, +// Item::Clever_Joker, +// Item::Devious_Joker, +// Item::Crafty_Joker, +// Item::Half_Joker, +// Item::Credit_Card, +// Item::Banner, +// Item::Mystic_Summit, +// Item::_8_Ball, +// Item::Misprint, +// Item::Raised_Fist, +// Item::Chaos_the_Clown, +// Item::Scary_Face, +// Item::Abstract_Joker, +// Item::Delayed_Gratification, +// Item::Gros_Michel, +// Item::Even_Steven, +// Item::Odd_Todd, +// Item::Scholar, +// Item::Business_Card, +// Item::Supernova, +// Item::Ride_the_Bus, +// Item::Egg, +// Item::Runner, +// Item::Ice_Cream, +// Item::Splash, +// Item::Blue_Joker, +// Item::Faceless_Joker, +// Item::Green_Joker, +// Item::Superposition, +// Item::To_Do_List, +// Item::Cavendish, +// Item::Red_Card, +// Item::Square_Joker, +// Item::Riff_raff, +// Item::Photograph, +// Item::Mail_In_Rebate, +// Item::Hallucination, +// Item::Fortune_Teller, +// Item::Juggler, +// Item::Drunkard, +// Item::Golden_Joker, +// Item::Popcorn, +// Item::Walkie_Talkie, +// Item::Smiley_Face, +// Item::Golden_Ticket, +// Item::Swashbuckler, +// Item::Hanging_Chad, +// Item::Shoot_the_Moon}; + +// std::vector COMMON_JOKERS = { +// Item::Joker, +// Item::Greedy_Joker, +// Item::Lusty_Joker, +// Item::Wrathful_Joker, +// Item::Gluttonous_Joker, +// Item::Jolly_Joker, +// Item::Zany_Joker, +// Item::Mad_Joker, +// Item::Crazy_Joker, +// Item::Droll_Joker, +// Item::Sly_Joker, +// Item::Wily_Joker, +// Item::Clever_Joker, +// Item::Devious_Joker, +// Item::Crafty_Joker, +// Item::Half_Joker, +// Item::Credit_Card, +// Item::Banner, +// Item::Mystic_Summit, +// Item::_8_Ball, +// Item::Misprint, +// Item::Raised_Fist, +// Item::Chaos_the_Clown, +// Item::Scary_Face, +// Item::Abstract_Joker, +// Item::Delayed_Gratification, +// Item::Gros_Michel, +// Item::Even_Steven, +// Item::Odd_Todd, +// Item::Scholar, +// Item::Business_Card, +// Item::Supernova, +// Item::Ride_the_Bus, +// Item::Egg, +// Item::Runner, +// Item::Ice_Cream, +// Item::Splash, +// Item::Blue_Joker, +// Item::Faceless_Joker, +// Item::Green_Joker, +// Item::Superposition, +// Item::To_Do_List, +// Item::Cavendish, +// Item::Red_Card, +// Item::Square_Joker, +// Item::Riff_raff, +// Item::Photograph, +// Item::Reserved_Parking, +// Item::Mail_In_Rebate, +// Item::Hallucination, +// Item::Fortune_Teller, +// Item::Juggler, +// Item::Drunkard, +// Item::Golden_Joker, +// Item::Popcorn, +// Item::Walkie_Talkie, +// Item::Smiley_Face, +// Item::Golden_Ticket, +// Item::Swashbuckler, +// Item::Hanging_Chad, +// Item::Shoot_the_Moon, +// }; + +// std::vector UNCOMMON_JOKERS_100 = { +// Item::Joker_Stencil, Item::Four_Fingers, +// Item::Mime, Item::Ceremonial_Dagger, +// Item::Marble_Joker, Item::Loyalty_Card, +// Item::Dusk, Item::Fibonacci, +// Item::Steel_Joker, Item::Hack, +// Item::Pareidolia, Item::Space_Joker, +// Item::Burglar, Item::Blackboard, +// Item::Constellation, Item::Hiker, +// Item::Card_Sharp, Item::Madness, +// Item::Vampire, Item::Shortcut, +// Item::Hologram, Item::Vagabond, +// Item::Cloud_9, Item::Rocket, +// Item::Midas_Mask, Item::Luchador, +// Item::Gift_Card, Item::Turtle_Bean, +// Item::Erosion, Item::Reserved_Parking, +// Item::To_the_Moon, Item::Stone_Joker, +// Item::Lucky_Cat, Item::Bull, +// Item::Diet_Cola, Item::Trading_Card, +// Item::Flash_Card, Item::Spare_Trousers, +// Item::Ramen, Item::Seltzer, +// Item::Castle, Item::Mr_Bones, +// Item::Acrobat, Item::Sock_and_Buskin, +// Item::Troubadour, Item::Certificate, +// Item::Smeared_Joker, Item::Throwback, +// Item::Rough_Gem, Item::Bloodstone, +// Item::Arrowhead, Item::Onyx_Agate, +// Item::Glass_Joker, Item::Showman, +// Item::Flower_Pot, Item::Merry_Andy, +// Item::Oops_All_6s, Item::The_Idol, +// Item::Seeing_Double, Item::Matador, +// Item::Stuntman, Item::Satellite, +// Item::Cartomancer, Item::Astronomer, +// Item::Burnt_Joker, Item::Bootstraps}; + +// std::vector UNCOMMON_JOKERS = { +// Item::Joker_Stencil, Item::Four_Fingers, +// Item::Mime, Item::Ceremonial_Dagger, +// Item::Marble_Joker, Item::Loyalty_Card, +// Item::Dusk, Item::Fibonacci, +// Item::Steel_Joker, Item::Hack, +// Item::Pareidolia, Item::Space_Joker, +// Item::Burglar, Item::Blackboard, +// Item::Sixth_Sense, Item::Constellation, +// Item::Hiker, Item::Card_Sharp, +// Item::Madness, Item::Seance, +// Item::Vampire, Item::Shortcut, +// Item::Hologram, Item::Cloud_9, +// Item::Rocket, Item::Midas_Mask, +// Item::Luchador, Item::Gift_Card, +// Item::Turtle_Bean, Item::Erosion, +// Item::To_the_Moon, Item::Stone_Joker, +// Item::Lucky_Cat, Item::Bull, +// Item::Diet_Cola, Item::Trading_Card, +// Item::Flash_Card, Item::Spare_Trousers, +// Item::Ramen, Item::Seltzer, +// Item::Castle, Item::Mr_Bones, +// Item::Acrobat, Item::Sock_and_Buskin, +// Item::Troubadour, Item::Certificate, +// Item::Smeared_Joker, Item::Throwback, +// Item::Rough_Gem, Item::Bloodstone, +// Item::Arrowhead, Item::Onyx_Agate, +// Item::Glass_Joker, Item::Showman, +// Item::Flower_Pot, Item::Merry_Andy, +// Item::Oops_All_6s, Item::The_Idol, +// Item::Seeing_Double, Item::Matador, +// Item::Satellite, Item::Cartomancer, +// Item::Astronomer, Item::Bootstraps, +// }; + +// std::vector RARE_JOKERS_100 = {Item::DNA, +// Item::Sixth_Sense, +// Item::Seance, +// Item::Baron, +// Item::Obelisk, +// Item::Baseball_Card, +// Item::Ancient_Joker, +// Item::Campfire, +// Item::Blueprint, +// Item::Wee_Joker, +// Item::Hit_the_Road, +// Item::The_Duo, +// Item::The_Trio, +// Item::The_Family, +// Item::The_Order, +// Item::The_Tribe, +// Item::Invisible_Joker, +// Item::Brainstorm, +// Item::Drivers_License}; + +// std::vector RARE_JOKERS = { +// Item::DNA, +// Item::Vagabond, +// Item::Baron, +// Item::Obelisk, +// Item::Baseball_Card, +// Item::Ancient_Joker, +// Item::Campfire, +// Item::Blueprint, +// Item::Wee_Joker, +// Item::Hit_the_Road, +// Item::The_Duo, +// Item::The_Trio, +// Item::The_Family, +// Item::The_Order, +// Item::The_Tribe, +// Item::Stuntman, +// Item::Invisible_Joker, +// Item::Brainstorm, +// Item::Drivers_License, +// Item::Burnt_Joker, +// }; + +// std::vector LEGENDARY_JOKERS = {Item::Canio, Item::Triboulet, +// Item::Yorick, Item::Chicot, +// Item::Perkeo}; + +// std::vector VOUCHERS = { +// Item::Overstock, Item::Overstock_Plus, Item::Clearance_Sale, +// Item::Liquidation, Item::Hone, Item::Glow_Up, +// Item::Reroll_Surplus, Item::Reroll_Glut, Item::Crystal_Ball, +// Item::Omen_Globe, Item::Telescope, Item::Observatory, +// Item::Grabber, Item::Nacho_Tong, Item::Wasteful, +// Item::Recyclomancy, Item::Tarot_Merchant, Item::Tarot_Tycoon, +// Item::Planet_Merchant, Item::Planet_Tycoon, Item::Seed_Money, +// Item::Money_Tree, Item::Blank, Item::Antimatter, +// Item::Magic_Trick, Item::Illusion, Item::Hieroglyph, +// Item::Petroglyph, Item::Directors_Cut, Item::Retcon, +// Item::Paint_Brush, Item::Palette}; + +// std::vector SPECTRALS = { +// Item::Familiar, Item::Grim, Item::Incantation, Item::Talisman, +// Item::Aura, Item::Wraith, Item::Sigil, Item::Ouija, +// Item::Ectoplasm, Item::Immolate, Item::Ankh, Item::Deja_Vu, +// Item::Hex, Item::Trance, Item::Medium, Item::Cryptid, +// Item::RETRY, // Soul +// Item::RETRY // Black_Hole +// }; + +// std::vector TAGS = { +// Item::Uncommon_Tag, Item::Rare_Tag, Item::Negative_Tag, +// Item::Foil_Tag, Item::Holographic_Tag, Item::Polychrome_Tag, +// Item::Investment_Tag, Item::Voucher_Tag, Item::Boss_Tag, +// Item::Standard_Tag, Item::Charm_Tag, Item::Meteor_Tag, +// Item::Buffoon_Tag, Item::Handy_Tag, Item::Garbage_Tag, +// Item::Ethereal_Tag, Item::Coupon_Tag, Item::Double_Tag, +// Item::Juggle_Tag, Item::D6_Tag, Item::Top_up_Tag, +// Item::Speed_Tag, Item::Orbital_Tag, Item::Economy_Tag}; + +// std::vector BOSSES = { +// Item::The_Arm, Item::The_Club, Item::The_Eye, +// Item::Amber_Acorn, Item::Cerulean_Bell, Item::Crimson_Heart, +// Item::Verdant_Leaf, Item::Violet_Vessel, Item::The_Fish, +// Item::The_Flint, Item::The_Goad, Item::The_Head, +// Item::The_Hook, Item::The_House, Item::The_Manacle, +// Item::The_Mark, Item::The_Mouth, Item::The_Needle, +// Item::The_Ox, Item::The_Pillar, Item::The_Plant, +// Item::The_Psychic, Item::The_Serpent, Item::The_Tooth, +// Item::The_Wall, Item::The_Water, Item::The_Wheel, +// Item::The_Window}; diff --git a/immolate/items.hpp b/immolate/items.hpp new file mode 100644 index 0000000..ded376d --- /dev/null +++ b/immolate/items.hpp @@ -0,0 +1,3578 @@ +#ifndef ITEMS_HPP +#define ITEMS_HPP + +#include +#include +#include +#include +#include + +enum class Item { + RETRY, + + // Jokers + J_BEGIN, + + J_C_BEGIN, + Joker, + Greedy_Joker, + Lusty_Joker, + Wrathful_Joker, + Gluttonous_Joker, + Jolly_Joker, + Zany_Joker, + Mad_Joker, + Crazy_Joker, + Droll_Joker, + Sly_Joker, + Wily_Joker, + Clever_Joker, + Devious_Joker, + Crafty_Joker, + Half_Joker, + Credit_Card, + Banner, + Mystic_Summit, + _8_Ball, + Misprint, + Raised_Fist, + Chaos_the_Clown, + Scary_Face, + Abstract_Joker, + Delayed_Gratification, + Gros_Michel, + Even_Steven, + Odd_Todd, + Scholar, + Business_Card, + Supernova, + Ride_the_Bus, + Egg, + Runner, + Ice_Cream, + Splash, + Blue_Joker, + Faceless_Joker, + Green_Joker, + Superposition, + To_Do_List, + Cavendish, + Red_Card, + Square_Joker, + Riff_raff, + Photograph, + Reserved_Parking, + Mail_In_Rebate, + Hallucination, + Fortune_Teller, + Juggler, + Drunkard, + Golden_Joker, + Popcorn, + Walkie_Talkie, + Smiley_Face, + Golden_Ticket, + Swashbuckler, + Hanging_Chad, + Shoot_the_Moon, + J_C_END, + + J_U_BEGIN, + Joker_Stencil, + Four_Fingers, + Mime, + Ceremonial_Dagger, + Marble_Joker, + Loyalty_Card, + Dusk, + Fibonacci, + Steel_Joker, + Hack, + Pareidolia, + Space_Joker, + Burglar, + Blackboard, + Sixth_Sense, + Constellation, + Hiker, + Card_Sharp, + Madness, + Seance, + Shortcut, + Hologram, + Cloud_9, + Rocket, + Midas_Mask, + Luchador, + Gift_Card, + Turtle_Bean, + Erosion, + To_the_Moon, + Stone_Joker, + Lucky_Cat, + Bull, + Diet_Cola, + Trading_Card, + Flash_Card, + Spare_Trousers, + Ramen, + Seltzer, + Castle, + Mr_Bones, + Acrobat, + Sock_and_Buskin, + Troubadour, + Certificate, + Smeared_Joker, + Throwback, + Rough_Gem, + Bloodstone, + Arrowhead, + Onyx_Agate, + Glass_Joker, + Showman, + Flower_Pot, + Merry_Andy, + Oops_All_6s, + The_Idol, + Seeing_Double, + Matador, + Stuntman, + Satellite, + Cartomancer, + Astronomer, + Bootstraps, + J_U_END, + + J_R_BEGIN, + DNA, + Vampire, + Vagabond, + Baron, + Obelisk, + Baseball_Card, + Ancient_Joker, + Campfire, + Blueprint, + Wee_Joker, + Hit_the_Road, + The_Duo, + The_Trio, + The_Family, + The_Order, + The_Tribe, + Invisible_Joker, + Brainstorm, + Drivers_License, + Burnt_Joker, + J_R_END, + + J_L_BEGIN, + Canio, + Triboulet, + Yorick, + Chicot, + Perkeo, + J_L_END, + + J_END, + + // Vouchers + V_BEGIN, + Overstock, + Overstock_Plus, + Clearance_Sale, + Liquidation, + Hone, + Glow_Up, + Reroll_Surplus, + Reroll_Glut, + Crystal_Ball, + Omen_Globe, + Telescope, + Observatory, + Grabber, + Nacho_Tong, + Wasteful, + Recyclomancy, + Tarot_Merchant, + Tarot_Tycoon, + Planet_Merchant, + Planet_Tycoon, + Seed_Money, + Money_Tree, + Blank, + Antimatter, + Magic_Trick, + Illusion, + Hieroglyph, + Petroglyph, + Directors_Cut, + Retcon, + Paint_Brush, + Palette, + V_END, + + // Tarots + T_BEGIN, + The_Fool, + The_Magician, + The_High_Priestess, + The_Empress, + The_Emperor, + The_Hierophant, + The_Lovers, + The_Chariot, + Justice, + The_Hermit, + The_Wheel_of_Fortune, + Strength, + The_Hanged_Man, + Death, + Temperance, + The_Devil, + The_Tower, + The_Star, + The_Moon, + The_Sun, + Judgement, + The_World, + T_END, + + // Planets + P_BEGIN, + Mercury, + Venus, + Earth, + Mars, + Jupiter, + Saturn, + Uranus, + Neptune, + Pluto, + Planet_X, + Ceres, + Eris, + P_END, + + // Hands + H_BEGIN, + Pair, + Three_of_a_Kind, + Full_House, + Four_of_a_Kind, + Flush, + Straight, + Two_Pair, + Straight_Flush, + High_Card, + Five_of_a_Kind, + Flush_House, + Flush_Five, + H_END, + + // Spectrals + S_BEGIN, + Familiar, + Grim, + Incantation, + Talisman, + Aura, + Wraith, + Sigil, + Ouija, + Ectoplasm, + Immolate, + Ankh, + Deja_Vu, + Hex, + Trance, + Medium, + Cryptid, + The_Soul, + Black_Hole, + S_END, + + // Enhancements + ENHANCEMENT_BEGIN, + No_Enhancement, + Bonus_Card, + Mult_Card, + Wild_Card, + Glass_Card, + Steel_Card, + Stone_Card, + Gold_Card, + Lucky_Card, + ENHANCEMENT_END, + + // Seals + SEAL_BEGIN, + No_Seal, + Gold_Seal, + Red_Seal, + Blue_Seal, + Purple_Seal, + SEAL_END, + + // Editions + E_BEGIN, + No_Edition, + Foil, + Holographic, + Polychrome, + Negative, + E_END, + + // Booster Packs + PACK_BEGIN, + Arcana_Pack, + Jumbo_Arcana_Pack, + Mega_Arcana_Pack, + Celestial_Pack, + Jumbo_Celestial_Pack, + Mega_Celestial_Pack, + Standard_Pack, + Jumbo_Standard_Pack, + Mega_Standard_Pack, + Buffoon_Pack, + Jumbo_Buffoon_Pack, + Mega_Buffoon_Pack, + Spectral_Pack, + Jumbo_Spectral_Pack, + Mega_Spectral_Pack, + PACK_END, + + // Tags + TAG_BEGIN, + Uncommon_Tag, + Rare_Tag, + Negative_Tag, + Foil_Tag, + Holographic_Tag, + Polychrome_Tag, + Investment_Tag, + Voucher_Tag, + Boss_Tag, + Standard_Tag, + Charm_Tag, + Meteor_Tag, + Buffoon_Tag, + Handy_Tag, + Garbage_Tag, + Ethereal_Tag, + Coupon_Tag, + Double_Tag, + Juggle_Tag, + D6_Tag, + Top_up_Tag, + Speed_Tag, + Orbital_Tag, + Economy_Tag, + TAG_END, + + // Blinds + B_BEGIN, + Small_Blind, + Big_Blind, + The_Hook, + The_Ox, + The_House, + The_Wall, + The_Wheel, + The_Arm, + The_Club, + The_Fish, + The_Psychic, + The_Goad, + The_Water, + The_Window, + The_Manacle, + The_Eye, + The_Mouth, + The_Plant, + The_Serpent, + The_Pillar, + The_Needle, + The_Head, + The_Tooth, + The_Flint, + The_Mark, + B_F_BEGIN, + Amber_Acorn, + Verdant_Leaf, + Violet_Vessel, + Crimson_Heart, + Cerulean_Bell, + B_F_END, + B_END, + + // Suits + SUIT_BEGIN, + Hearts, + Clubs, + Diamonds, + Spades, + SUIT_END, + + // Ranks + RANK_BEGIN, + _2, + _3, + _4, + _5, + _6, + _7, + _8, + _9, + _10, + Jack, + Queen, + King, + Ace, + RANK_END, + + // Cards + C_BEGIN, + C_2, + C_3, + C_4, + C_5, + C_6, + C_7, + C_8, + C_9, + C_A, + C_J, + C_K, + C_Q, + C_T, + D_2, + D_3, + D_4, + D_5, + D_6, + D_7, + D_8, + D_9, + D_A, + D_J, + D_K, + D_Q, + D_T, + H_2, + H_3, + H_4, + H_5, + H_6, + H_7, + H_8, + H_9, + H_A, + H_J, + H_K, + H_Q, + H_T, + S_2, + S_3, + S_4, + S_5, + S_6, + S_7, + S_8, + S_9, + S_A, + S_J, + S_K, + S_Q, + S_T, + C_END, + + // Decks + D_BEGIN, + Red_Deck, + Blue_Deck, + Yellow_Deck, + Green_Deck, + Black_Deck, + Magic_Deck, + Nebula_Deck, + Ghost_Deck, + Abandoned_Deck, + Checkered_Deck, + Zodiac_Deck, + Painted_Deck, + Anaglyph_Deck, + Plasma_Deck, + Erratic_Deck, + Challenge_Deck, + D_END, + + // Challenges + CHAL_BEGIN, + The_Omelette, + _15_Minute_City, + Rich_get_Richer, + On_a_Knifes_Edge, + X_ray_Vision, + Mad_World, + Luxury_Tax, + Non_Perishable, + Medusa, + Double_or_Nothing, + Typecast, + Inflation, + Bram_Poker, + Fragile, + Monolith, + Blast_Off, + Five_Card_Draw, + Golden_Needle, + Cruelty, + Jokerless, + CHAL_END, + + // Stakes + STAKE_BEGIN, + White_Stake, + Red_Stake, + Green_Stake, + Black_Stake, + Blue_Stake, + Purple_Stake, + Orange_Stake, + Gold_Stake, + STAKE_END, + + RARITY_BEGIN, + Common, + Uncommon, + Rare, + Legendary, + RARITY_END, + + TYPE_BEGIN, + T_Joker, + T_Tarot, + T_Planet, + T_Spectral, + T_Playing_Card, + TYPE_END, + + ITEMS_END +}; +inline std::string itemToString(Item i) { + switch (i) { + case Item::RETRY: + return "RETRY"; + case Item::J_BEGIN: + return "J BEGIN"; + case Item::J_C_BEGIN: + return "J C BEGIN"; + case Item::Joker: + return "Joker"; + case Item::Greedy_Joker: + return "Greedy Joker"; + case Item::Lusty_Joker: + return "Lusty Joker"; + case Item::Wrathful_Joker: + return "Wrathful Joker"; + case Item::Gluttonous_Joker: + return "Gluttonous Joker"; + case Item::Jolly_Joker: + return "Jolly Joker"; + case Item::Zany_Joker: + return "Zany Joker"; + case Item::Mad_Joker: + return "Mad Joker"; + case Item::Crazy_Joker: + return "Crazy Joker"; + case Item::Droll_Joker: + return "Droll Joker"; + case Item::Sly_Joker: + return "Sly Joker"; + case Item::Wily_Joker: + return "Wily Joker"; + case Item::Clever_Joker: + return "Clever Joker"; + case Item::Devious_Joker: + return "Devious Joker"; + case Item::Crafty_Joker: + return "Crafty Joker"; + case Item::Half_Joker: + return "Half Joker"; + case Item::Credit_Card: + return "Credit Card"; + case Item::Banner: + return "Banner"; + case Item::Mystic_Summit: + return "Mystic Summit"; + case Item::_8_Ball: + return "8 Ball"; + case Item::Misprint: + return "Misprint"; + case Item::Raised_Fist: + return "Raised Fist"; + case Item::Chaos_the_Clown: + return "Chaos the Clown"; + case Item::Scary_Face: + return "Scary Face"; + case Item::Abstract_Joker: + return "Abstract Joker"; + case Item::Delayed_Gratification: + return "Delayed Gratification"; + case Item::Gros_Michel: + return "Gros Michel"; + case Item::Even_Steven: + return "Even Steven"; + case Item::Odd_Todd: + return "Odd Todd"; + case Item::Scholar: + return "Scholar"; + case Item::Business_Card: + return "Business Card"; + case Item::Supernova: + return "Supernova"; + case Item::Ride_the_Bus: + return "Ride the Bus"; + case Item::Egg: + return "Egg"; + case Item::Runner: + return "Runner"; + case Item::Ice_Cream: + return "Ice Cream"; + case Item::Splash: + return "Splash"; + case Item::Blue_Joker: + return "Blue Joker"; + case Item::Faceless_Joker: + return "Faceless Joker"; + case Item::Green_Joker: + return "Green Joker"; + case Item::Superposition: + return "Superposition"; + case Item::To_Do_List: + return "To Do List"; + case Item::Cavendish: + return "Cavendish"; + case Item::Red_Card: + return "Red Card"; + case Item::Square_Joker: + return "Square Joker"; + case Item::Riff_raff: + return "Riff-raff"; + case Item::Photograph: + return "Photograph"; + case Item::Reserved_Parking: + return "Reserved Parking"; + case Item::Mail_In_Rebate: + return "Mail-In Rebate"; + case Item::Hallucination: + return "Hallucination"; + case Item::Fortune_Teller: + return "Fortune Teller"; + case Item::Juggler: + return "Juggler"; + case Item::Drunkard: + return "Drunkard"; + case Item::Golden_Joker: + return "Golden Joker"; + case Item::Popcorn: + return "Popcorn"; + case Item::Walkie_Talkie: + return "Walkie Talkie"; + case Item::Smiley_Face: + return "Smiley Face"; + case Item::Golden_Ticket: + return "Golden Ticket"; + case Item::Swashbuckler: + return "Swashbuckler"; + case Item::Hanging_Chad: + return "Hanging Chad"; + case Item::Shoot_the_Moon: + return "Shoot the Moon"; + case Item::J_C_END: + return "J C END"; + case Item::J_U_BEGIN: + return "J U BEGIN"; + case Item::Joker_Stencil: + return "Joker Stencil"; + case Item::Four_Fingers: + return "Four Fingers"; + case Item::Mime: + return "Mime"; + case Item::Ceremonial_Dagger: + return "Ceremonial Dagger"; + case Item::Marble_Joker: + return "Marble Joker"; + case Item::Loyalty_Card: + return "Loyalty Card"; + case Item::Dusk: + return "Dusk"; + case Item::Fibonacci: + return "Fibonacci"; + case Item::Steel_Joker: + return "Steel Joker"; + case Item::Hack: + return "Hack"; + case Item::Pareidolia: + return "Pareidolia"; + case Item::Space_Joker: + return "Space Joker"; + case Item::Burglar: + return "Burglar"; + case Item::Blackboard: + return "Blackboard"; + case Item::Sixth_Sense: + return "Sixth Sense"; + case Item::Constellation: + return "Constellation"; + case Item::Hiker: + return "Hiker"; + case Item::Card_Sharp: + return "Card Sharp"; + case Item::Madness: + return "Madness"; + case Item::Seance: + return "SΘance"; + case Item::Shortcut: + return "Shortcut"; + case Item::Hologram: + return "Hologram"; + case Item::Cloud_9: + return "Cloud 9"; + case Item::Rocket: + return "Rocket"; + case Item::Midas_Mask: + return "Midas Mask"; + case Item::Luchador: + return "Luchador"; + case Item::Gift_Card: + return "Gift Card"; + case Item::Turtle_Bean: + return "Turtle Bean"; + case Item::Erosion: + return "Erosion"; + case Item::To_the_Moon: + return "To the Moon"; + case Item::Stone_Joker: + return "Stone Joker"; + case Item::Lucky_Cat: + return "Lucky Cat"; + case Item::Bull: + return "Bull"; + case Item::Diet_Cola: + return "Diet Cola"; + case Item::Trading_Card: + return "Trading Card"; + case Item::Flash_Card: + return "Flash Card"; + case Item::Spare_Trousers: + return "Spare Trousers"; + case Item::Ramen: + return "Ramen"; + case Item::Seltzer: + return "Seltzer"; + case Item::Castle: + return "Castle"; + case Item::Mr_Bones: + return "Mr. Bones"; + case Item::Acrobat: + return "Acrobat"; + case Item::Sock_and_Buskin: + return "Sock and Buskin"; + case Item::Troubadour: + return "Troubadour"; + case Item::Certificate: + return "Certificate"; + case Item::Smeared_Joker: + return "Smeared Joker"; + case Item::Throwback: + return "Throwback"; + case Item::Rough_Gem: + return "Rough Gem"; + case Item::Bloodstone: + return "Bloodstone"; + case Item::Arrowhead: + return "Arrowhead"; + case Item::Onyx_Agate: + return "Onyx Agate"; + case Item::Glass_Joker: + return "Glass Joker"; + case Item::Showman: + return "Showman"; + case Item::Flower_Pot: + return "Flower Pot"; + case Item::Merry_Andy: + return "Merry Andy"; + case Item::Oops_All_6s: + return "Oops! All 6s"; + case Item::The_Idol: + return "The Idol"; + case Item::Seeing_Double: + return "Seeing Double"; + case Item::Matador: + return "Matador"; + case Item::Stuntman: + return "Stuntman"; + case Item::Satellite: + return "Satellite"; + case Item::Cartomancer: + return "Cartomancer"; + case Item::Astronomer: + return "Astronomer"; + case Item::Bootstraps: + return "Bootstraps"; + case Item::J_U_END: + return "J U END"; + case Item::J_R_BEGIN: + return "J R BEGIN"; + case Item::DNA: + return "DNA"; + case Item::Vampire: + return "Vampire"; + case Item::Vagabond: + return "Vagabond"; + case Item::Baron: + return "Baron"; + case Item::Obelisk: + return "Obelisk"; + case Item::Baseball_Card: + return "Baseball Card"; + case Item::Ancient_Joker: + return "Ancient Joker"; + case Item::Campfire: + return "Campfire"; + case Item::Blueprint: + return "Blueprint"; + case Item::Wee_Joker: + return "Wee Joker"; + case Item::Hit_the_Road: + return "Hit the Road"; + case Item::The_Duo: + return "The Duo"; + case Item::The_Trio: + return "The Trio"; + case Item::The_Family: + return "The Family"; + case Item::The_Order: + return "The Order"; + case Item::The_Tribe: + return "The Tribe"; + case Item::Invisible_Joker: + return "Invisible Joker"; + case Item::Brainstorm: + return "Brainstorm"; + case Item::Drivers_License: + return "Driver's License"; + case Item::Burnt_Joker: + return "Burnt Joker"; + case Item::J_R_END: + return "J R END"; + case Item::J_L_BEGIN: + return "J L BEGIN"; + case Item::Canio: + return "Canio"; + case Item::Triboulet: + return "Triboulet"; + case Item::Yorick: + return "Yorick"; + case Item::Chicot: + return "Chicot"; + case Item::Perkeo: + return "Perkeo"; + case Item::J_L_END: + return "J L END"; + case Item::J_END: + return "J END"; + case Item::V_BEGIN: + return "V BEGIN"; + case Item::Overstock: + return "Overstock"; + case Item::Overstock_Plus: + return "Overstock Plus"; + case Item::Clearance_Sale: + return "Clearance Sale"; + case Item::Liquidation: + return "Liquidation"; + case Item::Hone: + return "Hone"; + case Item::Glow_Up: + return "Glow Up"; + case Item::Reroll_Surplus: + return "Reroll Surplus"; + case Item::Reroll_Glut: + return "Reroll Glut"; + case Item::Crystal_Ball: + return "Crystal Ball"; + case Item::Omen_Globe: + return "Omen Globe"; + case Item::Telescope: + return "Telescope"; + case Item::Observatory: + return "Observatory"; + case Item::Grabber: + return "Grabber"; + case Item::Nacho_Tong: + return "Nacho Tong"; + case Item::Wasteful: + return "Wasteful"; + case Item::Recyclomancy: + return "Recyclomancy"; + case Item::Tarot_Merchant: + return "Tarot Merchant"; + case Item::Tarot_Tycoon: + return "Tarot Tycoon"; + case Item::Planet_Merchant: + return "Planet Merchant"; + case Item::Planet_Tycoon: + return "Planet Tycoon"; + case Item::Seed_Money: + return "Seed Money"; + case Item::Money_Tree: + return "Money Tree"; + case Item::Blank: + return "Blank"; + case Item::Antimatter: + return "Antimatter"; + case Item::Magic_Trick: + return "Magic Trick"; + case Item::Illusion: + return "Illusion"; + case Item::Hieroglyph: + return "Hieroglyph"; + case Item::Petroglyph: + return "Petroglyph"; + case Item::Directors_Cut: + return "Director's Cut"; + case Item::Retcon: + return "Retcon"; + case Item::Paint_Brush: + return "Paint Brush"; + case Item::Palette: + return "Palette"; + case Item::V_END: + return "V END"; + case Item::T_BEGIN: + return "T BEGIN"; + case Item::The_Fool: + return "The Fool"; + case Item::The_Magician: + return "The Magician"; + case Item::The_High_Priestess: + return "The High Priestess"; + case Item::The_Empress: + return "The Empress"; + case Item::The_Emperor: + return "The Emperor"; + case Item::The_Hierophant: + return "The Hierophant"; + case Item::The_Lovers: + return "The Lovers"; + case Item::The_Chariot: + return "The Chariot"; + case Item::Justice: + return "Justice"; + case Item::The_Hermit: + return "The Hermit"; + case Item::The_Wheel_of_Fortune: + return "The Wheel of Fortune"; + case Item::Strength: + return "Strength"; + case Item::The_Hanged_Man: + return "The Hanged Man"; + case Item::Death: + return "Death"; + case Item::Temperance: + return "Temperance"; + case Item::The_Devil: + return "The Devil"; + case Item::The_Tower: + return "The Tower"; + case Item::The_Star: + return "The Star"; + case Item::The_Moon: + return "The Moon"; + case Item::The_Sun: + return "The Sun"; + case Item::Judgement: + return "Judgement"; + case Item::The_World: + return "The World"; + case Item::T_END: + return "T END"; + case Item::P_BEGIN: + return "P BEGIN"; + case Item::Mercury: + return "Mercury"; + case Item::Venus: + return "Venus"; + case Item::Earth: + return "Earth"; + case Item::Mars: + return "Mars"; + case Item::Jupiter: + return "Jupiter"; + case Item::Saturn: + return "Saturn"; + case Item::Uranus: + return "Uranus"; + case Item::Neptune: + return "Neptune"; + case Item::Pluto: + return "Pluto"; + case Item::Planet_X: + return "Planet X"; + case Item::Ceres: + return "Ceres"; + case Item::Eris: + return "Eris"; + case Item::P_END: + return "P END"; + case Item::H_BEGIN: + return "H BEGIN"; + case Item::Pair: + return "Pair"; + case Item::Three_of_a_Kind: + return "Three of a Kind"; + case Item::Full_House: + return "Full House"; + case Item::Four_of_a_Kind: + return "Four of a Kind"; + case Item::Flush: + return "Flush"; + case Item::Straight: + return "Straight"; + case Item::Two_Pair: + return "Two Pair"; + case Item::Straight_Flush: + return "Straight Flush"; + case Item::High_Card: + return "High Card"; + case Item::Five_of_a_Kind: + return "Five of a Kind"; + case Item::Flush_House: + return "Flush House"; + case Item::Flush_Five: + return "Flush Five"; + case Item::H_END: + return "H END"; + case Item::S_BEGIN: + return "S BEGIN"; + case Item::Familiar: + return "Familiar"; + case Item::Grim: + return "Grim"; + case Item::Incantation: + return "Incantation"; + case Item::Talisman: + return "Talisman"; + case Item::Aura: + return "Aura"; + case Item::Wraith: + return "Wraith"; + case Item::Sigil: + return "Sigil"; + case Item::Ouija: + return "Ouija"; + case Item::Ectoplasm: + return "Ectoplasm"; + case Item::Immolate: + return "Immolate"; + case Item::Ankh: + return "Ankh"; + case Item::Deja_Vu: + return "Deja Vu"; + case Item::Hex: + return "Hex"; + case Item::Trance: + return "Trance"; + case Item::Medium: + return "Medium"; + case Item::Cryptid: + return "Cryptid"; + case Item::The_Soul: + return "The Soul"; + case Item::Black_Hole: + return "Black Hole"; + case Item::S_END: + return "S END"; + case Item::ENHANCEMENT_BEGIN: + return "ENHANCEMENT BEGIN"; + case Item::No_Enhancement: + return "No Enhancement"; + case Item::Bonus_Card: + return "Bonus Card"; + case Item::Mult_Card: + return "Mult Card"; + case Item::Wild_Card: + return "Wild Card"; + case Item::Glass_Card: + return "Glass Card"; + case Item::Steel_Card: + return "Steel Card"; + case Item::Stone_Card: + return "Stone Card"; + case Item::Gold_Card: + return "Gold Card"; + case Item::Lucky_Card: + return "Lucky Card"; + case Item::ENHANCEMENT_END: + return "ENHANCEMENT END"; + case Item::SEAL_BEGIN: + return "SEAL BEGIN"; + case Item::No_Seal: + return "No Seal"; + case Item::Gold_Seal: + return "Gold Seal"; + case Item::Red_Seal: + return "Red Seal"; + case Item::Blue_Seal: + return "Blue Seal"; + case Item::Purple_Seal: + return "Purple Seal"; + case Item::SEAL_END: + return "SEAL END"; + case Item::E_BEGIN: + return "E BEGIN"; + case Item::No_Edition: + return "No Edition"; + case Item::Foil: + return "Foil"; + case Item::Holographic: + return "Holographic"; + case Item::Polychrome: + return "Polychrome"; + case Item::Negative: + return "Negative"; + case Item::E_END: + return "E END"; + case Item::PACK_BEGIN: + return "PACK BEGIN"; + case Item::Arcana_Pack: + return "Arcana Pack"; + case Item::Jumbo_Arcana_Pack: + return "Jumbo Arcana Pack"; + case Item::Mega_Arcana_Pack: + return "Mega Arcana Pack"; + case Item::Celestial_Pack: + return "Celestial Pack"; + case Item::Jumbo_Celestial_Pack: + return "Jumbo Celestial Pack"; + case Item::Mega_Celestial_Pack: + return "Mega Celestial Pack"; + case Item::Standard_Pack: + return "Standard Pack"; + case Item::Jumbo_Standard_Pack: + return "Jumbo Standard Pack"; + case Item::Mega_Standard_Pack: + return "Mega Standard Pack"; + case Item::Buffoon_Pack: + return "Buffoon Pack"; + case Item::Jumbo_Buffoon_Pack: + return "Jumbo Buffoon Pack"; + case Item::Mega_Buffoon_Pack: + return "Mega Buffoon Pack"; + case Item::Spectral_Pack: + return "Spectral Pack"; + case Item::Jumbo_Spectral_Pack: + return "Jumbo Spectral Pack"; + case Item::Mega_Spectral_Pack: + return "Mega Spectral Pack"; + case Item::PACK_END: + return "PACK END"; + case Item::TAG_BEGIN: + return "TAG BEGIN"; + case Item::Uncommon_Tag: + return "Uncommon Tag"; + case Item::Rare_Tag: + return "Rare Tag"; + case Item::Negative_Tag: + return "Negative Tag"; + case Item::Foil_Tag: + return "Foil Tag"; + case Item::Holographic_Tag: + return "Holographic Tag"; + case Item::Polychrome_Tag: + return "Polychrome Tag"; + case Item::Investment_Tag: + return "Investment Tag"; + case Item::Voucher_Tag: + return "Voucher Tag"; + case Item::Boss_Tag: + return "Boss Tag"; + case Item::Standard_Tag: + return "Standard Tag"; + case Item::Charm_Tag: + return "Charm Tag"; + case Item::Meteor_Tag: + return "Meteor Tag"; + case Item::Buffoon_Tag: + return "Buffoon Tag"; + case Item::Handy_Tag: + return "Handy Tag"; + case Item::Garbage_Tag: + return "Garbage Tag"; + case Item::Ethereal_Tag: + return "Ethereal Tag"; + case Item::Coupon_Tag: + return "Coupon Tag"; + case Item::Double_Tag: + return "Double Tag"; + case Item::Juggle_Tag: + return "Juggle Tag"; + case Item::D6_Tag: + return "D6 Tag"; + case Item::Top_up_Tag: + return "Top-up Tag"; + case Item::Speed_Tag: + return "Speed Tag"; + case Item::Orbital_Tag: + return "Orbital Tag"; + case Item::Economy_Tag: + return "Economy Tag"; + case Item::TAG_END: + return "TAG END"; + case Item::B_BEGIN: + return "B BEGIN"; + case Item::Small_Blind: + return "Small Blind"; + case Item::Big_Blind: + return "Big Blind"; + case Item::The_Hook: + return "The Hook"; + case Item::The_Ox: + return "The Ox"; + case Item::The_House: + return "The House"; + case Item::The_Wall: + return "The Wall"; + case Item::The_Wheel: + return "The Wheel"; + case Item::The_Arm: + return "The Arm"; + case Item::The_Club: + return "The Club"; + case Item::The_Fish: + return "The Fish"; + case Item::The_Psychic: + return "The Psychic"; + case Item::The_Goad: + return "The Goad"; + case Item::The_Water: + return "The Water"; + case Item::The_Window: + return "The Window"; + case Item::The_Manacle: + return "The Manacle"; + case Item::The_Eye: + return "The Eye"; + case Item::The_Mouth: + return "The Mouth"; + case Item::The_Plant: + return "The Plant"; + case Item::The_Serpent: + return "The Serpent"; + case Item::The_Pillar: + return "The Pillar"; + case Item::The_Needle: + return "The Needle"; + case Item::The_Head: + return "The Head"; + case Item::The_Tooth: + return "The Tooth"; + case Item::The_Flint: + return "The Flint"; + case Item::The_Mark: + return "The Mark"; + case Item::B_F_BEGIN: + return "B F BEGIN"; + case Item::Amber_Acorn: + return "Amber Acorn"; + case Item::Verdant_Leaf: + return "Verdant Leaf"; + case Item::Violet_Vessel: + return "Violet Vessel"; + case Item::Crimson_Heart: + return "Crimson Heart"; + case Item::Cerulean_Bell: + return "Cerulean Bell"; + case Item::B_F_END: + return "B F END"; + case Item::B_END: + return "B END"; + case Item::SUIT_BEGIN: + return "SUIT BEGIN"; + case Item::Hearts: + return "Hearts"; + case Item::Clubs: + return "Clubs"; + case Item::Diamonds: + return "Diamonds"; + case Item::Spades: + return "Spades"; + case Item::SUIT_END: + return "SUIT END"; + case Item::RANK_BEGIN: + return "RANK BEGIN"; + case Item::_2: + return "2"; + case Item::_3: + return "3"; + case Item::_4: + return "4"; + case Item::_5: + return "5"; + case Item::_6: + return "6"; + case Item::_7: + return "7"; + case Item::_8: + return "8"; + case Item::_9: + return "9"; + case Item::_10: + return "10"; + case Item::Jack: + return "Jack"; + case Item::Queen: + return "Queen"; + case Item::King: + return "King"; + case Item::Ace: + return "Ace"; + case Item::RANK_END: + return "RANK END"; + case Item::C_BEGIN: + return "C BEGIN"; + case Item::C_2: + return "C 2"; + case Item::C_3: + return "C 3"; + case Item::C_4: + return "C 4"; + case Item::C_5: + return "C 5"; + case Item::C_6: + return "C 6"; + case Item::C_7: + return "C 7"; + case Item::C_8: + return "C 8"; + case Item::C_9: + return "C 9"; + case Item::C_A: + return "C A"; + case Item::C_J: + return "C J"; + case Item::C_K: + return "C K"; + case Item::C_Q: + return "C Q"; + case Item::C_T: + return "C T"; + case Item::D_2: + return "D 2"; + case Item::D_3: + return "D 3"; + case Item::D_4: + return "D 4"; + case Item::D_5: + return "D 5"; + case Item::D_6: + return "D 6"; + case Item::D_7: + return "D 7"; + case Item::D_8: + return "D 8"; + case Item::D_9: + return "D 9"; + case Item::D_A: + return "D A"; + case Item::D_J: + return "D J"; + case Item::D_K: + return "D K"; + case Item::D_Q: + return "D Q"; + case Item::D_T: + return "D T"; + case Item::H_2: + return "H 2"; + case Item::H_3: + return "H 3"; + case Item::H_4: + return "H 4"; + case Item::H_5: + return "H 5"; + case Item::H_6: + return "H 6"; + case Item::H_7: + return "H 7"; + case Item::H_8: + return "H 8"; + case Item::H_9: + return "H 9"; + case Item::H_A: + return "H A"; + case Item::H_J: + return "H J"; + case Item::H_K: + return "H K"; + case Item::H_Q: + return "H Q"; + case Item::H_T: + return "H T"; + case Item::S_2: + return "S 2"; + case Item::S_3: + return "S 3"; + case Item::S_4: + return "S 4"; + case Item::S_5: + return "S 5"; + case Item::S_6: + return "S 6"; + case Item::S_7: + return "S 7"; + case Item::S_8: + return "S 8"; + case Item::S_9: + return "S 9"; + case Item::S_A: + return "S A"; + case Item::S_J: + return "S J"; + case Item::S_K: + return "S K"; + case Item::S_Q: + return "S Q"; + case Item::S_T: + return "S T"; + case Item::C_END: + return "C END"; + case Item::D_BEGIN: + return "D BEGIN"; + case Item::Red_Deck: + return "Red Deck"; + case Item::Blue_Deck: + return "Blue Deck"; + case Item::Yellow_Deck: + return "Yellow Deck"; + case Item::Green_Deck: + return "Green Deck"; + case Item::Black_Deck: + return "Black Deck"; + case Item::Magic_Deck: + return "Magic Deck"; + case Item::Nebula_Deck: + return "Nebula Deck"; + case Item::Ghost_Deck: + return "Ghost Deck"; + case Item::Abandoned_Deck: + return "Abandoned Deck"; + case Item::Checkered_Deck: + return "Checkered Deck"; + case Item::Zodiac_Deck: + return "Zodiac Deck"; + case Item::Painted_Deck: + return "Painted Deck"; + case Item::Anaglyph_Deck: + return "Anaglyph Deck"; + case Item::Plasma_Deck: + return "Plasma Deck"; + case Item::Erratic_Deck: + return "Erratic Deck"; + case Item::Challenge_Deck: + return "Challenge Deck"; + case Item::D_END: + return "D END"; + case Item::CHAL_BEGIN: + return "CHAL BEGIN"; + case Item::The_Omelette: + return "The Omelette"; + case Item::_15_Minute_City: + return "15 Minute City"; + case Item::Rich_get_Richer: + return "Rich get Richer"; + case Item::On_a_Knifes_Edge: + return "On a Knife's Edge"; + case Item::X_ray_Vision: + return "X-ray Vision"; + case Item::Mad_World: + return "Mad World"; + case Item::Luxury_Tax: + return "Luxury Tax"; + case Item::Non_Perishable: + return "Non-Perishable"; + case Item::Medusa: + return "Medusa"; + case Item::Double_or_Nothing: + return "Double or Nothing"; + case Item::Typecast: + return "Typecast"; + case Item::Inflation: + return "Inflation"; + case Item::Bram_Poker: + return "Bram Poker"; + case Item::Fragile: + return "Fragile"; + case Item::Monolith: + return "Monolith"; + case Item::Blast_Off: + return "Blast Off"; + case Item::Five_Card_Draw: + return "Five-Card Draw"; + case Item::Golden_Needle: + return "Golden Needle"; + case Item::Cruelty: + return "Cruelty"; + case Item::Jokerless: + return "Jokerless"; + case Item::CHAL_END: + return "CHAL END"; + case Item::STAKE_BEGIN: + return "STAKE BEGIN"; + case Item::White_Stake: + return "White Stake"; + case Item::Red_Stake: + return "Red Stake"; + case Item::Green_Stake: + return "Green Stake"; + case Item::Black_Stake: + return "Black Stake"; + case Item::Blue_Stake: + return "Blue Stake"; + case Item::Purple_Stake: + return "Purple Stake"; + case Item::Orange_Stake: + return "Orange Stake"; + case Item::Gold_Stake: + return "Gold Stake"; + case Item::STAKE_END: + return "STAKE END"; + case Item::RARITY_BEGIN: + return "RARITY BEGIN"; + case Item::Common: + return "Common"; + case Item::Uncommon: + return "Uncommon"; + case Item::Rare: + return "Rare"; + case Item::Legendary: + return "Legendary"; + case Item::RARITY_END: + return "RARITY END"; + case Item::TYPE_BEGIN: + return "TYPE BEGIN"; + case Item::T_Joker: + return "T Joker"; + case Item::T_Tarot: + return "T Tarot"; + case Item::T_Planet: + return "T Planet"; + case Item::T_Spectral: + return "T Spectral"; + case Item::T_Playing_Card: + return "T Playing Card"; + case Item::TYPE_END: + return "TYPE END"; + default: + std::cout << "ERROR; stringToItem found no items... contact dev" + << std::endl; + EXIT_FAILURE; + } +} +inline Item stringToItem(std::string i) { + if (i == "RETRY") { + return Item::RETRY; + }; + if (i == "J BEGIN") { + return Item::J_BEGIN; + }; + if (i == "J C BEGIN") { + return Item::J_C_BEGIN; + }; + if (i == "Joker") { + return Item::Joker; + }; + if (i == "Greedy Joker") { + return Item::Greedy_Joker; + }; + if (i == "Lusty Joker") { + return Item::Lusty_Joker; + }; + if (i == "Wrathful Joker") { + return Item::Wrathful_Joker; + }; + if (i == "Gluttonous Joker") { + return Item::Gluttonous_Joker; + }; + if (i == "Jolly Joker") { + return Item::Jolly_Joker; + }; + if (i == "Zany Joker") { + return Item::Zany_Joker; + }; + if (i == "Mad Joker") { + return Item::Mad_Joker; + }; + if (i == "Crazy Joker") { + return Item::Crazy_Joker; + }; + if (i == "Droll Joker") { + return Item::Droll_Joker; + }; + if (i == "Sly Joker") { + return Item::Sly_Joker; + }; + if (i == "Wily Joker") { + return Item::Wily_Joker; + }; + if (i == "Clever Joker") { + return Item::Clever_Joker; + }; + if (i == "Devious Joker") { + return Item::Devious_Joker; + }; + if (i == "Crafty Joker") { + return Item::Crafty_Joker; + }; + if (i == "Half Joker") { + return Item::Half_Joker; + }; + if (i == "Credit Card") { + return Item::Credit_Card; + }; + if (i == "Banner") { + return Item::Banner; + }; + if (i == "Mystic Summit") { + return Item::Mystic_Summit; + }; + if (i == "8 Ball") { + return Item::_8_Ball; + }; + if (i == "Misprint") { + return Item::Misprint; + }; + if (i == "Raised Fist") { + return Item::Raised_Fist; + }; + if (i == "Chaos the Clown") { + return Item::Chaos_the_Clown; + }; + if (i == "Scary Face") { + return Item::Scary_Face; + }; + if (i == "Abstract Joker") { + return Item::Abstract_Joker; + }; + if (i == "Delayed Gratification") { + return Item::Delayed_Gratification; + }; + if (i == "Gros Michel") { + return Item::Gros_Michel; + }; + if (i == "Even Steven") { + return Item::Even_Steven; + }; + if (i == "Odd Todd") { + return Item::Odd_Todd; + }; + if (i == "Scholar") { + return Item::Scholar; + }; + if (i == "Business Card") { + return Item::Business_Card; + }; + if (i == "Supernova") { + return Item::Supernova; + }; + if (i == "Ride the Bus") { + return Item::Ride_the_Bus; + }; + if (i == "Egg") { + return Item::Egg; + }; + if (i == "Runner") { + return Item::Runner; + }; + if (i == "Ice Cream") { + return Item::Ice_Cream; + }; + if (i == "Splash") { + return Item::Splash; + }; + if (i == "Blue Joker") { + return Item::Blue_Joker; + }; + if (i == "Faceless Joker") { + return Item::Faceless_Joker; + }; + if (i == "Green Joker") { + return Item::Green_Joker; + }; + if (i == "Superposition") { + return Item::Superposition; + }; + if (i == "To Do List") { + return Item::To_Do_List; + }; + if (i == "Cavendish") { + return Item::Cavendish; + }; + if (i == "Red Card") { + return Item::Red_Card; + }; + if (i == "Square Joker") { + return Item::Square_Joker; + }; + if (i == "Riff-raff") { + return Item::Riff_raff; + }; + if (i == "Photograph") { + return Item::Photograph; + }; + if (i == "Reserved Parking") { + return Item::Reserved_Parking; + }; + if (i == "Mail-In Rebate") { + return Item::Mail_In_Rebate; + }; + if (i == "Hallucination") { + return Item::Hallucination; + }; + if (i == "Fortune Teller") { + return Item::Fortune_Teller; + }; + if (i == "Juggler") { + return Item::Juggler; + }; + if (i == "Drunkard") { + return Item::Drunkard; + }; + if (i == "Golden Joker") { + return Item::Golden_Joker; + }; + if (i == "Popcorn") { + return Item::Popcorn; + }; + if (i == "Walkie Talkie") { + return Item::Walkie_Talkie; + }; + if (i == "Smiley Face") { + return Item::Smiley_Face; + }; + if (i == "Golden Ticket") { + return Item::Golden_Ticket; + }; + if (i == "Swashbuckler") { + return Item::Swashbuckler; + }; + if (i == "Hanging Chad") { + return Item::Hanging_Chad; + }; + if (i == "Shoot the Moon") { + return Item::Shoot_the_Moon; + }; + if (i == "J C END") { + return Item::J_C_END; + }; + if (i == "J U BEGIN") { + return Item::J_U_BEGIN; + }; + if (i == "Joker Stencil") { + return Item::Joker_Stencil; + }; + if (i == "Four Fingers") { + return Item::Four_Fingers; + }; + if (i == "Mime") { + return Item::Mime; + }; + if (i == "Ceremonial Dagger") { + return Item::Ceremonial_Dagger; + }; + if (i == "Marble Joker") { + return Item::Marble_Joker; + }; + if (i == "Loyalty Card") { + return Item::Loyalty_Card; + }; + if (i == "Dusk") { + return Item::Dusk; + }; + if (i == "Fibonacci") { + return Item::Fibonacci; + }; + if (i == "Steel Joker") { + return Item::Steel_Joker; + }; + if (i == "Hack") { + return Item::Hack; + }; + if (i == "Pareidolia") { + return Item::Pareidolia; + }; + if (i == "Space Joker") { + return Item::Space_Joker; + }; + if (i == "Burglar") { + return Item::Burglar; + }; + if (i == "Blackboard") { + return Item::Blackboard; + }; + if (i == "Sixth Sense") { + return Item::Sixth_Sense; + }; + if (i == "Constellation") { + return Item::Constellation; + }; + if (i == "Hiker") { + return Item::Hiker; + }; + if (i == "Card Sharp") { + return Item::Card_Sharp; + }; + if (i == "Madness") { + return Item::Madness; + }; + if (i == "SΘance") { + return Item::Seance; + }; + if (i == "Shortcut") { + return Item::Shortcut; + }; + if (i == "Hologram") { + return Item::Hologram; + }; + if (i == "Cloud 9") { + return Item::Cloud_9; + }; + if (i == "Rocket") { + return Item::Rocket; + }; + if (i == "Midas Mask") { + return Item::Midas_Mask; + }; + if (i == "Luchador") { + return Item::Luchador; + }; + if (i == "Gift Card") { + return Item::Gift_Card; + }; + if (i == "Turtle Bean") { + return Item::Turtle_Bean; + }; + if (i == "Erosion") { + return Item::Erosion; + }; + if (i == "To the Moon") { + return Item::To_the_Moon; + }; + if (i == "Stone Joker") { + return Item::Stone_Joker; + }; + if (i == "Lucky Cat") { + return Item::Lucky_Cat; + }; + if (i == "Bull") { + return Item::Bull; + }; + if (i == "Diet Cola") { + return Item::Diet_Cola; + }; + if (i == "Trading Card") { + return Item::Trading_Card; + }; + if (i == "Flash Card") { + return Item::Flash_Card; + }; + if (i == "Spare Trousers") { + return Item::Spare_Trousers; + }; + if (i == "Ramen") { + return Item::Ramen; + }; + if (i == "Seltzer") { + return Item::Seltzer; + }; + if (i == "Castle") { + return Item::Castle; + }; + if (i == "Mr. Bones") { + return Item::Mr_Bones; + }; + if (i == "Acrobat") { + return Item::Acrobat; + }; + if (i == "Sock and Buskin") { + return Item::Sock_and_Buskin; + }; + if (i == "Troubadour") { + return Item::Troubadour; + }; + if (i == "Certificate") { + return Item::Certificate; + }; + if (i == "Smeared Joker") { + return Item::Smeared_Joker; + }; + if (i == "Throwback") { + return Item::Throwback; + }; + if (i == "Rough Gem") { + return Item::Rough_Gem; + }; + if (i == "Bloodstone") { + return Item::Bloodstone; + }; + if (i == "Arrowhead") { + return Item::Arrowhead; + }; + if (i == "Onyx Agate") { + return Item::Onyx_Agate; + }; + if (i == "Glass Joker") { + return Item::Glass_Joker; + }; + if (i == "Showman") { + return Item::Showman; + }; + if (i == "Flower Pot") { + return Item::Flower_Pot; + }; + if (i == "Merry Andy") { + return Item::Merry_Andy; + }; + if (i == "Oops! All 6s") { + return Item::Oops_All_6s; + }; + if (i == "The Idol") { + return Item::The_Idol; + }; + if (i == "Seeing Double") { + return Item::Seeing_Double; + }; + if (i == "Matador") { + return Item::Matador; + }; + if (i == "Stuntman") { + return Item::Stuntman; + }; + if (i == "Satellite") { + return Item::Satellite; + }; + if (i == "Cartomancer") { + return Item::Cartomancer; + }; + if (i == "Astronomer") { + return Item::Astronomer; + }; + if (i == "Bootstraps") { + return Item::Bootstraps; + }; + if (i == "J U END") { + return Item::J_U_END; + }; + if (i == "J R BEGIN") { + return Item::J_R_BEGIN; + }; + if (i == "DNA") { + return Item::DNA; + }; + if (i == "Vampire") { + return Item::Vampire; + }; + if (i == "Vagabond") { + return Item::Vagabond; + }; + if (i == "Baron") { + return Item::Baron; + }; + if (i == "Obelisk") { + return Item::Obelisk; + }; + if (i == "Baseball Card") { + return Item::Baseball_Card; + }; + if (i == "Ancient Joker") { + return Item::Ancient_Joker; + }; + if (i == "Campfire") { + return Item::Campfire; + }; + if (i == "Blueprint") { + return Item::Blueprint; + }; + if (i == "Wee Joker") { + return Item::Wee_Joker; + }; + if (i == "Hit the Road") { + return Item::Hit_the_Road; + }; + if (i == "The Duo") { + return Item::The_Duo; + }; + if (i == "The Trio") { + return Item::The_Trio; + }; + if (i == "The Family") { + return Item::The_Family; + }; + if (i == "The Order") { + return Item::The_Order; + }; + if (i == "The Tribe") { + return Item::The_Tribe; + }; + if (i == "Invisible Joker") { + return Item::Invisible_Joker; + }; + if (i == "Brainstorm") { + return Item::Brainstorm; + }; + if (i == "Driver's License") { + return Item::Drivers_License; + }; + if (i == "Burnt Joker") { + return Item::Burnt_Joker; + }; + if (i == "J R END") { + return Item::J_R_END; + }; + if (i == "J L BEGIN") { + return Item::J_L_BEGIN; + }; + if (i == "Canio") { + return Item::Canio; + }; + if (i == "Triboulet") { + return Item::Triboulet; + }; + if (i == "Yorick") { + return Item::Yorick; + }; + if (i == "Chicot") { + return Item::Chicot; + }; + if (i == "Perkeo") { + return Item::Perkeo; + }; + if (i == "J L END") { + return Item::J_L_END; + }; + if (i == "J END") { + return Item::J_END; + }; + if (i == "V BEGIN") { + return Item::V_BEGIN; + }; + if (i == "Overstock") { + return Item::Overstock; + }; + if (i == "Overstock Plus") { + return Item::Overstock_Plus; + }; + if (i == "Clearance Sale") { + return Item::Clearance_Sale; + }; + if (i == "Liquidation") { + return Item::Liquidation; + }; + if (i == "Hone") { + return Item::Hone; + }; + if (i == "Glow Up") { + return Item::Glow_Up; + }; + if (i == "Reroll Surplus") { + return Item::Reroll_Surplus; + }; + if (i == "Reroll Glut") { + return Item::Reroll_Glut; + }; + if (i == "Crystal Ball") { + return Item::Crystal_Ball; + }; + if (i == "Omen Globe") { + return Item::Omen_Globe; + }; + if (i == "Telescope") { + return Item::Telescope; + }; + if (i == "Observatory") { + return Item::Observatory; + }; + if (i == "Grabber") { + return Item::Grabber; + }; + if (i == "Nacho Tong") { + return Item::Nacho_Tong; + }; + if (i == "Wasteful") { + return Item::Wasteful; + }; + if (i == "Recyclomancy") { + return Item::Recyclomancy; + }; + if (i == "Tarot Merchant") { + return Item::Tarot_Merchant; + }; + if (i == "Tarot Tycoon") { + return Item::Tarot_Tycoon; + }; + if (i == "Planet Merchant") { + return Item::Planet_Merchant; + }; + if (i == "Planet Tycoon") { + return Item::Planet_Tycoon; + }; + if (i == "Seed Money") { + return Item::Seed_Money; + }; + if (i == "Money Tree") { + return Item::Money_Tree; + }; + if (i == "Blank") { + return Item::Blank; + }; + if (i == "Antimatter") { + return Item::Antimatter; + }; + if (i == "Magic Trick") { + return Item::Magic_Trick; + }; + if (i == "Illusion") { + return Item::Illusion; + }; + if (i == "Hieroglyph") { + return Item::Hieroglyph; + }; + if (i == "Petroglyph") { + return Item::Petroglyph; + }; + if (i == "Director's Cut") { + return Item::Directors_Cut; + }; + if (i == "Retcon") { + return Item::Retcon; + }; + if (i == "Paint Brush") { + return Item::Paint_Brush; + }; + if (i == "Palette") { + return Item::Palette; + }; + if (i == "V END") { + return Item::V_END; + }; + if (i == "T BEGIN") { + return Item::T_BEGIN; + }; + if (i == "The Fool") { + return Item::The_Fool; + }; + if (i == "The Magician") { + return Item::The_Magician; + }; + if (i == "The High Priestess") { + return Item::The_High_Priestess; + }; + if (i == "The Empress") { + return Item::The_Empress; + }; + if (i == "The Emperor") { + return Item::The_Emperor; + }; + if (i == "The Hierophant") { + return Item::The_Hierophant; + }; + if (i == "The Lovers") { + return Item::The_Lovers; + }; + if (i == "The Chariot") { + return Item::The_Chariot; + }; + if (i == "Justice") { + return Item::Justice; + }; + if (i == "The Hermit") { + return Item::The_Hermit; + }; + if (i == "The Wheel of Fortune") { + return Item::The_Wheel_of_Fortune; + }; + if (i == "Strength") { + return Item::Strength; + }; + if (i == "The Hanged Man") { + return Item::The_Hanged_Man; + }; + if (i == "Death") { + return Item::Death; + }; + if (i == "Temperance") { + return Item::Temperance; + }; + if (i == "The Devil") { + return Item::The_Devil; + }; + if (i == "The Tower") { + return Item::The_Tower; + }; + if (i == "The Star") { + return Item::The_Star; + }; + if (i == "The Moon") { + return Item::The_Moon; + }; + if (i == "The Sun") { + return Item::The_Sun; + }; + if (i == "Judgement") { + return Item::Judgement; + }; + if (i == "The World") { + return Item::The_World; + }; + if (i == "T END") { + return Item::T_END; + }; + if (i == "P BEGIN") { + return Item::P_BEGIN; + }; + if (i == "Mercury") { + return Item::Mercury; + }; + if (i == "Venus") { + return Item::Venus; + }; + if (i == "Earth") { + return Item::Earth; + }; + if (i == "Mars") { + return Item::Mars; + }; + if (i == "Jupiter") { + return Item::Jupiter; + }; + if (i == "Saturn") { + return Item::Saturn; + }; + if (i == "Uranus") { + return Item::Uranus; + }; + if (i == "Neptune") { + return Item::Neptune; + }; + if (i == "Pluto") { + return Item::Pluto; + }; + if (i == "Planet X") { + return Item::Planet_X; + }; + if (i == "Ceres") { + return Item::Ceres; + }; + if (i == "Eris") { + return Item::Eris; + }; + if (i == "P END") { + return Item::P_END; + }; + if (i == "H BEGIN") { + return Item::H_BEGIN; + }; + if (i == "Pair") { + return Item::Pair; + }; + if (i == "Three of a Kind") { + return Item::Three_of_a_Kind; + }; + if (i == "Full House") { + return Item::Full_House; + }; + if (i == "Four of a Kind") { + return Item::Four_of_a_Kind; + }; + if (i == "Flush") { + return Item::Flush; + }; + if (i == "Straight") { + return Item::Straight; + }; + if (i == "Two Pair") { + return Item::Two_Pair; + }; + if (i == "Straight Flush") { + return Item::Straight_Flush; + }; + if (i == "High Card") { + return Item::High_Card; + }; + if (i == "Five of a Kind") { + return Item::Five_of_a_Kind; + }; + if (i == "Flush House") { + return Item::Flush_House; + }; + if (i == "Flush Five") { + return Item::Flush_Five; + }; + if (i == "H END") { + return Item::H_END; + }; + if (i == "S BEGIN") { + return Item::S_BEGIN; + }; + if (i == "Familiar") { + return Item::Familiar; + }; + if (i == "Grim") { + return Item::Grim; + }; + if (i == "Incantation") { + return Item::Incantation; + }; + if (i == "Talisman") { + return Item::Talisman; + }; + if (i == "Aura") { + return Item::Aura; + }; + if (i == "Wraith") { + return Item::Wraith; + }; + if (i == "Sigil") { + return Item::Sigil; + }; + if (i == "Ouija") { + return Item::Ouija; + }; + if (i == "Ectoplasm") { + return Item::Ectoplasm; + }; + if (i == "Immolate") { + return Item::Immolate; + }; + if (i == "Ankh") { + return Item::Ankh; + }; + if (i == "Deja Vu") { + return Item::Deja_Vu; + }; + if (i == "Hex") { + return Item::Hex; + }; + if (i == "Trance") { + return Item::Trance; + }; + if (i == "Medium") { + return Item::Medium; + }; + if (i == "Cryptid") { + return Item::Cryptid; + }; + if (i == "The Soul") { + return Item::The_Soul; + }; + if (i == "Black Hole") { + return Item::Black_Hole; + }; + if (i == "S END") { + return Item::S_END; + }; + if (i == "ENHANCEMENT BEGIN") { + return Item::ENHANCEMENT_BEGIN; + }; + if (i == "No Enhancement") { + return Item::No_Enhancement; + }; + if (i == "Bonus Card") { + return Item::Bonus_Card; + }; + if (i == "Mult Card") { + return Item::Mult_Card; + }; + if (i == "Wild Card") { + return Item::Wild_Card; + }; + if (i == "Glass Card") { + return Item::Glass_Card; + }; + if (i == "Steel Card") { + return Item::Steel_Card; + }; + if (i == "Stone Card") { + return Item::Stone_Card; + }; + if (i == "Gold Card") { + return Item::Gold_Card; + }; + if (i == "Lucky Card") { + return Item::Lucky_Card; + }; + if (i == "ENHANCEMENT END") { + return Item::ENHANCEMENT_END; + }; + if (i == "SEAL BEGIN") { + return Item::SEAL_BEGIN; + }; + if (i == "No Seal") { + return Item::No_Seal; + }; + if (i == "Gold Seal") { + return Item::Gold_Seal; + }; + if (i == "Red Seal") { + return Item::Red_Seal; + }; + if (i == "Blue Seal") { + return Item::Blue_Seal; + }; + if (i == "Purple Seal") { + return Item::Purple_Seal; + }; + if (i == "SEAL END") { + return Item::SEAL_END; + }; + if (i == "E BEGIN") { + return Item::E_BEGIN; + }; + if (i == "No Edition") { + return Item::No_Edition; + }; + if (i == "Foil") { + return Item::Foil; + }; + if (i == "Holographic") { + return Item::Holographic; + }; + if (i == "Polychrome") { + return Item::Polychrome; + }; + if (i == "Negative") { + return Item::Negative; + }; + if (i == "E END") { + return Item::E_END; + }; + if (i == "PACK BEGIN") { + return Item::PACK_BEGIN; + }; + if (i == "Arcana Pack") { + return Item::Arcana_Pack; + }; + if (i == "Jumbo Arcana Pack") { + return Item::Jumbo_Arcana_Pack; + }; + if (i == "Mega Arcana Pack") { + return Item::Mega_Arcana_Pack; + }; + if (i == "Celestial Pack") { + return Item::Celestial_Pack; + }; + if (i == "Jumbo Celestial Pack") { + return Item::Jumbo_Celestial_Pack; + }; + if (i == "Mega Celestial Pack") { + return Item::Mega_Celestial_Pack; + }; + if (i == "Standard Pack") { + return Item::Standard_Pack; + }; + if (i == "Jumbo Standard Pack") { + return Item::Jumbo_Standard_Pack; + }; + if (i == "Mega Standard Pack") { + return Item::Mega_Standard_Pack; + }; + if (i == "Buffoon Pack") { + return Item::Buffoon_Pack; + }; + if (i == "Jumbo Buffoon Pack") { + return Item::Jumbo_Buffoon_Pack; + }; + if (i == "Mega Buffoon Pack") { + return Item::Mega_Buffoon_Pack; + }; + if (i == "Spectral Pack") { + return Item::Spectral_Pack; + }; + if (i == "Jumbo Spectral Pack") { + return Item::Jumbo_Spectral_Pack; + }; + if (i == "Mega Spectral Pack") { + return Item::Mega_Spectral_Pack; + }; + if (i == "PACK END") { + return Item::PACK_END; + }; + if (i == "TAG BEGIN") { + return Item::TAG_BEGIN; + }; + if (i == "Uncommon Tag") { + return Item::Uncommon_Tag; + }; + if (i == "Rare Tag") { + return Item::Rare_Tag; + }; + if (i == "Negative Tag") { + return Item::Negative_Tag; + }; + if (i == "Foil Tag") { + return Item::Foil_Tag; + }; + if (i == "Holographic Tag") { + return Item::Holographic_Tag; + }; + if (i == "Polychrome Tag") { + return Item::Polychrome_Tag; + }; + if (i == "Investment Tag") { + return Item::Investment_Tag; + }; + if (i == "Voucher Tag") { + return Item::Voucher_Tag; + }; + if (i == "Boss Tag") { + return Item::Boss_Tag; + }; + if (i == "Standard Tag") { + return Item::Standard_Tag; + }; + if (i == "Charm Tag") { + return Item::Charm_Tag; + }; + if (i == "Meteor Tag") { + return Item::Meteor_Tag; + }; + if (i == "Buffoon Tag") { + return Item::Buffoon_Tag; + }; + if (i == "Handy Tag") { + return Item::Handy_Tag; + }; + if (i == "Garbage Tag") { + return Item::Garbage_Tag; + }; + if (i == "Ethereal Tag") { + return Item::Ethereal_Tag; + }; + if (i == "Coupon Tag") { + return Item::Coupon_Tag; + }; + if (i == "Double Tag") { + return Item::Double_Tag; + }; + if (i == "Juggle Tag") { + return Item::Juggle_Tag; + }; + if (i == "D6 Tag") { + return Item::D6_Tag; + }; + if (i == "Top-up Tag") { + return Item::Top_up_Tag; + }; + if (i == "Speed Tag") { + return Item::Speed_Tag; + }; + if (i == "Orbital Tag") { + return Item::Orbital_Tag; + }; + if (i == "Economy Tag") { + return Item::Economy_Tag; + }; + if (i == "TAG END") { + return Item::TAG_END; + }; + if (i == "B BEGIN") { + return Item::B_BEGIN; + }; + if (i == "Small Blind") { + return Item::Small_Blind; + }; + if (i == "Big Blind") { + return Item::Big_Blind; + }; + if (i == "The Hook") { + return Item::The_Hook; + }; + if (i == "The Ox") { + return Item::The_Ox; + }; + if (i == "The House") { + return Item::The_House; + }; + if (i == "The Wall") { + return Item::The_Wall; + }; + if (i == "The Wheel") { + return Item::The_Wheel; + }; + if (i == "The Arm") { + return Item::The_Arm; + }; + if (i == "The Club") { + return Item::The_Club; + }; + if (i == "The Fish") { + return Item::The_Fish; + }; + if (i == "The Psychic") { + return Item::The_Psychic; + }; + if (i == "The Goad") { + return Item::The_Goad; + }; + if (i == "The Water") { + return Item::The_Water; + }; + if (i == "The Window") { + return Item::The_Window; + }; + if (i == "The Manacle") { + return Item::The_Manacle; + }; + if (i == "The Eye") { + return Item::The_Eye; + }; + if (i == "The Mouth") { + return Item::The_Mouth; + }; + if (i == "The Plant") { + return Item::The_Plant; + }; + if (i == "The Serpent") { + return Item::The_Serpent; + }; + if (i == "The Pillar") { + return Item::The_Pillar; + }; + if (i == "The Needle") { + return Item::The_Needle; + }; + if (i == "The Head") { + return Item::The_Head; + }; + if (i == "The Tooth") { + return Item::The_Tooth; + }; + if (i == "The Flint") { + return Item::The_Flint; + }; + if (i == "The Mark") { + return Item::The_Mark; + }; + if (i == "B F BEGIN") { + return Item::B_F_BEGIN; + }; + if (i == "Amber Acorn") { + return Item::Amber_Acorn; + }; + if (i == "Verdant Leaf") { + return Item::Verdant_Leaf; + }; + if (i == "Violet Vessel") { + return Item::Violet_Vessel; + }; + if (i == "Crimson Heart") { + return Item::Crimson_Heart; + }; + if (i == "Cerulean Bell") { + return Item::Cerulean_Bell; + }; + if (i == "B F END") { + return Item::B_F_END; + }; + if (i == "B END") { + return Item::B_END; + }; + if (i == "SUIT BEGIN") { + return Item::SUIT_BEGIN; + }; + if (i == "Hearts") { + return Item::Hearts; + }; + if (i == "Clubs") { + return Item::Clubs; + }; + if (i == "Diamonds") { + return Item::Diamonds; + }; + if (i == "Spades") { + return Item::Spades; + }; + if (i == "SUIT END") { + return Item::SUIT_END; + }; + if (i == "RANK BEGIN") { + return Item::RANK_BEGIN; + }; + if (i == "2") { + return Item::_2; + }; + if (i == "3") { + return Item::_3; + }; + if (i == "4") { + return Item::_4; + }; + if (i == "5") { + return Item::_5; + }; + if (i == "6") { + return Item::_6; + }; + if (i == "7") { + return Item::_7; + }; + if (i == "8") { + return Item::_8; + }; + if (i == "9") { + return Item::_9; + }; + if (i == "10") { + return Item::_10; + }; + if (i == "Jack") { + return Item::Jack; + }; + if (i == "Queen") { + return Item::Queen; + }; + if (i == "King") { + return Item::King; + }; + if (i == "Ace") { + return Item::Ace; + }; + if (i == "RANK END") { + return Item::RANK_END; + }; + if (i == "C BEGIN") { + return Item::C_BEGIN; + }; + if (i == "C 2") { + return Item::C_2; + }; + if (i == "C 3") { + return Item::C_3; + }; + if (i == "C 4") { + return Item::C_4; + }; + if (i == "C 5") { + return Item::C_5; + }; + if (i == "C 6") { + return Item::C_6; + }; + if (i == "C 7") { + return Item::C_7; + }; + if (i == "C 8") { + return Item::C_8; + }; + if (i == "C 9") { + return Item::C_9; + }; + if (i == "C A") { + return Item::C_A; + }; + if (i == "C J") { + return Item::C_J; + }; + if (i == "C K") { + return Item::C_K; + }; + if (i == "C Q") { + return Item::C_Q; + }; + if (i == "C T") { + return Item::C_T; + }; + if (i == "D 2") { + return Item::D_2; + }; + if (i == "D 3") { + return Item::D_3; + }; + if (i == "D 4") { + return Item::D_4; + }; + if (i == "D 5") { + return Item::D_5; + }; + if (i == "D 6") { + return Item::D_6; + }; + if (i == "D 7") { + return Item::D_7; + }; + if (i == "D 8") { + return Item::D_8; + }; + if (i == "D 9") { + return Item::D_9; + }; + if (i == "D A") { + return Item::D_A; + }; + if (i == "D J") { + return Item::D_J; + }; + if (i == "D K") { + return Item::D_K; + }; + if (i == "D Q") { + return Item::D_Q; + }; + if (i == "D T") { + return Item::D_T; + }; + if (i == "H 2") { + return Item::H_2; + }; + if (i == "H 3") { + return Item::H_3; + }; + if (i == "H 4") { + return Item::H_4; + }; + if (i == "H 5") { + return Item::H_5; + }; + if (i == "H 6") { + return Item::H_6; + }; + if (i == "H 7") { + return Item::H_7; + }; + if (i == "H 8") { + return Item::H_8; + }; + if (i == "H 9") { + return Item::H_9; + }; + if (i == "H A") { + return Item::H_A; + }; + if (i == "H J") { + return Item::H_J; + }; + if (i == "H K") { + return Item::H_K; + }; + if (i == "H Q") { + return Item::H_Q; + }; + if (i == "H T") { + return Item::H_T; + }; + if (i == "S 2") { + return Item::S_2; + }; + if (i == "S 3") { + return Item::S_3; + }; + if (i == "S 4") { + return Item::S_4; + }; + if (i == "S 5") { + return Item::S_5; + }; + if (i == "S 6") { + return Item::S_6; + }; + if (i == "S 7") { + return Item::S_7; + }; + if (i == "S 8") { + return Item::S_8; + }; + if (i == "S 9") { + return Item::S_9; + }; + if (i == "S A") { + return Item::S_A; + }; + if (i == "S J") { + return Item::S_J; + }; + if (i == "S K") { + return Item::S_K; + }; + if (i == "S Q") { + return Item::S_Q; + }; + if (i == "S T") { + return Item::S_T; + }; + if (i == "C END") { + return Item::C_END; + }; + if (i == "D BEGIN") { + return Item::D_BEGIN; + }; + if (i == "Red Deck") { + return Item::Red_Deck; + }; + if (i == "Blue Deck") { + return Item::Blue_Deck; + }; + if (i == "Yellow Deck") { + return Item::Yellow_Deck; + }; + if (i == "Green Deck") { + return Item::Green_Deck; + }; + if (i == "Black Deck") { + return Item::Black_Deck; + }; + if (i == "Magic Deck") { + return Item::Magic_Deck; + }; + if (i == "Nebula Deck") { + return Item::Nebula_Deck; + }; + if (i == "Ghost Deck") { + return Item::Ghost_Deck; + }; + if (i == "Abandoned Deck") { + return Item::Abandoned_Deck; + }; + if (i == "Checkered Deck") { + return Item::Checkered_Deck; + }; + if (i == "Zodiac Deck") { + return Item::Zodiac_Deck; + }; + if (i == "Painted Deck") { + return Item::Painted_Deck; + }; + if (i == "Anaglyph Deck") { + return Item::Anaglyph_Deck; + }; + if (i == "Plasma Deck") { + return Item::Plasma_Deck; + }; + if (i == "Erratic Deck") { + return Item::Erratic_Deck; + }; + if (i == "Challenge Deck") { + return Item::Challenge_Deck; + }; + if (i == "D END") { + return Item::D_END; + }; + if (i == "CHAL BEGIN") { + return Item::CHAL_BEGIN; + }; + if (i == "The Omelette") { + return Item::The_Omelette; + }; + if (i == "15 Minute City") { + return Item::_15_Minute_City; + }; + if (i == "Rich get Richer") { + return Item::Rich_get_Richer; + }; + if (i == "On a Knife's Edge") { + return Item::On_a_Knifes_Edge; + }; + if (i == "X-ray Vision") { + return Item::X_ray_Vision; + }; + if (i == "Mad World") { + return Item::Mad_World; + }; + if (i == "Luxury Tax") { + return Item::Luxury_Tax; + }; + if (i == "Non-Perishable") { + return Item::Non_Perishable; + }; + if (i == "Medusa") { + return Item::Medusa; + }; + if (i == "Double or Nothing") { + return Item::Double_or_Nothing; + }; + if (i == "Typecast") { + return Item::Typecast; + }; + if (i == "Inflation") { + return Item::Inflation; + }; + if (i == "Bram Poker") { + return Item::Bram_Poker; + }; + if (i == "Fragile") { + return Item::Fragile; + }; + if (i == "Monolith") { + return Item::Monolith; + }; + if (i == "Blast Off") { + return Item::Blast_Off; + }; + if (i == "Five-Card Draw") { + return Item::Five_Card_Draw; + }; + if (i == "Golden Needle") { + return Item::Golden_Needle; + }; + if (i == "Cruelty") { + return Item::Cruelty; + }; + if (i == "Jokerless") { + return Item::Jokerless; + }; + if (i == "CHAL END") { + return Item::CHAL_END; + }; + if (i == "STAKE BEGIN") { + return Item::STAKE_BEGIN; + }; + if (i == "White Stake") { + return Item::White_Stake; + }; + if (i == "Red Stake") { + return Item::Red_Stake; + }; + if (i == "Green Stake") { + return Item::Green_Stake; + }; + if (i == "Black Stake") { + return Item::Black_Stake; + }; + if (i == "Blue Stake") { + return Item::Blue_Stake; + }; + if (i == "Purple Stake") { + return Item::Purple_Stake; + }; + if (i == "Orange Stake") { + return Item::Orange_Stake; + }; + if (i == "Gold Stake") { + return Item::Gold_Stake; + }; + if (i == "STAKE END") { + return Item::STAKE_END; + }; + if (i == "RARITY BEGIN") { + return Item::RARITY_BEGIN; + }; + if (i == "Common") { + return Item::Common; + }; + if (i == "Uncommon") { + return Item::Uncommon; + }; + if (i == "Rare") { + return Item::Rare; + }; + if (i == "Legendary") { + return Item::Legendary; + }; + if (i == "RARITY END") { + return Item::RARITY_END; + }; + if (i == "TYPE BEGIN") { + return Item::TYPE_BEGIN; + }; + if (i == "T Joker") { + return Item::T_Joker; + }; + if (i == "T Tarot") { + return Item::T_Tarot; + }; + if (i == "T Planet") { + return Item::T_Planet; + }; + if (i == "T Spectral") { + return Item::T_Spectral; + }; + if (i == "T Playing Card") { + return Item::T_Playing_Card; + }; + if (i == "TYPE END") { + return Item::TYPE_END; + }; + return Item::RETRY; +} + +// Structs for storing information +struct ShopInstance { + double jokerRate; + double tarotRate; + double planetRate; + double playingCardRate; + double spectralRate; + ShopInstance() { + jokerRate = 20; + tarotRate = 4; + planetRate = 4; + playingCardRate = 0; + spectralRate = 0; + }; + ShopInstance(double j, double t, double p, double c, double s) { + jokerRate = j; + tarotRate = t; + planetRate = p; + playingCardRate = c; + spectralRate = s; + } + double getTotalRate() { + return jokerRate + tarotRate + planetRate + playingCardRate + spectralRate; + } +}; + +struct JokerStickers { + bool eternal; + bool perishable; + bool rental; + JokerStickers() { + eternal = false; + perishable = false; + rental = false; + }; + JokerStickers(bool e, bool p, bool r) { + eternal = e; + perishable = p; + rental = r; + } +}; + +struct JokerData { + Item joker; + Item rarity; + Item edition; + JokerStickers stickers; + JokerData() { + joker = Item::Joker; + rarity = Item::Common; + edition = Item::No_Edition; + stickers = JokerStickers(); + }; + JokerData(Item j, Item r, Item e, JokerStickers s) { + joker = j; + rarity = r; + edition = e; + stickers = s; + }; +}; + +struct ShopItem { + Item type; + Item item; + JokerData jokerData; + ShopItem() { + type = Item::T_Tarot; + item = Item::The_Fool; + }; + ShopItem(Item t, Item i) { + type = t; + item = i; + }; + ShopItem(Item t, Item i, JokerData j) { + type = t; + item = i; + jokerData = j; + }; +}; + +struct WeightedItem { + Item item; + double weight; + WeightedItem(Item i, double w) { + item = i; + weight = w; + }; +}; + +struct Pack { + Item type; + int size; + int choices; + Pack(Item t, int s, int c) { + type = t; + size = s; + choices = c; + } +}; + +struct Card { + Item base; + Item enhancement; + Item edition; + Item seal; + Card(Item b, Item n, Item e, Item s) { + base = b; + enhancement = n; + edition = e; + seal = s; + } +}; + +constexpr inline std::array ENHANCEMENTS = { + Item::Bonus_Card, Item::Mult_Card, Item::Wild_Card, Item::Glass_Card, + Item::Steel_Card, Item::Stone_Card, Item::Gold_Card, Item::Lucky_Card}; + +constexpr inline std::array CARDS = { + Item::C_2, Item::C_3, Item::C_4, Item::C_5, Item::C_6, Item::C_7, Item::C_8, + Item::C_9, Item::C_A, Item::C_J, Item::C_K, Item::C_Q, Item::C_T, Item::D_2, + Item::D_3, Item::D_4, Item::D_5, Item::D_6, Item::D_7, Item::D_8, Item::D_9, + Item::D_A, Item::D_J, Item::D_K, Item::D_Q, Item::D_T, Item::H_2, Item::H_3, + Item::H_4, Item::H_5, Item::H_6, Item::H_7, Item::H_8, Item::H_9, Item::H_A, + Item::H_J, Item::H_K, Item::H_Q, Item::H_T, Item::S_2, Item::S_3, Item::S_4, + Item::S_5, Item::S_6, Item::S_7, Item::S_8, Item::S_9, Item::S_A, Item::S_J, + Item::S_K, Item::S_Q, Item::S_T}; + +constexpr inline std::array SUITS = {Item::Spades, Item::Hearts, + Item::Clubs, Item::Diamonds}; + +constexpr inline std::array RANKS = { + Item::_2, Item::_3, Item::_4, Item::_5, Item::_6, + Item::_7, Item::_8, Item::_9, Item::_10, Item::Jack, + Item::Queen, Item::King, Item::Ace}; + +inline std::array PACKS = { + WeightedItem(Item::RETRY, 22.42), // total + WeightedItem(Item::Arcana_Pack, 4), + WeightedItem(Item::Jumbo_Arcana_Pack, 2), + WeightedItem(Item::Mega_Arcana_Pack, 0.5), + WeightedItem(Item::Celestial_Pack, 4), + WeightedItem(Item::Jumbo_Celestial_Pack, 2), + WeightedItem(Item::Mega_Celestial_Pack, 0.5), + WeightedItem(Item::Standard_Pack, 4), + WeightedItem(Item::Jumbo_Standard_Pack, 2), + WeightedItem(Item::Mega_Standard_Pack, 0.5), + WeightedItem(Item::Buffoon_Pack, 1.2), + WeightedItem(Item::Jumbo_Buffoon_Pack, 0.6), + WeightedItem(Item::Mega_Buffoon_Pack, 0.15), + WeightedItem(Item::Spectral_Pack, 0.6), + WeightedItem(Item::Jumbo_Spectral_Pack, 0.3), + WeightedItem(Item::Mega_Spectral_Pack, 0.07)}; + +constexpr inline std::array TAROTS = {Item::The_Fool, + Item::The_Magician, + Item::The_High_Priestess, + Item::The_Empress, + Item::The_Emperor, + Item::The_Hierophant, + Item::The_Lovers, + Item::The_Chariot, + Item::Justice, + Item::The_Hermit, + Item::The_Wheel_of_Fortune, + Item::Strength, + Item::The_Hanged_Man, + Item::Death, + Item::Temperance, + Item::The_Devil, + Item::The_Tower, + Item::The_Star, + Item::The_Moon, + Item::The_Sun, + Item::Judgement, + Item::The_World}; + +constexpr inline std::array PLANETS = { + Item::Mercury, Item::Venus, Item::Earth, Item::Mars, + Item::Jupiter, Item::Saturn, Item::Uranus, Item::Neptune, + Item::Pluto, Item::Planet_X, Item::Ceres, Item::Eris}; + +constexpr inline std::array COMMON_JOKERS_100 = { + Item::Joker, + Item::Greedy_Joker, + Item::Lusty_Joker, + Item::Wrathful_Joker, + Item::Gluttonous_Joker, + Item::Jolly_Joker, + Item::Zany_Joker, + Item::Mad_Joker, + Item::Crazy_Joker, + Item::Droll_Joker, + Item::Sly_Joker, + Item::Wily_Joker, + Item::Clever_Joker, + Item::Devious_Joker, + Item::Crafty_Joker, + Item::Half_Joker, + Item::Credit_Card, + Item::Banner, + Item::Mystic_Summit, + Item::_8_Ball, + Item::Misprint, + Item::Raised_Fist, + Item::Chaos_the_Clown, + Item::Scary_Face, + Item::Abstract_Joker, + Item::Delayed_Gratification, + Item::Gros_Michel, + Item::Even_Steven, + Item::Odd_Todd, + Item::Scholar, + Item::Business_Card, + Item::Supernova, + Item::Ride_the_Bus, + Item::Egg, + Item::Runner, + Item::Ice_Cream, + Item::Splash, + Item::Blue_Joker, + Item::Faceless_Joker, + Item::Green_Joker, + Item::Superposition, + Item::To_Do_List, + Item::Cavendish, + Item::Red_Card, + Item::Square_Joker, + Item::Riff_raff, + Item::Photograph, + Item::Mail_In_Rebate, + Item::Hallucination, + Item::Fortune_Teller, + Item::Juggler, + Item::Drunkard, + Item::Golden_Joker, + Item::Popcorn, + Item::Walkie_Talkie, + Item::Smiley_Face, + Item::Golden_Ticket, + Item::Swashbuckler, + Item::Hanging_Chad, + Item::Shoot_the_Moon}; + +constexpr inline std::array COMMON_JOKERS = { + Item::Joker, + Item::Greedy_Joker, + Item::Lusty_Joker, + Item::Wrathful_Joker, + Item::Gluttonous_Joker, + Item::Jolly_Joker, + Item::Zany_Joker, + Item::Mad_Joker, + Item::Crazy_Joker, + Item::Droll_Joker, + Item::Sly_Joker, + Item::Wily_Joker, + Item::Clever_Joker, + Item::Devious_Joker, + Item::Crafty_Joker, + Item::Half_Joker, + Item::Credit_Card, + Item::Banner, + Item::Mystic_Summit, + Item::_8_Ball, + Item::Misprint, + Item::Raised_Fist, + Item::Chaos_the_Clown, + Item::Scary_Face, + Item::Abstract_Joker, + Item::Delayed_Gratification, + Item::Gros_Michel, + Item::Even_Steven, + Item::Odd_Todd, + Item::Scholar, + Item::Business_Card, + Item::Supernova, + Item::Ride_the_Bus, + Item::Egg, + Item::Runner, + Item::Ice_Cream, + Item::Splash, + Item::Blue_Joker, + Item::Faceless_Joker, + Item::Green_Joker, + Item::Superposition, + Item::To_Do_List, + Item::Cavendish, + Item::Red_Card, + Item::Square_Joker, + Item::Riff_raff, + Item::Photograph, + Item::Reserved_Parking, + Item::Mail_In_Rebate, + Item::Hallucination, + Item::Fortune_Teller, + Item::Juggler, + Item::Drunkard, + Item::Golden_Joker, + Item::Popcorn, + Item::Walkie_Talkie, + Item::Smiley_Face, + Item::Golden_Ticket, + Item::Swashbuckler, + Item::Hanging_Chad, + Item::Shoot_the_Moon, +}; + +constexpr inline std::array UNCOMMON_JOKERS_100 = { + Item::Joker_Stencil, Item::Four_Fingers, + Item::Mime, Item::Ceremonial_Dagger, + Item::Marble_Joker, Item::Loyalty_Card, + Item::Dusk, Item::Fibonacci, + Item::Steel_Joker, Item::Hack, + Item::Pareidolia, Item::Space_Joker, + Item::Burglar, Item::Blackboard, + Item::Constellation, Item::Hiker, + Item::Card_Sharp, Item::Madness, + Item::Vampire, Item::Shortcut, + Item::Hologram, Item::Vagabond, + Item::Cloud_9, Item::Rocket, + Item::Midas_Mask, Item::Luchador, + Item::Gift_Card, Item::Turtle_Bean, + Item::Erosion, Item::Reserved_Parking, + Item::To_the_Moon, Item::Stone_Joker, + Item::Lucky_Cat, Item::Bull, + Item::Diet_Cola, Item::Trading_Card, + Item::Flash_Card, Item::Spare_Trousers, + Item::Ramen, Item::Seltzer, + Item::Castle, Item::Mr_Bones, + Item::Acrobat, Item::Sock_and_Buskin, + Item::Troubadour, Item::Certificate, + Item::Smeared_Joker, Item::Throwback, + Item::Rough_Gem, Item::Bloodstone, + Item::Arrowhead, Item::Onyx_Agate, + Item::Glass_Joker, Item::Showman, + Item::Flower_Pot, Item::Merry_Andy, + Item::Oops_All_6s, Item::The_Idol, + Item::Seeing_Double, Item::Matador, + Item::Stuntman, Item::Satellite, + Item::Cartomancer, Item::Astronomer, + Item::Burnt_Joker, Item::Bootstraps}; + +constexpr inline std::array UNCOMMON_JOKERS = { + Item::Joker_Stencil, Item::Four_Fingers, + Item::Mime, Item::Ceremonial_Dagger, + Item::Marble_Joker, Item::Loyalty_Card, + Item::Dusk, Item::Fibonacci, + Item::Steel_Joker, Item::Hack, + Item::Pareidolia, Item::Space_Joker, + Item::Burglar, Item::Blackboard, + Item::Sixth_Sense, Item::Constellation, + Item::Hiker, Item::Card_Sharp, + Item::Madness, Item::Seance, + Item::Vampire, Item::Shortcut, + Item::Hologram, Item::Cloud_9, + Item::Rocket, Item::Midas_Mask, + Item::Luchador, Item::Gift_Card, + Item::Turtle_Bean, Item::Erosion, + Item::To_the_Moon, Item::Stone_Joker, + Item::Lucky_Cat, Item::Bull, + Item::Diet_Cola, Item::Trading_Card, + Item::Flash_Card, Item::Spare_Trousers, + Item::Ramen, Item::Seltzer, + Item::Castle, Item::Mr_Bones, + Item::Acrobat, Item::Sock_and_Buskin, + Item::Troubadour, Item::Certificate, + Item::Smeared_Joker, Item::Throwback, + Item::Rough_Gem, Item::Bloodstone, + Item::Arrowhead, Item::Onyx_Agate, + Item::Glass_Joker, Item::Showman, + Item::Flower_Pot, Item::Merry_Andy, + Item::Oops_All_6s, Item::The_Idol, + Item::Seeing_Double, Item::Matador, + Item::Satellite, Item::Cartomancer, + Item::Astronomer, Item::Bootstraps, +}; + +constexpr inline std::array RARE_JOKERS_100 = {Item::DNA, + Item::Sixth_Sense, + Item::Seance, + Item::Baron, + Item::Obelisk, + Item::Baseball_Card, + Item::Ancient_Joker, + Item::Campfire, + Item::Blueprint, + Item::Wee_Joker, + Item::Hit_the_Road, + Item::The_Duo, + Item::The_Trio, + Item::The_Family, + Item::The_Order, + Item::The_Tribe, + Item::Invisible_Joker, + Item::Brainstorm, + Item::Drivers_License}; + +constexpr inline std::array RARE_JOKERS = { + Item::DNA, + Item::Vagabond, + Item::Baron, + Item::Obelisk, + Item::Baseball_Card, + Item::Ancient_Joker, + Item::Campfire, + Item::Blueprint, + Item::Wee_Joker, + Item::Hit_the_Road, + Item::The_Duo, + Item::The_Trio, + Item::The_Family, + Item::The_Order, + Item::The_Tribe, + Item::Stuntman, + Item::Invisible_Joker, + Item::Brainstorm, + Item::Drivers_License, + Item::Burnt_Joker, +}; + +constexpr inline std::array LEGENDARY_JOKERS = { + Item::Canio, Item::Triboulet, Item::Yorick, Item::Chicot, Item::Perkeo}; + +constexpr inline std::array VOUCHERS = { + Item::Overstock, Item::Overstock_Plus, Item::Clearance_Sale, + Item::Liquidation, Item::Hone, Item::Glow_Up, + Item::Reroll_Surplus, Item::Reroll_Glut, Item::Crystal_Ball, + Item::Omen_Globe, Item::Telescope, Item::Observatory, + Item::Grabber, Item::Nacho_Tong, Item::Wasteful, + Item::Recyclomancy, Item::Tarot_Merchant, Item::Tarot_Tycoon, + Item::Planet_Merchant, Item::Planet_Tycoon, Item::Seed_Money, + Item::Money_Tree, Item::Blank, Item::Antimatter, + Item::Magic_Trick, Item::Illusion, Item::Hieroglyph, + Item::Petroglyph, Item::Directors_Cut, Item::Retcon, + Item::Paint_Brush, Item::Palette}; + +constexpr inline std::array SPECTRALS = { + Item::Familiar, Item::Grim, Item::Incantation, Item::Talisman, + Item::Aura, Item::Wraith, Item::Sigil, Item::Ouija, + Item::Ectoplasm, Item::Immolate, Item::Ankh, Item::Deja_Vu, + Item::Hex, Item::Trance, Item::Medium, Item::Cryptid, + Item::RETRY, // Soul + Item::RETRY // Black_Hole +}; + +constexpr inline std::array TAGS = { + Item::Uncommon_Tag, Item::Rare_Tag, Item::Negative_Tag, + Item::Foil_Tag, Item::Holographic_Tag, Item::Polychrome_Tag, + Item::Investment_Tag, Item::Voucher_Tag, Item::Boss_Tag, + Item::Standard_Tag, Item::Charm_Tag, Item::Meteor_Tag, + Item::Buffoon_Tag, Item::Handy_Tag, Item::Garbage_Tag, + Item::Ethereal_Tag, Item::Coupon_Tag, Item::Double_Tag, + Item::Juggle_Tag, Item::D6_Tag, Item::Top_up_Tag, + Item::Speed_Tag, Item::Orbital_Tag, Item::Economy_Tag}; + +constexpr inline std::array BOSSES = { + Item::The_Arm, Item::The_Club, Item::The_Eye, + Item::Amber_Acorn, Item::Cerulean_Bell, Item::Crimson_Heart, + Item::Verdant_Leaf, Item::Violet_Vessel, Item::The_Fish, + Item::The_Flint, Item::The_Goad, Item::The_Head, + Item::The_Hook, Item::The_House, Item::The_Manacle, + Item::The_Mark, Item::The_Mouth, Item::The_Needle, + Item::The_Ox, Item::The_Pillar, Item::The_Plant, + Item::The_Psychic, Item::The_Serpent, Item::The_Tooth, + Item::The_Wall, Item::The_Water, Item::The_Wheel, + Item::The_Window}; + +#endif \ No newline at end of file diff --git a/immolate/main.cpp b/immolate/main.cpp new file mode 100644 index 0000000..5467468 --- /dev/null +++ b/immolate/main.cpp @@ -0,0 +1,282 @@ +#include "functions.hpp" +#include "search.hpp" +#include +#include +#include + +long filter(Instance inst) { + long legendaries = 0; + inst.nextPack(1); + for (int p = 1; p <= 3; p++) { + Pack pack = packInfo(inst.nextPack(1)); + if (pack.type == Item::Arcana_Pack) { + auto packContents = inst.nextArcanaPack(pack.size, 1); + for (int x = 0; x < pack.size; x++) { + if (packContents[x] == Item::The_Soul) + legendaries++; + } + } + if (pack.type == Item::Spectral_Pack) { + auto packContents = inst.nextSpectralPack(pack.size, 1); + for (int x = 0; x < pack.size; x++) { + if (packContents[x] == Item::The_Soul) + legendaries++; + } + } + } + return legendaries; +}; + +long filter_perkeo_observatory(Instance inst) { + if (inst.nextVoucher(1) == Item::Telescope) { + inst.activateVoucher(Item::Telescope); + if (inst.nextVoucher(2) != Item::Observatory) + return 0; + } else + return 0; + int antes[5] = {1, 1, 2, 2, 2}; + for (int i = 0; i < 5; i++) { + Pack pack = packInfo(inst.nextPack(antes[i])); + std::vector packContents; + if (pack.type == Item::Arcana_Pack) { + packContents = inst.nextArcanaPack(pack.size, antes[i]); + } else if (pack.type == Item::Spectral_Pack) { + packContents = inst.nextSpectralPack(pack.size, antes[i]); + } else + continue; + for (int x = 0; x < pack.size; x++) { + if (packContents[x] == Item::The_Soul && + inst.nextJoker(ItemSource::Soul, antes[i], true).joker == + Item::Perkeo) + return 1; + } + } + return 0; +} + +long filter_negative_tag(Instance inst) { + // Note: If the score cutoff was passed as a variable, this code could be + // significantly optimized + int maxAnte = 20; + int score = 0; + for (int i = 2; i <= maxAnte; i++) { + if (inst.nextTag(i) == Item::Negative_Tag) + score++; + } + return score; +} + +long filter_lucky(Instance inst) { + for (int i = 0; i < 7; i++) { + if (inst.random(RandomType::Lucky_Money) >= 1.0/15) { + return 0; + } + } + return 1; +} + +long filter_suas_speedrun(Instance inst) { + // First four cards in shop must include Mr. Bones, Merry Andy, and Luchador + bool bones = false, andy = false, luchador = false; + for (int i = 0; i < 4; i++) { + ShopItem item = inst.nextShopItem(2); + if (item.item == Item::Mr_Bones) + bones = true; + if (item.item == Item::Merry_Andy) + andy = true; + if (item.item == Item::Luchador) + luchador = true; + } + if (!bones || !andy || !luchador) + return 0; + // Ante 1 must have a Coupon Tag + inst.initLocks(1, false, true); + bool coupon = false; + for (int i = 0; i < 2; i++) { + if (inst.nextTag(1) == Item::Coupon_Tag) + coupon = true; + } + if (!coupon) + return 1; + // Ante 2 Boss must be The Wall + inst.nextBoss(1); + inst.initUnlocks(2, false); + if (inst.nextBoss(2) != Item::The_Wall) + return 2; + return 3; +} + +long filter_cavendish(Instance inst) { + inst.initLocks(1, false, false); + // Check for a Charm Tag (Arcana Pack) + if (inst.nextTag(1) != Item::Charm_Tag) + return 0; + // Check for a Judgement within that pack + std::vector packContents = inst.nextArcanaPack(5, 1); + bool hasJudgement = false; + for (int i = 0; i < 5; i++) { + if (packContents[i] == Item::Judgement) + hasJudgement = true; + } + if (!hasJudgement) + return 1; + // Check for Gros Michel + if (inst.nextJoker(ItemSource::Judgement, 1, false).joker != Item::Gros_Michel) + return 2; + // Check for Gros Michel break + if (inst.random(RandomType::Gros_Michel) >= 1.0/6) + return 3; + // Check for Cavendish in first shop + if (inst.nextShopItem(1).item != Item::Cavendish || inst.nextShopItem(1).item != Item::Cavendish) + return 4; + // Check for Cavendish break + if (inst.random(RandomType::Cavendish) < 1.0/1000) + return 9999; + return 5; +} + +long filter_blank(Instance inst) { return 0; } + +// These won't be permanent filters, just ones I sub in and out while JSON +// filters aren't ready yet +long filter_test(Instance inst) { + // Four Fingers, Shortcut, and Smeared Joker in first two antes + // (https://discord.com/channels/1325151824638120007/1326284714125955183) + bool fingers = false; + bool shortcut = false; + bool smeared = false; + // 4 chances in Ante 1, 6 chances in Ante 2, so no rerolling + for (int i = 0; i < 4; i++) { + ShopItem item = inst.nextShopItem(1); + if (item.item == Item::Four_Fingers) { + fingers = true; + }; + if (item.item == Item::Shortcut) { + shortcut = true; + }; + if (item.item == Item::Smeared_Joker) { + smeared = true; + }; + } + for (int i = 0; i < 6; i++) { + ShopItem item = inst.nextShopItem(2); + if (item.item == Item::Four_Fingers) { + fingers = true; + }; + if (item.item == Item::Shortcut) { + shortcut = true; + }; + if (item.item == Item::Smeared_Joker) { + smeared = true; + }; + } + if (fingers && shortcut && smeared) { + return 1; + } + return 0; +} + +// Benchmark function +// Runs 1 billion seeds of perkeo observatory +// And prints total time and seeds per second +void benchmark() { + long total = 0; + long start = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + Search search(filter_perkeo_observatory, "IMMOLATE", 12, 1000000000); + search.highScore = 10; // No output + search.printDelay = 100000000000; + search.search(); + long end = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + std::cout << "------LONGER TESTING------\n"; + std::cout << "Total time: " << end - start << "ms\n"; + std::cout << "Seeds per second: " << std::fixed << std::setprecision(0) + << 1000000000 / ((end - start) / 1000.0) << "\n"; +} + +void benchmark_quick() { + long total = 0; + long start = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + Search search(filter_perkeo_observatory, "IMMOLATE", 12, 100000000); + search.highScore = 10; // No output + search.printDelay = 100000000000; + search.search(); + long end = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + std::cout << "----PERKEO OBSERVATORY----\n"; + std::cout << "Total time: " << end - start << "ms\n"; + std::cout << "Seeds per second: " << std::fixed << std::setprecision(0) + << 100000000 / ((end - start) / 1000.0) << "\n"; +} + +void benchmark_quick_lucky() { + long total = 0; + long start = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + Search search(filter_lucky, "IMMOLATE", 12, 100000000); + search.highScore = 10; // No output + search.printDelay = 100000000000; + search.search(); + long end = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + std::cout << "-------LUCKY CARDS-------\n"; + std::cout << "Total time: " << end - start << "ms\n"; + std::cout << "Seeds per second: " << std::fixed << std::setprecision(0) + << 100000000 / ((end - start) / 1000.0) << "\n"; +} + +void benchmark_single() { + long total = 0; + long start = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + Search search(filter_perkeo_observatory, "IMMOLATE", 1, 10000000); + search.highScore = 10; // No output + search.printDelay = 100000000000; + search.search(); + long end = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + std::cout << "----SINGLE THREADED PO----\n"; + std::cout << "Total time: " << end - start << "ms\n"; + std::cout << "Seeds per second: " << std::fixed << std::setprecision(0) + << 10000000 / ((end - start) / 1000.0) << "\n"; +} + +void benchmark_blank() { + long total = 0; + long start = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + Search search(filter_blank, "IMMOLATE", 12, 100000000); + search.printDelay = 100000000000; // No output + search.search(); + long end = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + std::cout << "-------BLANK FILTER-------\n"; + std::cout << "Total time: " << end - start << "ms\n"; + std::cout << "Seeds per second: " << std::fixed << std::setprecision(0) + << 100000000 / ((end - start) / 1000.0) << "\n"; +} + +int main() { + /*benchmark_single(); + benchmark_quick(); + benchmark_quick_lucky(); + benchmark_blank(); + benchmark();*/ + Search search(filter_cavendish, "11111J31", 8, 2318107019761); + search.highScore = 5; + search.printDelay = 2318107019761; + search.search(); + return 1; +} \ No newline at end of file diff --git a/immolate/rng.cpp b/immolate/rng.cpp new file mode 100644 index 0000000..6facfb1 --- /dev/null +++ b/immolate/rng.cpp @@ -0,0 +1,67 @@ +#include "rng.hpp" + +const std::string ItemSource::Shop = "sho"; +const std::string ItemSource::Emperor = "emp"; +const std::string ItemSource::High_Priestess = "pri"; +const std::string ItemSource::Judgement = "jud"; +const std::string ItemSource::Wraith = "wra"; +const std::string ItemSource::Arcana_Pack = "ar1"; +const std::string ItemSource::Omen_Globe = "ar2"; +const std::string ItemSource::Celestial_Pack = "pl1"; +const std::string ItemSource::Spectral_Pack = "spe"; +const std::string ItemSource::Standard_Pack = "sta"; +const std::string ItemSource::Buffoon_Pack = "buf"; +const std::string ItemSource::Vagabond = "vag"; +const std::string ItemSource::Superposition = "sup"; +const std::string ItemSource::_8_Ball = "8ba"; +const std::string ItemSource::Seance = "sea"; +const std::string ItemSource::Sixth_Sense = "sixth"; +const std::string ItemSource::Top_Up = "top"; +const std::string ItemSource::Rare_Tag = "rta"; +const std::string ItemSource::Uncommon_Tag = "uta"; +const std::string ItemSource::Purple_Seal = "8ba"; +const std::string ItemSource::Soul = "sou"; +const std::string ItemSource::Riff_Raff = "rif"; +const std::string ItemSource::Cartomancer = "car"; + +const std::string RandomType::Joker_Common = "Joker1"; +const std::string RandomType::Joker_Uncommon = "Joker2"; +const std::string RandomType::Joker_Rare = "Joker3"; +const std::string RandomType::Joker_Legendary = "Joker4"; +const std::string RandomType::Joker_Rarity = "rarity"; +const std::string RandomType::Joker_Edition = "edi"; +const std::string RandomType::Misprint = "misprint"; +const std::string RandomType::Standard_Has_Enhancement = "stdset"; +const std::string RandomType::Enhancement = "Enhanced"; +const std::string RandomType::Card = "front"; +const std::string RandomType::Standard_Edition = "standard_edition"; +const std::string RandomType::Standard_Has_Seal = "stdseal"; +const std::string RandomType::Standard_Seal = "stdsealtype"; +const std::string RandomType::Shop_Pack = "shop_pack"; +const std::string RandomType::Tarot = "Tarot"; +const std::string RandomType::Spectral = "Spectral"; +const std::string RandomType::Tags = "Tag"; +const std::string RandomType::Shuffle_New_Round = "nr"; +const std::string RandomType::Card_Type = "cdt"; +const std::string RandomType::Planet = "Planet"; +const std::string RandomType::Lucky_Mult = "lucky_mult"; +const std::string RandomType::Lucky_Money = "lucky_money"; +const std::string RandomType::Sigil = "sigil"; +const std::string RandomType::Ouija = "ouija"; +const std::string RandomType::Wheel_of_Fortune = "wheel_of_fortune"; +const std::string RandomType::Gros_Michel = "gros_michel"; +const std::string RandomType::Cavendish = "cavendish"; +const std::string RandomType::Voucher = "Voucher"; +const std::string RandomType::Voucher_Tag = "Voucher_fromtag"; +const std::string RandomType::Orbital_Tag = "orbital"; +const std::string RandomType::Soul = "soul_"; +const std::string RandomType::Erratic = "erratic"; +const std::string RandomType::Eternal = + "stake_shop_joker_eternal"; // Eternal jokers pre 1.0.1 +const std::string RandomType::Perishable = "ssjp"; +const std::string RandomType::Rental = "ssjr"; +const std::string RandomType::Eternal_Perishable = "etperpoll"; +const std::string RandomType::Rental_Pack = "packssjr"; +const std::string RandomType::Eternal_Perishable_Pack = "packetper"; +const std::string RandomType::Boss = "boss"; +const std::string RandomType::Omen_Globe = "omen_globe"; \ No newline at end of file diff --git a/immolate/rng.hpp b/immolate/rng.hpp new file mode 100644 index 0000000..df4085b --- /dev/null +++ b/immolate/rng.hpp @@ -0,0 +1,75 @@ +#ifndef RNG_HPP +#define RNG_HPP + +#include + +struct ItemSource { + static const std::string Shop; + static const std::string Emperor; + static const std::string High_Priestess; + static const std::string Judgement; + static const std::string Wraith; + static const std::string Arcana_Pack; + static const std::string Omen_Globe; + static const std::string Celestial_Pack; + static const std::string Spectral_Pack; + static const std::string Standard_Pack; + static const std::string Buffoon_Pack; + static const std::string Vagabond; + static const std::string Superposition; + static const std::string _8_Ball; + static const std::string Seance; + static const std::string Sixth_Sense; + static const std::string Top_Up; + static const std::string Rare_Tag; + static const std::string Uncommon_Tag; + static const std::string Purple_Seal; + static const std::string Soul; + static const std::string Riff_Raff; + static const std::string Cartomancer; +}; + +struct RandomType { + static const std::string Joker_Common; + static const std::string Joker_Uncommon; + static const std::string Joker_Rare; + static const std::string Joker_Legendary; + static const std::string Joker_Rarity; + static const std::string Joker_Edition; + static const std::string Misprint; + static const std::string Standard_Has_Enhancement; + static const std::string Enhancement; + static const std::string Card; + static const std::string Standard_Edition; + static const std::string Standard_Has_Seal; + static const std::string Standard_Seal; + static const std::string Shop_Pack; + static const std::string Tarot; + static const std::string Spectral; + static const std::string Tags; + static const std::string Shuffle_New_Round; + static const std::string Card_Type; + static const std::string Planet; + static const std::string Lucky_Mult; + static const std::string Lucky_Money; + static const std::string Sigil; + static const std::string Ouija; + static const std::string Wheel_of_Fortune; + static const std::string Gros_Michel; + static const std::string Cavendish; + static const std::string Voucher; + static const std::string Voucher_Tag; + static const std::string Orbital_Tag; + static const std::string Soul; + static const std::string Erratic; + static const std::string Eternal; // Eternal jokers pre 1.0.1 + static const std::string Perishable; + static const std::string Rental; + static const std::string Eternal_Perishable; + static const std::string Rental_Pack; + static const std::string Eternal_Perishable_Pack; + static const std::string Boss; + static const std::string Omen_Globe; +}; + +#endif // RNG_HPP \ No newline at end of file diff --git a/immolate/search.cpp b/immolate/search.cpp new file mode 100644 index 0000000..e69de29 diff --git a/immolate/search.hpp b/immolate/search.hpp new file mode 100644 index 0000000..4c96329 --- /dev/null +++ b/immolate/search.hpp @@ -0,0 +1,111 @@ +#ifndef SEARCH_HPP +#define SEARCH_HPP + +#include "instance.hpp" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +const long long BLOCK_SIZE = 1000000; + +class Search { +public: + std::atomic seedsProcessed{0}; + std::atomic highScore{1}; + long long printDelay = 10000000; + std::function filter; + std::atomic found{false}; // Atomic flag to signal when a solution is found + Seed foundSeed; // Store the found seed + bool exitOnFind = false; + long long startSeed; + int numThreads; + long long numSeeds; + std::mutex mtx; + std::atomic nextBlock{0}; // Shared index for the next block to be processed + + Search(std::function f) { + filter = f; + startSeed = 0; + numThreads = 1; + numSeeds = 2318107019761; + } + + Search(std::function f, int t) { + filter = f; + startSeed = 0; + numThreads = t; + numSeeds = 2318107019761; + } + + Search(std::function f, int t, long long n) { + filter = f; + startSeed = 0; + numThreads = t; + numSeeds = n; + }; + Search(std::function f, std::string seed, int t, long long n) { + filter = f; + startSeed = Seed(seed).getID(); + numThreads = t; + numSeeds = n; + }; + + void searchBlock(long long start, long long end) { + Seed s = Seed(start); + Instance inst(s); + for (long long i = start; i < end; ++i) { + if (found) return; // Exit if a solution is found + // Perform the search on the seed + int result = filter(inst); + if (result >= highScore) { + std::lock_guard lock(mtx); + highScore = result; + foundSeed = s; + std::cout << "Found seed: " << s.tostring() << " (" << result << ")" + << std::endl; + if (exitOnFind) { + found = true; + return; + } + } + seedsProcessed++; + if (seedsProcessed % printDelay == 0) { + std::cout << "Seeds processed: " << seedsProcessed << std::endl; + } + inst.next(); + } + } + + std::string search() { + std::vector threads; + long long totalBlocks = (numSeeds + BLOCK_SIZE - 1) / BLOCK_SIZE; + for (int t = 0; t < numThreads; t++) { + threads.emplace_back([this, totalBlocks]() { + while (true) { + long long block = nextBlock.fetch_add(1); + if (block >= totalBlocks) break; + long long start = block * BLOCK_SIZE + startSeed; + long long end = std::min(start + BLOCK_SIZE, numSeeds + startSeed); + searchBlock(start, end); + } + }); + } + + for (auto& thread : threads) { + thread.join(); + } + + return foundSeed.tostring(); + } +}; + +#endif \ No newline at end of file diff --git a/immolate/seed.cpp b/immolate/seed.cpp new file mode 100644 index 0000000..0595dba --- /dev/null +++ b/immolate/seed.cpp @@ -0,0 +1,112 @@ +#include "seed.hpp" +#include "util.hpp" +#include + +Seed::Seed() { + seed.fill(-1); + length = 0; + for (int i = 0; i < 8; i++) { + cache[i].fill(-1); + } +} + +Seed::Seed(std::string strSeed) { + seed.fill(-1); + length = strSeed.size(); + for (int i = 0; i < 8; i++) { + cache[i].fill(-1); + } + // Note: Assumes this is safe + for (long unsigned int i = 0; i < strSeed.size(); i++) { + seed[strSeed.size() - 1 - i] = charSeeds[strSeed[i]]; + } +} + +Seed::Seed(long long id) { + length = 0; + for (int i = 0; i < 8; i++) { + cache[i].fill(-1); + } + for (int i = 0; i < 8; i++) { + if (id > 0) { + length++; + seed[i] = (id - 1) / idCoeff[i]; + id -= 1 + seed[i] * idCoeff[i]; + } else { + seed[i] = -1; + } + } +} + +std::string Seed::tostring() { + std::string strSeed; + for (int i = 7; i >= 0; i--) { + if (seed[i] != -1) { + strSeed.push_back(seedChars[seed[i]]); + } + } + return strSeed; +} + +void Seed::debugprint() { + for (int i = 0; i < 8; i++) { + std::cout << seed[i] << " "; + } + std::cout << std::endl; +} + +long long Seed::getID() { + long long id = 0; + for (int i = 0; i <= 7; i++) { + if (seed[i] >= 0) { + id += idCoeff[i] * seed[i] + 1; + } + } + return id; +} + +void Seed::next() { + if (length < 8) { + seed[length] = 0; + length++; + } else { + int i = 7; + while (i >= 0) { + cache[i].fill(-1); + if (seed[i] == 34) { + seed[i] = -1; + length--; + } else { + seed[i]++; + break; + } + i--; + } + } +} + +// Not optimized for performance +// I don't think this will need to be implemented in searching +void Seed::next(int x) { + long long newID = (getID() + x) % 2318107019761; + *this = Seed(newID); +} + +double Seed::pseudohash(int prefixLength) { + if (length == 0) return 1; //Empty seed edge case + + if (cache[length-1][prefixLength+length-1] == -1) { + int i = length - 2; + while (i >= 0 && cache[i][prefixLength+length-1] == -1) { + i--; + } + if (i == -1) { + cache[0][prefixLength+length-1] = pseudostep(seedChars[seed[0]], prefixLength+length, 1); + i = 0; + } + for (int j = i+1; j < length; j++) { + cache[j][prefixLength+length-1] = pseudostep(seedChars[seed[j]], prefixLength+length-j, cache[j-1][prefixLength+length-1]); + } + } + return cache[length-1][prefixLength+length-1]; +} \ No newline at end of file diff --git a/immolate/seed.hpp b/immolate/seed.hpp new file mode 100644 index 0000000..8e184be --- /dev/null +++ b/immolate/seed.hpp @@ -0,0 +1,49 @@ +#ifndef SEED_HPP +#define SEED_HPP + +#include +#include + +// Seed helper class +// Caches hashing info recursively to save speed +// Because of that, also has an interesting order for seeds: +// , 1, 11, 111, ..., 11111111, 21111111, 31111111, ..., Z1111111, +// 2111111, 12111111, ..., ZZ111111, 211111, ..., ZZZZZZZZ +const std::string seedChars = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const std::array charSeeds = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, + 8, -1, -1, -1, -1, -1, -1, -1, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; +const std::array idCoeff = { + 66231629136, 1892332261, 54066636, 1544761, 44136, 1261, 36, 1}; + +struct Seed { + // -1 is blank, 0 to 34 represent valid characters + // To aid in hashing, stored right to left + std::array seed; + + int length; + + // The cache. Stored as [position in seed][length of string] + std::array, 8> cache; + + Seed(); + Seed(std::string strSeed); + Seed(long long id); + + std::string tostring(); + void debugprint(); + long long getID(); + + void next(); + void next(int x); + + double pseudohash(int prefixLength); +}; + +#endif // SEED_HPP \ No newline at end of file diff --git a/immolate/util.cpp b/immolate/util.cpp new file mode 100644 index 0000000..82df8f6 --- /dev/null +++ b/immolate/util.cpp @@ -0,0 +1,178 @@ +#include "util.hpp" +#include +#include + +LuaRandom::LuaRandom(double seed) { + double d = seed; + uint64_t r = 0x11090601; + for (int i = 0; i < 4; i++) { + uint64_t m = 1ull << (r & 255); + r >>= 8; + d = d * 3.14159265358979323846 + 2.7182818284590452354; + dbllong u; + u.dbl = d; + if (u.ulong < m) + u.ulong += m; + state[i] = u.ulong; + } + for (int i = 0; i < 10; i++) { + _randint(); + } +} + +LuaRandom::LuaRandom() { LuaRandom(0); } + +uint64_t LuaRandom::_randint() { + uint64_t z = 0; + uint64_t r = 0; + z = state[0]; + z = (((z << 31ull) ^ z) >> 45ull) ^ ((z & (MAX_UINT64 << 1ull)) << 18ull); + r ^= z; + state[0] = z; + z = state[1]; + z = (((z << 19ull) ^ z) >> 30ull) ^ ((z & (MAX_UINT64 << 6ull)) << 28ull); + r ^= z; + state[1] = z; + z = state[2]; + z = (((z << 24ull) ^ z) >> 48ull) ^ ((z & (MAX_UINT64 << 9ull)) << 7ull); + r ^= z; + state[2] = z; + z = state[3]; + z = (((z << 21ull) ^ z) >> 39ull) ^ ((z & (MAX_UINT64 << 17ull)) << 8ull); + r ^= z; + state[3] = z; + return r; +} + +uint64_t LuaRandom::randdblmem() { + return (_randint() & 4503599627370495ull) | 4607182418800017408ull; +} + +double LuaRandom::random() { + dbllong u; + u.ulong = randdblmem(); + return u.dbl - 1.0; +} + +int LuaRandom::randint(int min, int max) { + return (int)(random() * (max - min + 1)) + min; +} + +int portable_clzll(uint64_t x) { + if (x == 0) + return 64; // Undefined for 0, by convention we return 64 + +#if defined(__GNUC__) || defined(__clang__) + return __builtin_clzll(x); +#elif defined(_MSC_VER) + unsigned long index; + if (_BitScanReverse64(&index, x)) { + return 63 - index; + } + return 64; +#else + // Fallback for other compilers (manual bit manipulation) + int n = 0; + if (x <= 0x00000000FFFFFFFF) { + n += 32; + x <<= 32; + } + if (x <= 0x0000FFFFFFFFFFFF) { + n += 16; + x <<= 16; + } + if (x <= 0x00FFFFFFFFFFFFFF) { + n += 8; + x <<= 8; + } + if (x <= 0x0FFFFFFFFFFFFFFF) { + n += 4; + x <<= 4; + } + if (x <= 0x3FFFFFFFFFFFFFFF) { + n += 2; + x <<= 2; + } + if (x <= 0x7FFFFFFFFFFFFFFF) { + n += 1; + } + return n; +#endif +} + +double fract(double x) { + uint64_t x_int; + + std::memcpy(&x_int, &x, sizeof(x_int)); + + uint64_t expo = (x_int & DBL_EXPO) >> DBL_MANT_SZ; + if (expo < DBL_EXPO_BIAS) { + return x; + } + if (expo == ((1 << DBL_EXPO_SZ) - 1)) { + return std::numeric_limits::quiet_NaN(); + } + uint64_t expo_biased = expo - DBL_EXPO_BIAS; + if (expo_biased >= DBL_MANT_SZ) { + return 0; + } + uint64_t mant = x_int & DBL_MANT; + uint64_t frac_mant = mant & ((1ull << (DBL_MANT_SZ - expo_biased)) - 1); + if (frac_mant == 0) { + return 0; + } + uint64_t frac_lzcnt = portable_clzll(frac_mant) - (64 - DBL_MANT_SZ); + uint64_t res_expo = (expo - frac_lzcnt - 1) << DBL_MANT_SZ; + uint64_t res_mant = (frac_mant << (frac_lzcnt + 1)) & DBL_MANT; + uint64_t res = res_expo | res_mant; + + double result; + std::memcpy(&result, &res, sizeof(result)); + return result; +} + +double pseudohash(std::string s) { + double num = 1; + for (size_t i = s.length(); i > 0; i--) { + num = fract(1.1239285023 / num * s[i - 1] * 3.141592653589793116 + + 3.141592653589793116 * i); + } + return num; +} + +double pseudohash_from(std::string s, double num) { + for (size_t i = s.length(); i > 0; i--) { + num = fract(1.1239285023 / num * s[i - 1] * 3.141592653589793116 + + 3.141592653589793116 * i); + } + return num; +} + +double pseudostep(char s, int pos, double num) { + return fract(1.1239285023 / num * s * 3.141592653589793116 + + 3.141592653589793116 * pos); +} + +std::string anteToString(int a) { + if (a < 10) + return {(char)(0x30 + a)}; + else + return {(char)(0x30 + a / 10), (char)(0x30 + a % 10)}; +} + +const double inv_prec = std::pow(10.0, 13); +const double two_inv_prec = std::pow(2.0, 13); +const double five_inv_prec = std::pow(5.0, 13); + +double round13(double x) { + double normal_case = std::round(x * inv_prec) / inv_prec; + if (normal_case == + (std::round(std::nextafter(x, -1) * inv_prec) / inv_prec)) { + return normal_case; + } + double truncated = fract(x * two_inv_prec) * five_inv_prec; + if (fract(truncated) >= 0.5) { + return (std::floor(x * inv_prec) + 1) / inv_prec; + } + return std::floor(x * inv_prec) / inv_prec; +} \ No newline at end of file diff --git a/immolate/util.hpp b/immolate/util.hpp new file mode 100644 index 0000000..4fbbe4e --- /dev/null +++ b/immolate/util.hpp @@ -0,0 +1,46 @@ +#ifndef UTIL_HPP +#define UTIL_HPP + +#include +#include +#include + +const uint64_t MAX_UINT64 = 18446744073709551615ull; + +typedef union DoubleLong { + double dbl; + uint64_t ulong; +} dbllong; + +struct LuaRandom { + uint64_t state[4]; + LuaRandom(double seed); + LuaRandom(); + uint64_t _randint(); + uint64_t randdblmem(); + double random(); + int randint(int min, int max); +}; + +#define DBL_EXPO 0x7FF0000000000000 +#define DBL_MANT 0x000FFFFFFFFFFFFF + +#define DBL_EXPO_SZ 11 +#define DBL_MANT_SZ 52 + +#define DBL_EXPO_BIAS 1023 + +#if defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanReverse64) +#endif + +int portable_clzll(uint64_t x); +double fract(double x); +double pseudohash(std::string s); +double pseudohash_from(std::string s, double num); +double pseudostep(char s, int pos, double num); +std::string anteToString(int a); +double round13(double x); + +#endif // UTIL_HPP \ No newline at end of file From 5c20c88d135bd57d155a50ea97a87ea3865496f9 Mon Sep 17 00:00:00 2001 From: OceanRamen Date: Sun, 14 Jun 2026 02:35:57 +0100 Subject: [PATCH 2/5] Remove immolate/ and .github/ from branch Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/copilot-instructions.md | 25 - .github/ui_modding.md | 136 -- immolate/functions.cpp | 11 - immolate/functions.hpp | 639 ------ immolate/immolate.cpp | 246 --- immolate/immolate.hpp | 143 -- immolate/instance.hpp | 135 -- immolate/items.cpp | 356 --- immolate/items.hpp | 3578 ------------------------------- immolate/main.cpp | 282 --- immolate/rng.cpp | 67 - immolate/rng.hpp | 75 - immolate/search.cpp | 0 immolate/search.hpp | 111 - immolate/seed.cpp | 112 - immolate/seed.hpp | 49 - immolate/util.cpp | 178 -- immolate/util.hpp | 46 - 18 files changed, 6189 deletions(-) delete mode 100644 .github/copilot-instructions.md delete mode 100644 .github/ui_modding.md delete mode 100644 immolate/functions.cpp delete mode 100644 immolate/functions.hpp delete mode 100644 immolate/immolate.cpp delete mode 100644 immolate/immolate.hpp delete mode 100644 immolate/instance.hpp delete mode 100644 immolate/items.cpp delete mode 100644 immolate/items.hpp delete mode 100644 immolate/main.cpp delete mode 100644 immolate/rng.cpp delete mode 100644 immolate/rng.hpp delete mode 100644 immolate/search.cpp delete mode 100644 immolate/search.hpp delete mode 100644 immolate/seed.cpp delete mode 100644 immolate/seed.hpp delete mode 100644 immolate/util.cpp delete mode 100644 immolate/util.hpp diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index 14cb8a5..0000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,25 +0,0 @@ -# Brainstorm Copilot Instructions - -- **Purpose**: Balatro mod that automates rerolls to find seeds matching user-selected filters (tags, vouchers, packs) and restarts runs accordingly. Main logic lives in [Core/Brainstorm.lua](Core/Brainstorm.lua); a working options tab is in [UI/ui.lua](UI/ui.lua); older prototypes live under [Debug](Debug). -- **Load flow**: The mod is injected via [lovely.toml](lovely.toml): `nativefs.lua` is loaded before `main.lua`, `Core/Brainstorm.lua` is appended to `main.lua`, and `Brainstorm.init()` runs after the profile load patch in `game.lua`. -- **Globals & patching**: `Brainstorm.init()` discovers its own path, loads config, and executes `UI/ui.lua`. Core behaviors monkeypatch `Controller:key_press_update`, `Game:update`, and `create_UIBox_round_scores_row` while calling the originals; keep these reference patterns when extending patches. -- **Config storage**: Defaults sit in `Brainstorm.DEFAULT_CONFIG` and are serialized to `config.lua` via `STR_PACK/STR_UNPACK` using `nativefs`. Config load merges defaults and drops dead fields (e.g., the old `keybind_autoreroll`); when adding settings, seed defaults, keep backward compatibility, and call `Brainstorm.writeConfig()` (UI exit already does this). -- **Controls**: Holding the modifier (`lctrl` by default) + `r` triggers a manual reroll; modifier + `a` toggles auto-reroll. `keybind_autoreroll` is a plain `love.keyboard.isDown` check; respect current bindings when changing input logic. -- **Manual reroll path**: `Brainstorm.reroll()` deletes and restarts the run with the current stake/seed/challenge context, preserving seeded/challenge flags on `G.GAME`. Keep these assignments if you alter reroll behavior. -- **Auto-reroll loop**: `Game:update` drives `Brainstorm.autoReroll()` on a dynamic interval derived from `ar_prefs.spf_int` (1000 ⇒ 0.01s, 500 ⇒ 0.02s) while `Brainstorm.ar_active` is true; a status text is shown after 60 frames via `Brainstorm.attentionText()` and cleared in `removeAttentionText()`. -- **Seed search implementation**: `Brainstorm.autoReroll()` FFI-loads `Immolate.dll` and calls `brainstorm(seed, pack, tag, souls)` (DLL ignores voucher/observatory/perkeo in current builds); keep the Lua `ffi.cdef` in sync with the DLL you ship. It writes `G.GAME.used_filter` and `filter_info` for downstream display; keep these fields if you change the flow. If the DLL is missing or lacks the symbol, auto-reroll now disables itself instead of crashing. -- **Immolate source**: The C++ sources live in [immolate/](immolate); `immolate.cpp` wires globals `BRAINSTORM_PACK/TAG/SOULS` into a `Search` over seeds and exports `brainstorm`/`brainstorm_cpp` plus `free_result`. Search walks blocks of 1,000,000 seeds, is multi-threaded, and stops on the first seed meeting the filter when `exitOnFind` is true. Filters currently check first-pack type, first-tag match, and require N souls from the first Mega Arcana pack; add new filters there if the DLL needs to evolve. -- **DLL version drift**: The shipped `Immolate.dll` may be older than the sources under [immolate/](immolate). Treat the C++ tree as reference, but verify exports/signatures against the actual DLL before changing `ffi.cdef` or call sites. -- **Filters & prefs**: Search options live under `Brainstorm.config.ar_filters` (pack, tag, voucher ids/names, soul skips, instant observatory/perkeo) and `ar_prefs` (seeds-per-frame). UI callbacks in [UI/ui.lua](UI/ui.lua) update these and immediately persist. -- **UI integration**: `create_tabs` is patched to append a "Brainstorm" tab (only when `tab_h == 7.05`, matching the options screen). It uses base-game helpers (`create_option_cycle`, `create_toggle`) to render cycles/toggles. `G.FUNCS.exit_overlay_menu` is wrapped to save config on close. -- **Display tweaks**: `create_UIBox_round_scores_row` colors the seed label red when seeded and blue when a filtered seed was used; preserve this branch if altering round score rows. -- **Vendored dependencies**: [nativefs.lua](nativefs.lua) is a bundled filesystem shim (LuaJIT+FFI); avoid modifications unless fixing I/O. [Immolate.dll](Immolate.dll) is an external binary providing the seed search routine. [steamodded_compat.lua](steamodded_compat.lua) only carries mod metadata. -- **Styling & formatting**: Lua uses Stylua ([stylua.toml](stylua.toml), [\.vscode/settings.json](.vscode/settings.json)) with 2-space indents, 80-col preference, and double-quote bias. See [style_guide.md](style_guide.md) for naming (snake_case vars, camelCase functions, PascalCase classes) and general conventions. -- **Adding settings**: 1) Extend `Brainstorm.config` defaults. 2) Wire UI controls in [UI/ui.lua](UI/ui.lua) with `G.FUNCS.change_*` callbacks that update config and call `Brainstorm.writeConfig()`. 3) Ensure new values are passed into `autoReroll()` if they influence search. -- **Touching globals**: Many helpers assume global tables (`G`, `G.FUNCS`, `G.UIT`). Cache originals before wrapping and re-call them to avoid breaking base game behavior. -- **Debug folder**: [Debug/settings.lua](Debug/settings.lua) and [Debug/ui.lua](Debug/ui.lua) are experimental layouts; treat them as reference prototypes, not the active UI. -- **Testing workflow**: No automated tests. Validation is manual by running Balatro/Steamodded with this mod enabled; exercise keybinds and the "Brainstorm" options tab to confirm config persistence and reroll behavior. -- **Release/versioning**: Current version string is `Brainstorm v2.2.0-alpha` in `Brainstorm.VERSION`; update this and `lovely.toml`/headers if you ship releases. -- **Safety tips**: Avoid touching `config.lua` by hand; prefer the UI or defaults. Keep non-ASCII out of sources. Do not remove calls to `Brainstorm.writeConfig()` around UI exits. - -If any part of this feels unclear or incomplete, tell me which sections to expand or examples to add. diff --git a/.github/ui_modding.md b/.github/ui_modding.md deleted file mode 100644 index 7118845..0000000 --- a/.github/ui_modding.md +++ /dev/null @@ -1,136 +0,0 @@ -# Balatro UI Modding Guide - -This document explains how to build custom UI using Balatro’s built-in UI engine, with a focus on creating custom settings tabs and reusable controls. - -## Mental model - -- The UI is a tree of nodes built from plain Lua tables. You pass that tree into `UIBox{definition=..., config=...}` to instantiate it. -- Node types are enumerated in `G.UIT` (see `globals.lua`): - - `T` text, `B` box, `C` column, `R` row, `O` object (Sprite/DynaText/etc.), `ROOT`, `S` slider, `I` input. -- Every node table uses keys: - - `n` (required): the node type from `G.UIT.*`. - - `config` (optional): alignment, padding, colors, callbacks, refs, ids. - - `nodes` (optional): array of child nodes. -- Layout is resolved by `UIElement:set_alignments` (engine/ui.lua). `config.align` letters: `c` center vertically, `m` center horizontally, `b` bottom, `r` right. Top/left are default. `padding` defaults to `G.UIT.padding`. -- `ref_table` + `ref_value` binds UI to live data; text and object refs auto-refresh when values change. -- Interactivity uses `config.button = 'name'` to invoke `G.FUNCS.name`. Helpers wire `hover`, `shadow`, etc. for you. - -## Useful engine locations - -- Node enums: `globals.lua` (`self.UIT = { T=1, B=2, C=3, R=4, O=5, ROOT=7, S=8, I=9 }`). -- UIBox creation, sizing, alignment: `engine/ui.lua` (class `UIBox`, `UIElement`). -- Input/callbacks for buttons, sliders, toggles, option cycles: `functions/button_callbacks.lua`. -- Prefab builders (sliders, toggles, option cycles, tabs, buttons): `functions/UI_definitions.lua` near the helper definitions. -- Settings tabs pattern: `create_UIBox_settings` and `G.UIDEF.settings_tab` in `functions/UI_definitions.lua`. - -## Core building blocks (prefabs) - -Use these helpers instead of raw nodes where possible: - -- **Button**: `UIBox_button(args)` → clickable pill. - - - Args: `button` (G.FUNCS name), `label` array, `colour`, `minw/minh`, `choice/chosen` for toggle-style buttons, `focus_args` for controller nav. - -- **Slider**: `create_slider(args)` → drag or discrete slider bound to `ref_table/ref_value`. - - - Required: `ref_table`, `ref_value`, `min`, `max` (numbers). Optional: `label`, `w/h`, `callback`, `decimal_places`, `colour`. - - Behavior implemented by `G.FUNCS.slider` and `G.FUNCS.slider_descreet`. - -- **Toggle**: `create_toggle(args)` → checkbox-style toggle bound to `ref_table/ref_value`. - - - Required: `ref_table`, `ref_value`, `label`. - - Optional: `callback` (runs on change), `info` (array of extra text lines), `active_colour`, `inactive_colour`, `scale`. - - Behavior: `G.FUNCS.toggle_button` and `G.FUNCS.toggle`. - -- **Option Cycle**: `create_option_cycle(args)` → left/right cycle with pips. - - - Required: `options` (array), `current_option` (1-based), `opt_callback` (G.FUNCS name or nil). - - Optional: `label`, `info`, `w/h`, `scale`, `cycle_shoulders` (adds shoulder prompts), `no_pips`. - - Behavior: `G.FUNCS.option_cycle` updates `current_option` and `current_option_val`, fires `opt_callback`. - -- **Tabs**: `create_tabs(args)` → tab strip + content area. - - - Each tab entry: `{ label=..., chosen=bool, tab_definition_function=fn, tab_definition_function_args=... }`. - - `create_tabs` instantiates the chosen tab’s definition into `tab_contents`. - - Useful args: `tab_h`, `tab_w`, `tab_alignment`, `snap_to_nav`, `no_shoulders`. - -- **Overlay shell**: `create_UIBox_generic_options(args)` → modal frame with optional back button and infotip slot. - - - Args: `contents` (array or single node), `back_func`, `colour`, `bg_colour`, `outline_colour`, `no_back`, `snap_back`. - -- **Dyn container**: `UIBox_dyn_container(inner_table, horizontal, colour_override, background_override, flipped, padding)` → framed grouping block. - -- **Text input**: `create_text_input(args)` for simple keyboard input; binds to `ref_table/ref_value` with max length, prompt text. - -## Making a custom settings tab (example) - -Add a new tab alongside existing ones in `create_UIBox_settings` and define its builder. Example: - -```lua --- 1) Define your tab builder (anywhere after G.UIDEF exists) -function G.UIDEF.settings_tab_fancy() - return {n=G.UIT.ROOT, config={align="cm", padding=0.05, colour=G.C.CLEAR}, nodes={ - create_toggle({label="Enable Fancy Mode", ref_table=G.SETTINGS, ref_value="fancy_mode", callback=function(val) - G.FUNCS.apply_fancy_mode(val) - end}), - create_slider({label="Fancy Intensity", w=4, h=0.4, ref_table=G.SETTINGS, ref_value="fancy_intensity", min=0, max=100, callback="apply_fancy_intensity"}), - create_option_cycle({label="Fancy Style", options={"Soft","Bold","Loud"}, current_option=1, opt_callback="set_fancy_style"}) - }} -end - --- 2) Insert the tab into the settings tabs list (in create_UIBox_settings) -tabs[#tabs+1] = { - label = "Fancy", - tab_definition_function = G.UIDEF.settings_tab_fancy, - tab_definition_function_args = nil -} -``` - -Then implement the callbacks you referenced (e.g., `G.FUNCS.apply_fancy_mode`, `G.FUNCS.apply_fancy_intensity`, `G.FUNCS.set_fancy_style`). They’ll receive the cycle/slider/toggle configs or values per the existing callbacks in `button_callbacks.lua`. - -## Making a standalone modal/panel - -1. Build your content nodes using rows/cols and prefabs: - -```lua -local content = { - UIBox_button({label={"Do Thing"}, button="my_action", minw=3}), - create_toggle({label="Flag", ref_table=G.SETTINGS, ref_value="my_flag"}), - create_slider({label="Value", w=4, h=0.4, ref_table=G.SETTINGS, ref_value="my_val", min=0, max=10}) -} -``` - -1. Wrap in `create_UIBox_generic_options({contents = content, back_func = "exit_overlay_menu"})` and pass that as the `definition` to a new `UIBox` to show the overlay. - -## Binding data and IDs - -- Use `ref_table/ref_value` on `T` nodes to auto-update text when values change. -- Use `id` in `config` to fetch elements later with `UIBox:get_UIE_by_ID(id)`. -- Objects (`n=G.UIT.O`) can wrap `Sprite`, `DynaText`, or another `UIBox` via `config.object`. - -## Controller focus - -- `focus_args` on interactive nodes controls navigation; helpers set sensible defaults: sliders (`type='slider'`), cycles (`type='cycle'`), tabs (`type='tab'`), buttons (`nav='wide'`, etc.). -- `snap_to_nav=true` on `create_tabs` helps initial focus within overlays. - -## Gotchas - -- Buttons need `hover=true` (helpers do this) and a `button` string to trigger a callback. -- If you bind text to changing data, a length change triggers a layout recalc; avoid `no_recalc` unless you really need fixed width. -- `id` values must be unique within a UIBox tree. -- Colors are premultiplied alpha tables; `colour[4]` near zero hides the element. - -## Where to look in code - -- Prefab helpers: `functions/UI_definitions.lua` (slider/toggle/cycle/tabs/buttons). -- Input + callbacks: `functions/button_callbacks.lua`. -- Core UI tree + layout: `engine/ui.lua`. -- Node enums/constants: `globals.lua` (G.UIT, colors in G.C). - -## Extending further - -- You can nest `UIBox` instances via `n=G.UIT.O` with `config.object = UIBox{definition=...}` to embed sub-UIs. -- `UIBox_dyn_container` gives quick framed blocks for grouped options. -- Use `create_text_input` if you need player text entry (e.g., seeds, names). - -Keep everything data-first: assemble tables, wire callbacks in `G.FUNCS`, and let the engine handle layout and interaction. diff --git a/immolate/functions.cpp b/immolate/functions.cpp deleted file mode 100644 index 11ee471..0000000 --- a/immolate/functions.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "functions.hpp" - -std::vector PACK_INFO = { - Pack(Item::Arcana_Pack, 3, 1), Pack(Item::Arcana_Pack, 5, 1), - Pack(Item::Arcana_Pack, 5, 2), Pack(Item::Celestial_Pack, 3, 1), - Pack(Item::Celestial_Pack, 5, 1), Pack(Item::Celestial_Pack, 5, 2), - Pack(Item::Standard_Pack, 3, 1), Pack(Item::Standard_Pack, 5, 1), - Pack(Item::Standard_Pack, 5, 2), Pack(Item::Buffoon_Pack, 2, 1), - Pack(Item::Buffoon_Pack, 4, 1), Pack(Item::Buffoon_Pack, 4, 2), - Pack(Item::Spectral_Pack, 2, 1), Pack(Item::Spectral_Pack, 4, 1), - Pack(Item::Spectral_Pack, 4, 2)}; diff --git a/immolate/functions.hpp b/immolate/functions.hpp deleted file mode 100644 index f9599bd..0000000 --- a/immolate/functions.hpp +++ /dev/null @@ -1,639 +0,0 @@ -#ifndef FUNCTIONS_HPP -#define FUNCTIONS_HPP - -#include "instance.hpp" -#include "rng.hpp" -#include - -// Note: Technically, marking everything as inline is not a proper fix. Ideally, -// we'd place these correctly into hpp and cpp files BUT, i want to have sanity - -// Helper functions -inline void Instance::lock(Item item) { locked[(int)item] = true; } -inline void Instance::unlock(Item item) { locked[(int)item] = false; } -inline bool Instance::isLocked(Item item) { return locked[(int)item]; } - -// Lock initializers -inline void Instance::initLocks(int ante, bool freshProfile, bool freshRun) { - if (ante < 2) { - lock(Item::The_Mouth); - lock(Item::The_Fish); - lock(Item::The_Wall); - lock(Item::The_House); - lock(Item::The_Mark); - lock(Item::The_Wheel); - lock(Item::The_Arm); - lock(Item::The_Water); - lock(Item::The_Needle); - lock(Item::The_Flint); - lock(Item::Negative_Tag); - lock(Item::Standard_Tag); - lock(Item::Meteor_Tag); - lock(Item::Buffoon_Tag); - lock(Item::Handy_Tag); - lock(Item::Garbage_Tag); - lock(Item::Ethereal_Tag); - lock(Item::Top_up_Tag); - lock(Item::Orbital_Tag); - } - if (ante < 3) { - lock(Item::The_Tooth); - lock(Item::The_Eye); - } - if (ante < 4) { - lock(Item::The_Plant); - } - if (ante < 5) { - lock(Item::The_Serpent); - } - if (ante < 6) { - lock(Item::The_Ox); - } - if (freshProfile) { - // Tags - lock(Item::Negative_Tag); - lock(Item::Foil_Tag); - lock(Item::Holographic_Tag); - lock(Item::Polychrome_Tag); - lock(Item::Rare_Tag); - - // Jokers - lock(Item::Golden_Ticket); - lock(Item::Mr_Bones); - lock(Item::Acrobat); - lock(Item::Sock_and_Buskin); - lock(Item::Swashbuckler); - lock(Item::Troubadour); - lock(Item::Certificate); - lock(Item::Smeared_Joker); - lock(Item::Throwback); - lock(Item::Hanging_Chad); - lock(Item::Rough_Gem); - lock(Item::Bloodstone); - lock(Item::Arrowhead); - lock(Item::Onyx_Agate); - lock(Item::Glass_Joker); - lock(Item::Showman); - lock(Item::Flower_Pot); - lock(Item::Blueprint); - lock(Item::Wee_Joker); - lock(Item::Merry_Andy); - lock(Item::Oops_All_6s); - lock(Item::The_Idol); - lock(Item::Seeing_Double); - lock(Item::Matador); - lock(Item::Hit_the_Road); - lock(Item::The_Duo); - lock(Item::The_Trio); - lock(Item::The_Family); - lock(Item::The_Order); - lock(Item::The_Tribe); - lock(Item::Stuntman); - lock(Item::Invisible_Joker); - lock(Item::Brainstorm); - lock(Item::Satellite); - lock(Item::Shoot_the_Moon); - lock(Item::Drivers_License); - lock(Item::Cartomancer); - lock(Item::Astronomer); - lock(Item::Burnt_Joker); - lock(Item::Bootstraps); - - // Vouchers - lock(Item::Overstock_Plus); - lock(Item::Liquidation); - lock(Item::Glow_Up); - lock(Item::Reroll_Glut); - lock(Item::Omen_Globe); - lock(Item::Observatory); - lock(Item::Nacho_Tong); - lock(Item::Recyclomancy); - lock(Item::Tarot_Tycoon); - lock(Item::Planet_Tycoon); - lock(Item::Money_Tree); - lock(Item::Antimatter); - lock(Item::Illusion); - lock(Item::Petroglyph); - lock(Item::Retcon); - lock(Item::Palette); - } - - // Locked in start of run - if (freshRun) { - // Require hand discoveries - lock(Item::Planet_X); - lock(Item::Ceres); - lock(Item::Eris); - lock(Item::Five_of_a_Kind); - lock(Item::Flush_House); - lock(Item::Flush_Five); - - // Requires specific card enhancement - lock(Item::Stone_Joker); // Stone - lock(Item::Steel_Joker); // Steel - lock(Item::Glass_Joker); // Glass - lock(Item::Golden_Ticket); // Gold - lock(Item::Lucky_Cat); // Lucky - - // Requires Gros Michel death - lock(Item::Cavendish); - - // Vouchers - lock(Item::Overstock_Plus); - lock(Item::Liquidation); - lock(Item::Glow_Up); - lock(Item::Reroll_Glut); - lock(Item::Omen_Globe); - lock(Item::Observatory); - lock(Item::Nacho_Tong); - lock(Item::Recyclomancy); - lock(Item::Tarot_Tycoon); - lock(Item::Planet_Tycoon); - lock(Item::Money_Tree); - lock(Item::Antimatter); - lock(Item::Illusion); - lock(Item::Petroglyph); - lock(Item::Retcon); - lock(Item::Palette); - } -} -inline void Instance::initUnlocks(int ante, bool freshProfile) { - if (ante == 2) { - unlock(Item::The_Mouth); - unlock(Item::The_Fish); - unlock(Item::The_Wall); - unlock(Item::The_House); - unlock(Item::The_Mark); - unlock(Item::The_Wheel); - unlock(Item::The_Arm); - unlock(Item::The_Water); - unlock(Item::The_Needle); - unlock(Item::The_Flint); - if (!freshProfile) - unlock(Item::Negative_Tag); - unlock(Item::Standard_Tag); - unlock(Item::Meteor_Tag); - unlock(Item::Buffoon_Tag); - unlock(Item::Handy_Tag); - unlock(Item::Garbage_Tag); - unlock(Item::Ethereal_Tag); - unlock(Item::Top_up_Tag); - unlock(Item::Orbital_Tag); - } - if (ante == 3) { - unlock(Item::The_Tooth); - unlock(Item::The_Eye); - } - if (ante == 4) { - unlock(Item::The_Plant); - } - if (ante == 5) { - unlock(Item::The_Serpent); - } - if (ante == 6) { - unlock(Item::The_Ox); - } -} - -// Card Generators -inline Item Instance::nextTarot(std::string source, int ante, bool soulable) { - std::string anteStr = anteToString(ante); - if (soulable && (params.showman || !isLocked(Item::The_Soul)) && - random(RandomType::Soul + RandomType::Tarot + anteStr) > 0.997) { - return Item::The_Soul; - } - return randchoice(RandomType::Tarot + source + anteStr, TAROTS); -} - -inline Item Instance::nextPlanet(std::string source, int ante, bool soulable) { - std::string anteStr = anteToString(ante); - if (soulable && (params.showman || !isLocked(Item::Black_Hole)) && - random(RandomType::Soul + RandomType::Planet + anteStr) > 0.997) { - return Item::Black_Hole; - } - return randchoice(RandomType::Planet + source + anteStr, PLANETS); -} - -inline Item Instance::nextSpectral(std::string source, int ante, - bool soulable) { - std::string anteStr = anteToString(ante); - if (soulable) { - Item forcedKey = Item::RETRY; - if ((params.showman || !isLocked(Item::The_Soul)) && - random(RandomType::Soul + RandomType::Spectral + anteStr) > 0.997) - forcedKey = Item::The_Soul; - if ((params.showman || !isLocked(Item::Black_Hole)) && - random(RandomType::Soul + RandomType::Spectral + anteStr) > 0.997) - forcedKey = Item::Black_Hole; - if (forcedKey != Item::RETRY) - return forcedKey; - } - return randchoice(RandomType::Spectral + source + anteStr, SPECTRALS); -} - -inline JokerData Instance::nextJoker(std::string source, int ante, - bool hasStickers) { - std::string anteStr = anteToString(ante); - - // Get rarity - Item rarity; - if (source == ItemSource::Soul) - rarity = Item::Legendary; - else if (source == ItemSource::Wraith) - rarity = Item::Rare; - else if (source == ItemSource::Rare_Tag) - rarity = Item::Rare; - else if (source == ItemSource::Uncommon_Tag) - rarity = Item::Uncommon; - else { - double rarityPoll = random(RandomType::Joker_Rarity + anteStr + source); - if (rarityPoll > 0.95) - rarity = Item::Rare; - else if (rarityPoll > 0.7) - rarity = Item::Uncommon; - else - rarity = Item::Common; - } - - // Get edition - int editionRate = 1; - if (isVoucherActive(Item::Glow_Up)) - editionRate = 4; - else if (isVoucherActive(Item::Hone)) - editionRate = 2; - Item edition; - double editionPoll = random(RandomType::Joker_Edition + source + anteStr); - if (editionPoll > 0.997) { - edition = Item::Negative; - } else if (editionPoll > 1 - 0.006 * editionRate) { - edition = Item::Polychrome; - } else if (editionPoll > 1 - 0.02 * editionRate) { - edition = Item::Holographic; - } else if (editionPoll > 1 - 0.04 * editionRate) { - edition = Item::Foil; - } else { - edition = Item::No_Edition; - } - - // Get next joker - Item joker; - if (rarity == Item::Legendary) { - if (params.version > 10099) { - joker = randchoice(RandomType::Joker_Legendary, LEGENDARY_JOKERS); - } else { - joker = randchoice(RandomType::Joker_Legendary + source + anteStr, - LEGENDARY_JOKERS); - } - } else if (rarity == Item::Rare) { - if (params.version > 10099) { - joker = - randchoice(RandomType::Joker_Rare + source + anteStr, RARE_JOKERS); - } else { - joker = randchoice(RandomType::Joker_Rare + source + anteStr, - RARE_JOKERS_100); - } - } else if (rarity == Item::Uncommon) { - if (params.version > 10099) { - joker = randchoice(RandomType::Joker_Uncommon + source + anteStr, - UNCOMMON_JOKERS); - } else { - joker = randchoice(RandomType::Joker_Uncommon + source + anteStr, - UNCOMMON_JOKERS_100); - } - } else if (rarity == Item::Common) { - if (params.version > 10099) { - joker = randchoice(RandomType::Joker_Common + source + anteStr, - COMMON_JOKERS); - } else { - joker = randchoice(RandomType::Joker_Common + source + anteStr, - COMMON_JOKERS_100); - } - } - - // Get next joker stickers - JokerStickers stickers = JokerStickers(); - if (hasStickers) { - if (params.version > 10099) { - double stickerPoll = random(((source == ItemSource::Buffoon_Pack) - ? RandomType::Eternal_Perishable_Pack - : RandomType::Eternal_Perishable) + - anteStr); - if (stickerPoll > 0.7 && params.stake >= Item::Black_Stake) { - if (joker != Item::Gros_Michel && joker != Item::Ice_Cream && - joker != Item::Cavendish && joker != Item::Luchador && - joker != Item::Turtle_Bean && joker != Item::Diet_Cola && - joker != Item::Popcorn && joker != Item::Ramen && - joker != Item::Seltzer && joker != Item::Mr_Bones && - joker != Item::Invisible_Joker) { - stickers.eternal = true; - } - } - if (stickerPoll > 0.4 && stickerPoll <= 0.7 && - params.stake >= Item::Orange_Stake && - joker != Item::Ceremonial_Dagger && joker != Item::Ride_the_Bus && - joker != Item::Runner && joker != Item::Constellation && - joker != Item::Green_Joker && joker != Item::Red_Card && - joker != Item::Madness && joker != Item::Square_Joker && - joker != Item::Vampire && joker != Item::Rocket && - joker != Item::Obelisk && joker != Item::Lucky_Cat && - joker != Item::Flash_Card && joker != Item::Spare_Trousers && - joker != Item::Castle && joker != Item::Wee_Joker) { - stickers.perishable = true; - } - if (params.stake >= Item::Gold_Stake) { - stickers.rental = random(((source == ItemSource::Buffoon_Pack) - ? RandomType::Rental_Pack - : RandomType::Rental) + - anteStr) > 0.7; - } - } else { - if (params.stake >= Item::Black_Stake) { - if (joker != Item::Gros_Michel && joker != Item::Ice_Cream && - joker != Item::Cavendish && joker != Item::Luchador && - joker != Item::Turtle_Bean && joker != Item::Diet_Cola && - joker != Item::Popcorn && joker != Item::Ramen && - joker != Item::Seltzer && joker != Item::Mr_Bones && - joker != Item::Invisible_Joker) { - stickers.eternal = random(RandomType::Eternal + anteStr) > 0.7; - } - } - } - } - - return JokerData(joker, rarity, edition, stickers); -} - -// Shop Logic -inline ShopInstance Instance::getShopInstance() { - double tarotRate = 4; - double planetRate = 4; - double playingCardRate = 0; - double spectralRate = 0; - if (params.deck == Item::Ghost_Deck) { - spectralRate = 2; - } - if (isVoucherActive(Item::Tarot_Tycoon)) { - tarotRate = 32; - } else if (isVoucherActive(Item::Tarot_Merchant)) { - tarotRate = 9.6; - } - if (isVoucherActive(Item::Planet_Tycoon)) { - planetRate = 32; - } else if (isVoucherActive(Item::Planet_Merchant)) { - planetRate = 9.6; - } - if (isVoucherActive(Item::Magic_Trick)) { - playingCardRate = 4; - } - - return ShopInstance(20, tarotRate, planetRate, playingCardRate, spectralRate); -}; - -inline Item shopItemType(ShopInstance shop, double cdtPoll) { - if (cdtPoll < shop.jokerRate) { - return Item::T_Joker; - } - cdtPoll -= shop.jokerRate; - - if (cdtPoll < shop.tarotRate) { - return Item::T_Tarot; - } - cdtPoll -= shop.tarotRate; - - if (cdtPoll < shop.planetRate) { - return Item::T_Planet; - } - cdtPoll -= shop.planetRate; - - if (cdtPoll < shop.playingCardRate) { - return Item::T_Playing_Card; - } - - return Item::T_Spectral; -} - -inline ShopItem Instance::nextShopItem(int ante) { - std::string anteStr = anteToString(ante); - - ShopInstance shop = getShopInstance(); - double cdtPoll = - random(RandomType::Card_Type + anteStr) * shop.getTotalRate(); - Item type = shopItemType(shop, cdtPoll); - - if (type == Item::T_Joker) { - JokerData jkr = nextJoker(ItemSource::Shop, ante, true); - return ShopItem(type, jkr.joker, jkr); - } else if (type == Item::T_Tarot) { - return ShopItem(type, nextTarot(ItemSource::Shop, ante, false)); - } else if (type == Item::T_Planet) { - return ShopItem(type, nextPlanet(ItemSource::Shop, ante, false)); - } else if (type == Item::T_Spectral) { - return ShopItem(type, nextSpectral(ItemSource::Shop, ante, false)); - } - // Todo: Magic Trick support - return ShopItem(); -} - -// Packs and Pack Contents -inline Item Instance::nextPack(int ante) { - if (ante <= 2 && !cache.generatedFirstPack && params.version > 10099) { - cache.generatedFirstPack = true; - return Item::Buffoon_Pack; - } - std::string anteStr = anteToString(ante); - return randweightedchoice(RandomType::Shop_Pack + anteStr, PACKS); -} - -extern std::vector PACK_INFO; - -inline Pack packInfo(Item pack) { - return PACK_INFO[(int)pack - (int)Item::Arcana_Pack]; -} - -inline Card Instance::nextStandardCard(int ante) { - std::string anteStr = anteToString(ante); - - // Enhancement - Item enhancement; - if (random(RandomType::Standard_Has_Enhancement + anteStr) <= 0.6) { - enhancement = Item::No_Enhancement; - } else { - enhancement = randchoice(RandomType::Enhancement + - ItemSource::Standard_Pack + anteStr, - ENHANCEMENTS); - } - - // Base - Item base = - randchoice(RandomType::Card + ItemSource::Standard_Pack + anteStr, CARDS); - - // Edition - Item edition; - double editionPoll = random(RandomType::Standard_Edition + anteStr); - if (editionPoll > 0.988) - edition = Item::Polychrome; - else if (editionPoll > 0.96) - edition = Item::Holographic; - else if (editionPoll > 0.92) - edition = Item::Foil; - else - edition = Item::No_Edition; - - // Seal - Item seal; - if (random(RandomType::Standard_Has_Seal + anteStr) <= 0.8) { - seal = Item::No_Seal; - } else { - double sealPoll = random(RandomType::Standard_Seal + anteStr); - if (sealPoll > 0.75) { - seal = Item::Red_Seal; - } else if (sealPoll > 0.5) { - seal = Item::Blue_Seal; - } else if (sealPoll > 0.25) { - seal = Item::Gold_Seal; - } else { - seal = Item::Purple_Seal; - } - } - - return Card(base, enhancement, edition, seal); -}; - -inline std::vector Instance::nextArcanaPack(int size, int ante) { - std::vector pack; - for (int i = 0; i < size; i++) { - if (isVoucherActive(Item::Omen_Globe) && - random(RandomType::Omen_Globe) > 0.8) { - pack.push_back(nextSpectral(ItemSource::Omen_Globe, ante, true)); - } else { - pack.push_back(nextTarot(ItemSource::Arcana_Pack, ante, true)); - } - if (!params.showman) { - lock(pack[i]); - } - } - for (int i = 0; i < size; i++) { - unlock(pack[i]); - } - return pack; -}; - -inline std::vector Instance::nextCelestialPack(int size, int ante) { - std::vector pack; - for (int i = 0; i < size; i++) { - pack.push_back(nextPlanet(ItemSource::Celestial_Pack, ante, true)); - if (!params.showman) - lock(pack[i]); - } - for (int i = 0; i < size; i++) { - unlock(pack[i]); - } - return pack; -}; - -inline std::vector Instance::nextSpectralPack(int size, int ante) { - std::vector pack; - for (int i = 0; i < size; i++) { - pack.push_back(nextSpectral(ItemSource::Spectral_Pack, ante, true)); - if (!params.showman) - lock(pack[i]); - } - for (int i = 0; i < size; i++) { - unlock(pack[i]); - } - return pack; -}; - -inline std::vector Instance::nextStandardPack(int size, int ante) { - std::vector pack; - for (int i = 0; i < size; i++) { - pack.push_back(nextStandardCard(ante)); - } - return pack; -}; - -inline std::vector Instance::nextBuffoonPack(int size, int ante) { - std::vector pack; - for (int i = 0; i < size; i++) { - pack.push_back(nextJoker(ItemSource::Buffoon_Pack, ante, true)); - if (!params.showman) - lock(pack[i].joker); - } - for (int i = 0; i < size; i++) { - unlock(pack[i].joker); - } - return pack; -}; - -// Misc -inline bool Instance::isVoucherActive(Item voucher) { - return params.vouchers[(int)voucher - (int)Item::Overstock]; -} - -inline void Instance::activateVoucher(Item voucher) { - params.vouchers[(int)voucher - (int)Item::Overstock] = true; - lock(voucher); - // Unlock next level voucher - for (unsigned long int i = 0; i < VOUCHERS.size(); i += 2) { - if (VOUCHERS[i] == voucher) { - unlock(VOUCHERS[i + 1]); - }; - }; -}; - -inline Item Instance::nextVoucher(int ante) { - return randchoice(RandomType::Voucher + anteToString(ante), VOUCHERS); -} - -inline void Instance::setDeck(Item deck) { - params.deck = deck; - if (deck == Item::Magic_Deck) { - activateVoucher(Item::Crystal_Ball); - } - if (deck == Item::Nebula_Deck) { - activateVoucher(Item::Telescope); - } - if (deck == Item::Zodiac_Deck) { - activateVoucher(Item::Tarot_Merchant); - activateVoucher(Item::Planet_Merchant); - activateVoucher(Item::Overstock); - } -} - -inline void Instance::setStake(Item stake) { params.stake = stake; } - -inline Item Instance::nextTag(int ante) { - return randchoice(RandomType::Tags + anteToString(ante), TAGS); -} - -inline Item Instance::nextBoss(int ante) { - constexpr int MAX_BOSSES = - 16; // Adjust this value based on the maximum number of bosses you expect - std::array bossPool; - int numBosses = 0; - - for (unsigned long int i = 0; i < BOSSES.size(); i++) { - if (!isLocked(BOSSES[i])) { - if ((ante % 8 == 0 && BOSSES[i] > Item::B_F_BEGIN) || - (ante % 8 != 0 && BOSSES[i] < Item::B_F_BEGIN)) { - bossPool[numBosses++] = BOSSES[i]; - } - } - } - - if (numBosses == 0) { - for (unsigned long int i = 0; i < BOSSES.size(); i++) { - if ((ante % 8 == 0 && BOSSES[i] > Item::B_F_BEGIN) || - (ante % 8 != 0 && BOSSES[i] < Item::B_F_BEGIN)) { - unlock(BOSSES[i]); - } - } - return nextBoss(ante); - } - - Item chosenBoss = randchoice("boss", bossPool); - lock(chosenBoss); - return chosenBoss; -} - -#endif \ No newline at end of file diff --git a/immolate/immolate.cpp b/immolate/immolate.cpp deleted file mode 100644 index 82719d6..0000000 --- a/immolate/immolate.cpp +++ /dev/null @@ -1,246 +0,0 @@ -#include "functions.hpp" -#include "minijson.hpp" -#include "search.hpp" -#include -#include - -Item BRAINSTORM_PACK = Item::RETRY; -Item BRAINSTORM_TAG = Item::Charm_Tag; -long BRAINSTORM_SOULS = 1; - -long filter(Instance inst) { - if (BRAINSTORM_PACK != Item::RETRY) { - inst.cache.generatedFirstPack = true; // we don't care about Pack 1 - if (inst.nextPack(1) != BRAINSTORM_PACK) { - return 0; - } - } - if (BRAINSTORM_TAG != Item::RETRY) { - if (inst.nextTag(1) != BRAINSTORM_TAG) { - return 0; - } - } - if (BRAINSTORM_SOULS > 0) { - for (int i = 1; i <= BRAINSTORM_SOULS; i++) { - auto tarots = inst.nextArcanaPack(5, 1); // Mega Arcana Pack - bool found_soul = false; - for (int t = 0; t < 5; t++) { - if (tarots[t] == Item::The_Soul) { - found_soul = true; - break; - } - } - if (!found_soul) { - return 0; - } - } - } - return 1; -}; - -IMMOLATE_API std::string brainstorm_cpp(std::string seed, std::string pack, -std::string tag, double souls) { BRAINSTORM_PACK = stringToItem(pack); - BRAINSTORM_TAG = stringToItem(tag); - BRAINSTORM_SOULS = souls; - Search search(filter, seed, 1, 100000000); - search.exitOnFind = true; - return search.search(); -} - -struct Step { - std::string op; - mini_json::Value args; -}; - -static Item parseItemSafe(const mini_json::Value &v) { - if (!v.isString()) return Item::RETRY; - return stringToItem(v.getString()); -} - -static bool matchesItem(const Item actual, const mini_json::Value &args) { - const auto &eq = args["equals"]; - if (eq.isString() && actual != parseItemSafe(eq)) return false; - const auto &inArr = args["in"]; - if (inArr.isArray()) { - bool ok = false; - for (const auto &el : inArr.array) { - if (el.isString() && actual == parseItemSafe(el)) { - ok = true; - break; - } - } - if (!ok) return false; - } - return true; -} - -static bool matchesJoker(const JokerData &jd, const mini_json::Value &match) { - if (!match.isObject()) return true; - if (match["joker"].isString() && jd.joker != parseItemSafe(match["joker"])) return false; - if (match["rarity"].isString() && jd.rarity != parseItemSafe(match["rarity"])) return false; - if (match["edition"].isString() && jd.edition != parseItemSafe(match["edition"])) return false; - const auto &stickers = match["stickers"]; - if (stickers.isObject()) { - if (stickers["eternal"].isBool() && jd.stickers.eternal != stickers["eternal"].getBool()) return false; - if (stickers["perishable"].isBool() && jd.stickers.perishable != stickers["perishable"].getBool()) return false; - if (stickers["rental"].isBool() && jd.stickers.rental != stickers["rental"].getBool()) return false; - } - return true; -} - -static long long getNumber(const mini_json::Value &v, long long def) { - return v.isNumber() ? static_cast(v.number) : def; -} - -static bool applyStep(const Step &step, Instance &inst) { - const auto &args = step.args; - if (step.op == "tag") { - int idx = static_cast(getNumber(args["index"], 1)); - Item val = inst.nextTag(idx); - return matchesItem(val, args); - } - if (step.op == "pack") { - int idx = static_cast(getNumber(args["index"], 1)); - Item val = inst.nextPack(idx); - return matchesItem(val, args); - } - if (step.op == "voucher") { - int idx = static_cast(getNumber(args["index"], 1)); - Item val = inst.nextVoucher(idx); - if (!matchesItem(val, args)) return false; - if (args["activate"].getBool(false)) { - inst.activateVoucher(val); - } - return true; - } - if (step.op == "boss") { - int idx = static_cast(getNumber(args["index"], 1)); - Item val = inst.nextBoss(idx); - return matchesItem(val, args); - } - if (step.op == "joker") { - int draw = static_cast(getNumber(args["draw"], 1)); - int ante = static_cast(getNumber(args["ante"], 1)); - bool stickers = args["has_stickers"].getBool(true); - std::string source = args["source"].getString("Brainstorm_Joker"); - JokerData jd = inst.nextJoker(source, ante, stickers); - // Advance draws if draw > 1 - for (int i = 1; i < draw; i++) { - inst.nextJoker(source, ante, stickers); - } - return matchesJoker(jd, args["match"]); - } - if (step.op == "joker_window") { - int limit = static_cast(getNumber(args["limit"], 1)); - int ante = static_cast(getNumber(args["ante"], 1)); - bool stickers = args["has_stickers"].getBool(true); - std::string source = args["source"].getString("Brainstorm_Joker_Window"); - const auto &match = args["any"]["match"].isObject() ? args["any"]["match"] : args["match"]; - for (int i = 0; i < limit; i++) { - JokerData jd = inst.nextJoker(source, ante, stickers); - if (matchesJoker(jd, match)) return true; - } - return false; - } - if (step.op == "state") { - std::string field = args["field"].getString(""); - if (field == "id") { - long long id = inst.seed.getID(); - const auto &eq = args["equals"]; - if (eq.isNumber() && id != static_cast(eq.number)) return false; - const auto &range = args["range"]; - if (range.isArray() && range.array.size() >= 2) { - long long lo = static_cast(range.array[0].number); - long long hi = static_cast(range.array[1].number); - if (id < lo || id > hi) return false; - } - } - return true; - } - if (step.op == "set") { - const auto &deck = args["deck"]; - const auto &stake = args["stake"]; - if (deck.isString()) inst.setDeck(stringToItem(deck.getString())); - if (stake.isString() || stake.isNumber()) { - if (stake.isString()) { - inst.setStake(stringToItem(stake.getString())); - } else { - inst.setStake(static_cast(static_cast(stake.number))); - } - } - return true; - } - // Unknown op -> fail safely - return false; -} - -static void collectSteps(const mini_json::Value &node, std::vector &out) { - if (node.isObject() && node["all"].isArray()) { - for (const auto &child : node["all"].array) { - collectSteps(child, out); - } - return; - } - if (node.isObject() && node["op"].isString()) { - Step s; - s.op = node["op"].getString(); - s.args = node["args"]; - out.push_back(s); - } -} - -static const char *dupCString(const std::string &str) { - char *c_result = (char *)malloc(str.length() + 1); - if (!c_result) return nullptr; - std::strcpy(c_result, str.c_str()); - return c_result; -} - -IMMOLATE_API const char *brainstorm_query(const char *seed, - const char *query_json) { - std::string seed_str = seed ? seed : ""; - std::string query_str = query_json ? query_json : ""; - - mini_json::Value root; - if (!mini_json::parse(query_str, root) || !root.isObject()) { - return dupCString(""); - } - - std::vector steps; - collectSteps(root["filter"], steps); - if (steps.empty()) { - return dupCString(""); - } - - const auto &search = root["search"]; - int threads = static_cast(getNumber(search["threads"], 1)); - long long max_seeds = getNumber(search["max_seeds"], 100000000); - bool exit_on_find = search["exit_on_find"].getBool(true); - - Search s([steps](Instance inst) { - for (const auto &step : steps) { - if (!applyStep(step, inst)) return 0; - } - return 1; - }, seed_str, threads, max_seeds > 0 ? max_seeds : 100000000); - s.exitOnFind = exit_on_find; - std::string result = s.search(); - return dupCString(result); -} - -extern "C" { - IMMOLATE_API const char* brainstorm(const char* seed, const char* pack, -const char* tag, double souls) { std::string cpp_seed(seed); std::string -cpp_pack(pack); std::string cpp_tag(tag); std::string result = -brainstorm_cpp(cpp_seed, cpp_pack, cpp_tag, souls); - - char* c_result = (char*)malloc(result.length() + 1); - strcpy(c_result, result.c_str()); - - return c_result; - } - - IMMOLATE_API void free_result(const char* result) { - free((void*)result); - } -} diff --git a/immolate/immolate.hpp b/immolate/immolate.hpp deleted file mode 100644 index 8cd5188..0000000 --- a/immolate/immolate.hpp +++ /dev/null @@ -1,143 +0,0 @@ - -#include -#ifdef _WIN32 -#ifdef BUILDING_DLL -#define IMMOLATE_API __declspec(dllexport) -#else -#define IMMOLATE_API __declspec(dllimport) -#endif -#else -#define IMMOLATE_API -#endif - -// Declare the functions with IMMOLATE_API -IMMOLATE_API std::string brainstorm_cpp(std::string seed, std::string pack, - std::string tag, double souls); -IMMOLATE_API const char *brainstorm_query(const char *seed, - const char *query_json); -extern "C" { -IMMOLATE_API const char *brainstorm(const char *seed, const char *pack, - const char *tag, double souls); -IMMOLATE_API void free_result(const char *result); -} - -#ifdef __EMSCRIPTEN__ -#include -using namespace emscripten; -EMSCRIPTEN_BINDINGS(Immolate) { - // instance.hpp - register_vector("VectorStr"); - register_vector("VectorJkr"); - register_vector("VectorCrd"); - class_("InstParams") - .constructor<>() - .constructor() - .property("deck", &InstParams::deck) - .property("stake", &InstParams::stake) - .property("showman", &InstParams::showman) - .property("vouchers", &InstParams::vouchers) - .property("version", &InstParams::version); - class_("Instance") - .constructor() - .function("get_node", &Instance::get_node) - .function("random", &Instance::random) - .function("randint", &Instance::randint) - .function("randchoice", &Instance::randchoice) - .property("params", &Instance::params) - .property("seed", &Instance::seed) - - // functions.hpp - .function("lock", &Instance::lock) - .function("unlock", &Instance::unlock) - .function("isLocked", &Instance::isLocked) - .function("initLocks", &Instance::initLocks) - .function("initUnlocks", &Instance::initUnlocks) - .function("nextTarot", &Instance::nextTarot) - .function("nextPlanet", &Instance::nextPlanet) - .function("nextSpectral", &Instance::nextSpectral) - .function("nextJoker", &Instance::nextJoker) - .function("getShopInstance", &Instance::getShopInstance) - .function("nextShopItem", &Instance::nextShopItem) - .function("nextPack", &Instance::nextPack) - .function("nextStandardCard", &Instance::nextStandardCard) - .function("nextArcanaPack", &Instance::nextArcanaPack) - .function("nextCelestialPack", &Instance::nextCelestialPack) - .function("nextSpectralPack", &Instance::nextSpectralPack) - .function("nextBuffoonPack", &Instance::nextBuffoonPack) - .function("nextStandardPack", &Instance::nextStandardPack) - .function("isVoucherActive", &Instance::isVoucherActive) - .function("activateVoucher", &Instance::activateVoucher) - .function("nextVoucher", &Instance::nextVoucher) - .function("setDeck", &Instance::setDeck) - .function("setStake", &Instance::setStake) - .function("nextTag", &Instance::nextTag) - .function("nextBoss", &Instance::nextBoss); - function("packInfo", &packInfo); - - // items.hpp - class_("ShopInstance") - .constructor<>() - .constructor() - .function("getTotalRate", &ShopInstance::getTotalRate) - .property("jokerRate", &ShopInstance::jokerRate) - .property("tarotRate", &ShopInstance::tarotRate) - .property("planetRate", &ShopInstance::planetRate) - .property("playingCardRate", &ShopInstance::playingCardRate) - .property("spectralRate", &ShopInstance::spectralRate); - class_("JokerStickers") - .constructor<>() - .constructor() - .property("eternal", &JokerStickers::eternal) - .property("perishable", &JokerStickers::perishable) - .property("rental", &JokerStickers::rental); - class_("JokerData") - .constructor<>() - .constructor() - .property("joker", &JokerData::joker) - .property("rarity", &JokerData::rarity) - .property("edition", &JokerData::edition) - .property("stickers", &JokerData::stickers); - class_("ShopItem") - .constructor<>() - .constructor() - .constructor() - .property("type", &ShopItem::type) - .property("item", &ShopItem::item) - .property("jokerData", &ShopItem::jokerData); - class_("WeightedItem") - .constructor() - .property("item", &WeightedItem::item) - .property("weight", &WeightedItem::weight); - class_("Pack") - .constructor() - .property("type", &Pack::type) - .property("size", &Pack::size) - .property("choices", &Pack::choices); - class_("Card") - .constructor() - .property("base", &Card::base) - .property("enhancement", &Card::enhancement) - .property("edition", &Card::edition) - .property("seal", &Card::seal); - constant("ENHANCEMENTS", &ENHANCEMENTS); - constant("CARDS", &CARDS); - constant("SUITS", &SUITS); - constant("RANKS", &RANKS); - constant("TAROTS", &TAROTS); - constant("PLANETS", &PLANETS); - constant("COMMON_JOKERS", &COMMON_JOKERS); - constant("UNCOMMON_JOKERS", &UNCOMMON_JOKERS); - constant("RARE_JOKERS", &RARE_JOKERS); - constant("LEGENDARY_JOKERS", &LEGENDARY_JOKERS); - constant("VOUCHERS", &VOUCHERS); - constant("SPECTRALS", &SPECTRALS); - constant("TAGS", &TAGS); - constant("BOSSES", &BOSSES); - - // util.hpp - function("pseudohash", &pseudohash) class_("LuaRandom") - .constructor<>() - .constructor() - .function("random", &LuaRandom::random); -} -#endif diff --git a/immolate/instance.hpp b/immolate/instance.hpp deleted file mode 100644 index 4489a81..0000000 --- a/immolate/instance.hpp +++ /dev/null @@ -1,135 +0,0 @@ -#include "items.hpp" -#include "seed.hpp" -#include "util.hpp" -#include -#include -#pragma once - -struct Cache { - std::map nodes; - bool generatedFirstPack = false; -}; - -struct InstParams { - Item deck; - Item stake; - bool showman; - int sixesFactor; - long version; - bool vouchers[32] = {false}; - InstParams() { - deck = Item::Red_Deck; - stake = Item::White_Stake; - showman = false; - sixesFactor = 1; - version = 10103; // 1.0.1c - } - InstParams(Item d, Item s, bool show, long v) { - deck = d; - stake = s; - showman = show; - sixesFactor = 1; - version = v; - } -}; - -struct Instance { - bool locked[(int)Item::ITEMS_END] = {false}; - Seed &seed; - double hashedSeed; - Cache cache; - InstParams params; - LuaRandom rng; - Instance(Seed &s) : seed(s) { - hashedSeed = s.pseudohash(0); - params = InstParams(); - rng = LuaRandom(0); - }; - void reset(Seed &s) { // This is slow, use next() unless necessary - seed = s; - hashedSeed = s.pseudohash(0); - params = InstParams(); - cache.nodes - .clear(); // Somehow `clear` is faster than swapping with empty map - cache.generatedFirstPack = false; - }; - void next() { - seed.next(); - hashedSeed = seed.pseudohash(0); - params = InstParams(); - cache.nodes.clear(); - cache.generatedFirstPack = false; - } - double get_node(std::string ID) { - if (cache.nodes.count(ID) == 0) { - cache.nodes[ID] = pseudohash_from(ID, seed.pseudohash(ID.length())); - } - cache.nodes[ID] = - round13(fract(cache.nodes[ID] * 1.72431234 + 2.134453429141)); - return (cache.nodes[ID] + hashedSeed) / 2; - } - double random(std::string ID) { - rng = LuaRandom(get_node(ID)); - return rng.random(); - } - int randint(std::string ID, int min, int max) { - rng = LuaRandom(get_node(ID)); - return rng.randint(min, max); - } - template - Item randchoice(std::string ID, const std::array &items) { - rng = LuaRandom(get_node(ID)); - Item item = items[rng.randint(0, items.size() - 1)]; - if ((params.showman == false && isLocked(item)) || item == Item::RETRY) { - int resample = 2; - while (true) { - rng = LuaRandom(get_node(ID + "_resample" + anteToString(resample))); - Item item = items[rng.randint(0, items.size() - 1)]; - resample++; - if ((item != Item::RETRY && !isLocked(item)) || resample > 1000) - return item; - } - } - return item; - } - template - Item randweightedchoice(std::string ID, - const std::array &items) { - rng = LuaRandom(get_node(ID)); - double poll = rng.random() * items[0].weight; - int idx = 1; - double weight = 0; - while (weight < poll) { - weight += items[idx].weight; - idx++; - } - return items[idx - 1].item; - } - - // Functions defined in functions.hpp - void lock(Item item); - void unlock(Item item); - bool isLocked(Item item); - void initLocks(int ante, bool freshProfile, bool freshRun); - void initUnlocks(int ante, bool freshProfile); - Item nextTarot(std::string source, int ante, bool soulable); - Item nextPlanet(std::string source, int ante, bool soulable); - Item nextSpectral(std::string source, int ante, bool soulable); - JokerData nextJoker(std::string source, int ante, bool hasStickers); - ShopInstance getShopInstance(); - ShopItem nextShopItem(int ante); - Item nextPack(int ante); - std::vector nextArcanaPack(int size, int ante); - std::vector nextCelestialPack(int size, int ante); - std::vector nextSpectralPack(int size, int ante); - std::vector nextBuffoonPack(int size, int ante); - std::vector nextStandardPack(int size, int ante); - Card nextStandardCard(int ante); - bool isVoucherActive(Item voucher); - void activateVoucher(Item voucher); - Item nextVoucher(int ante); - void setDeck(Item deck); - void setStake(Item stake); - Item nextTag(int ante); - Item nextBoss(int ante); -}; \ No newline at end of file diff --git a/immolate/items.cpp b/immolate/items.cpp deleted file mode 100644 index eb9a04e..0000000 --- a/immolate/items.cpp +++ /dev/null @@ -1,356 +0,0 @@ -// #include "items.hpp" - -// std::vector ENHANCEMENTS = { -// Item::Bonus_Card, Item::Mult_Card, Item::Wild_Card, Item::Glass_Card, -// Item::Steel_Card, Item::Stone_Card, Item::Gold_Card, Item::Lucky_Card}; - -// std::vector CARDS = { -// Item::C_2, Item::C_3, Item::C_4, Item::C_5, Item::C_6, Item::C_7, -// Item::C_8, Item::C_9, Item::C_A, Item::C_J, Item::C_K, Item::C_Q, -// Item::C_T, Item::D_2, Item::D_3, Item::D_4, Item::D_5, Item::D_6, -// Item::D_7, Item::D_8, Item::D_9, Item::D_A, Item::D_J, Item::D_K, -// Item::D_Q, Item::D_T, Item::H_2, Item::H_3, Item::H_4, Item::H_5, -// Item::H_6, Item::H_7, Item::H_8, Item::H_9, Item::H_A, Item::H_J, -// Item::H_K, Item::H_Q, Item::H_T, Item::S_2, Item::S_3, Item::S_4, -// Item::S_5, Item::S_6, Item::S_7, Item::S_8, Item::S_9, Item::S_A, -// Item::S_J, Item::S_K, Item::S_Q, Item::S_T}; - -// std::vector SUITS = {Item::Spades, Item::Hearts, Item::Clubs, -// Item::Diamonds}; - -// std::vector RANKS = {Item::_2, Item::_3, Item::_4, Item::_5, -// Item::_6, Item::_7, Item::_8, Item::_9, -// Item::_10, Item::Jack, Item::Queen, Item::King, -// Item::Ace}; - -// std::vector PACKS = { -// WeightedItem(Item::RETRY, 22.42), // total -// WeightedItem(Item::Arcana_Pack, 4), -// WeightedItem(Item::Jumbo_Arcana_Pack, 2), -// WeightedItem(Item::Mega_Arcana_Pack, 0.5), -// WeightedItem(Item::Celestial_Pack, 4), -// WeightedItem(Item::Jumbo_Celestial_Pack, 2), -// WeightedItem(Item::Mega_Celestial_Pack, 0.5), -// WeightedItem(Item::Standard_Pack, 4), -// WeightedItem(Item::Jumbo_Standard_Pack, 2), -// WeightedItem(Item::Mega_Standard_Pack, 0.5), -// WeightedItem(Item::Buffoon_Pack, 1.2), -// WeightedItem(Item::Jumbo_Buffoon_Pack, 0.6), -// WeightedItem(Item::Mega_Buffoon_Pack, 0.15), -// WeightedItem(Item::Spectral_Pack, 0.6), -// WeightedItem(Item::Jumbo_Spectral_Pack, 0.3), -// WeightedItem(Item::Mega_Spectral_Pack, 0.07)}; - -// std::vector TAROTS = {Item::The_Fool, -// Item::The_Magician, -// Item::The_High_Priestess, -// Item::The_Empress, -// Item::The_Emperor, -// Item::The_Hierophant, -// Item::The_Lovers, -// Item::The_Chariot, -// Item::Justice, -// Item::The_Hermit, -// Item::The_Wheel_of_Fortune, -// Item::Strength, -// Item::The_Hanged_Man, -// Item::Death, -// Item::Temperance, -// Item::The_Devil, -// Item::The_Tower, -// Item::The_Star, -// Item::The_Moon, -// Item::The_Sun, -// Item::Judgement, -// Item::The_World}; - -// std::vector PLANETS = {Item::Mercury, Item::Venus, Item::Earth, -// Item::Mars, Item::Jupiter, Item::Saturn, -// Item::Uranus, Item::Neptune, Item::Pluto, -// Item::Planet_X, Item::Ceres, Item::Eris}; - -// std::vector COMMON_JOKERS_100 = {Item::Joker, -// Item::Greedy_Joker, -// Item::Lusty_Joker, -// Item::Wrathful_Joker, -// Item::Gluttonous_Joker, -// Item::Jolly_Joker, -// Item::Zany_Joker, -// Item::Mad_Joker, -// Item::Crazy_Joker, -// Item::Droll_Joker, -// Item::Sly_Joker, -// Item::Wily_Joker, -// Item::Clever_Joker, -// Item::Devious_Joker, -// Item::Crafty_Joker, -// Item::Half_Joker, -// Item::Credit_Card, -// Item::Banner, -// Item::Mystic_Summit, -// Item::_8_Ball, -// Item::Misprint, -// Item::Raised_Fist, -// Item::Chaos_the_Clown, -// Item::Scary_Face, -// Item::Abstract_Joker, -// Item::Delayed_Gratification, -// Item::Gros_Michel, -// Item::Even_Steven, -// Item::Odd_Todd, -// Item::Scholar, -// Item::Business_Card, -// Item::Supernova, -// Item::Ride_the_Bus, -// Item::Egg, -// Item::Runner, -// Item::Ice_Cream, -// Item::Splash, -// Item::Blue_Joker, -// Item::Faceless_Joker, -// Item::Green_Joker, -// Item::Superposition, -// Item::To_Do_List, -// Item::Cavendish, -// Item::Red_Card, -// Item::Square_Joker, -// Item::Riff_raff, -// Item::Photograph, -// Item::Mail_In_Rebate, -// Item::Hallucination, -// Item::Fortune_Teller, -// Item::Juggler, -// Item::Drunkard, -// Item::Golden_Joker, -// Item::Popcorn, -// Item::Walkie_Talkie, -// Item::Smiley_Face, -// Item::Golden_Ticket, -// Item::Swashbuckler, -// Item::Hanging_Chad, -// Item::Shoot_the_Moon}; - -// std::vector COMMON_JOKERS = { -// Item::Joker, -// Item::Greedy_Joker, -// Item::Lusty_Joker, -// Item::Wrathful_Joker, -// Item::Gluttonous_Joker, -// Item::Jolly_Joker, -// Item::Zany_Joker, -// Item::Mad_Joker, -// Item::Crazy_Joker, -// Item::Droll_Joker, -// Item::Sly_Joker, -// Item::Wily_Joker, -// Item::Clever_Joker, -// Item::Devious_Joker, -// Item::Crafty_Joker, -// Item::Half_Joker, -// Item::Credit_Card, -// Item::Banner, -// Item::Mystic_Summit, -// Item::_8_Ball, -// Item::Misprint, -// Item::Raised_Fist, -// Item::Chaos_the_Clown, -// Item::Scary_Face, -// Item::Abstract_Joker, -// Item::Delayed_Gratification, -// Item::Gros_Michel, -// Item::Even_Steven, -// Item::Odd_Todd, -// Item::Scholar, -// Item::Business_Card, -// Item::Supernova, -// Item::Ride_the_Bus, -// Item::Egg, -// Item::Runner, -// Item::Ice_Cream, -// Item::Splash, -// Item::Blue_Joker, -// Item::Faceless_Joker, -// Item::Green_Joker, -// Item::Superposition, -// Item::To_Do_List, -// Item::Cavendish, -// Item::Red_Card, -// Item::Square_Joker, -// Item::Riff_raff, -// Item::Photograph, -// Item::Reserved_Parking, -// Item::Mail_In_Rebate, -// Item::Hallucination, -// Item::Fortune_Teller, -// Item::Juggler, -// Item::Drunkard, -// Item::Golden_Joker, -// Item::Popcorn, -// Item::Walkie_Talkie, -// Item::Smiley_Face, -// Item::Golden_Ticket, -// Item::Swashbuckler, -// Item::Hanging_Chad, -// Item::Shoot_the_Moon, -// }; - -// std::vector UNCOMMON_JOKERS_100 = { -// Item::Joker_Stencil, Item::Four_Fingers, -// Item::Mime, Item::Ceremonial_Dagger, -// Item::Marble_Joker, Item::Loyalty_Card, -// Item::Dusk, Item::Fibonacci, -// Item::Steel_Joker, Item::Hack, -// Item::Pareidolia, Item::Space_Joker, -// Item::Burglar, Item::Blackboard, -// Item::Constellation, Item::Hiker, -// Item::Card_Sharp, Item::Madness, -// Item::Vampire, Item::Shortcut, -// Item::Hologram, Item::Vagabond, -// Item::Cloud_9, Item::Rocket, -// Item::Midas_Mask, Item::Luchador, -// Item::Gift_Card, Item::Turtle_Bean, -// Item::Erosion, Item::Reserved_Parking, -// Item::To_the_Moon, Item::Stone_Joker, -// Item::Lucky_Cat, Item::Bull, -// Item::Diet_Cola, Item::Trading_Card, -// Item::Flash_Card, Item::Spare_Trousers, -// Item::Ramen, Item::Seltzer, -// Item::Castle, Item::Mr_Bones, -// Item::Acrobat, Item::Sock_and_Buskin, -// Item::Troubadour, Item::Certificate, -// Item::Smeared_Joker, Item::Throwback, -// Item::Rough_Gem, Item::Bloodstone, -// Item::Arrowhead, Item::Onyx_Agate, -// Item::Glass_Joker, Item::Showman, -// Item::Flower_Pot, Item::Merry_Andy, -// Item::Oops_All_6s, Item::The_Idol, -// Item::Seeing_Double, Item::Matador, -// Item::Stuntman, Item::Satellite, -// Item::Cartomancer, Item::Astronomer, -// Item::Burnt_Joker, Item::Bootstraps}; - -// std::vector UNCOMMON_JOKERS = { -// Item::Joker_Stencil, Item::Four_Fingers, -// Item::Mime, Item::Ceremonial_Dagger, -// Item::Marble_Joker, Item::Loyalty_Card, -// Item::Dusk, Item::Fibonacci, -// Item::Steel_Joker, Item::Hack, -// Item::Pareidolia, Item::Space_Joker, -// Item::Burglar, Item::Blackboard, -// Item::Sixth_Sense, Item::Constellation, -// Item::Hiker, Item::Card_Sharp, -// Item::Madness, Item::Seance, -// Item::Vampire, Item::Shortcut, -// Item::Hologram, Item::Cloud_9, -// Item::Rocket, Item::Midas_Mask, -// Item::Luchador, Item::Gift_Card, -// Item::Turtle_Bean, Item::Erosion, -// Item::To_the_Moon, Item::Stone_Joker, -// Item::Lucky_Cat, Item::Bull, -// Item::Diet_Cola, Item::Trading_Card, -// Item::Flash_Card, Item::Spare_Trousers, -// Item::Ramen, Item::Seltzer, -// Item::Castle, Item::Mr_Bones, -// Item::Acrobat, Item::Sock_and_Buskin, -// Item::Troubadour, Item::Certificate, -// Item::Smeared_Joker, Item::Throwback, -// Item::Rough_Gem, Item::Bloodstone, -// Item::Arrowhead, Item::Onyx_Agate, -// Item::Glass_Joker, Item::Showman, -// Item::Flower_Pot, Item::Merry_Andy, -// Item::Oops_All_6s, Item::The_Idol, -// Item::Seeing_Double, Item::Matador, -// Item::Satellite, Item::Cartomancer, -// Item::Astronomer, Item::Bootstraps, -// }; - -// std::vector RARE_JOKERS_100 = {Item::DNA, -// Item::Sixth_Sense, -// Item::Seance, -// Item::Baron, -// Item::Obelisk, -// Item::Baseball_Card, -// Item::Ancient_Joker, -// Item::Campfire, -// Item::Blueprint, -// Item::Wee_Joker, -// Item::Hit_the_Road, -// Item::The_Duo, -// Item::The_Trio, -// Item::The_Family, -// Item::The_Order, -// Item::The_Tribe, -// Item::Invisible_Joker, -// Item::Brainstorm, -// Item::Drivers_License}; - -// std::vector RARE_JOKERS = { -// Item::DNA, -// Item::Vagabond, -// Item::Baron, -// Item::Obelisk, -// Item::Baseball_Card, -// Item::Ancient_Joker, -// Item::Campfire, -// Item::Blueprint, -// Item::Wee_Joker, -// Item::Hit_the_Road, -// Item::The_Duo, -// Item::The_Trio, -// Item::The_Family, -// Item::The_Order, -// Item::The_Tribe, -// Item::Stuntman, -// Item::Invisible_Joker, -// Item::Brainstorm, -// Item::Drivers_License, -// Item::Burnt_Joker, -// }; - -// std::vector LEGENDARY_JOKERS = {Item::Canio, Item::Triboulet, -// Item::Yorick, Item::Chicot, -// Item::Perkeo}; - -// std::vector VOUCHERS = { -// Item::Overstock, Item::Overstock_Plus, Item::Clearance_Sale, -// Item::Liquidation, Item::Hone, Item::Glow_Up, -// Item::Reroll_Surplus, Item::Reroll_Glut, Item::Crystal_Ball, -// Item::Omen_Globe, Item::Telescope, Item::Observatory, -// Item::Grabber, Item::Nacho_Tong, Item::Wasteful, -// Item::Recyclomancy, Item::Tarot_Merchant, Item::Tarot_Tycoon, -// Item::Planet_Merchant, Item::Planet_Tycoon, Item::Seed_Money, -// Item::Money_Tree, Item::Blank, Item::Antimatter, -// Item::Magic_Trick, Item::Illusion, Item::Hieroglyph, -// Item::Petroglyph, Item::Directors_Cut, Item::Retcon, -// Item::Paint_Brush, Item::Palette}; - -// std::vector SPECTRALS = { -// Item::Familiar, Item::Grim, Item::Incantation, Item::Talisman, -// Item::Aura, Item::Wraith, Item::Sigil, Item::Ouija, -// Item::Ectoplasm, Item::Immolate, Item::Ankh, Item::Deja_Vu, -// Item::Hex, Item::Trance, Item::Medium, Item::Cryptid, -// Item::RETRY, // Soul -// Item::RETRY // Black_Hole -// }; - -// std::vector TAGS = { -// Item::Uncommon_Tag, Item::Rare_Tag, Item::Negative_Tag, -// Item::Foil_Tag, Item::Holographic_Tag, Item::Polychrome_Tag, -// Item::Investment_Tag, Item::Voucher_Tag, Item::Boss_Tag, -// Item::Standard_Tag, Item::Charm_Tag, Item::Meteor_Tag, -// Item::Buffoon_Tag, Item::Handy_Tag, Item::Garbage_Tag, -// Item::Ethereal_Tag, Item::Coupon_Tag, Item::Double_Tag, -// Item::Juggle_Tag, Item::D6_Tag, Item::Top_up_Tag, -// Item::Speed_Tag, Item::Orbital_Tag, Item::Economy_Tag}; - -// std::vector BOSSES = { -// Item::The_Arm, Item::The_Club, Item::The_Eye, -// Item::Amber_Acorn, Item::Cerulean_Bell, Item::Crimson_Heart, -// Item::Verdant_Leaf, Item::Violet_Vessel, Item::The_Fish, -// Item::The_Flint, Item::The_Goad, Item::The_Head, -// Item::The_Hook, Item::The_House, Item::The_Manacle, -// Item::The_Mark, Item::The_Mouth, Item::The_Needle, -// Item::The_Ox, Item::The_Pillar, Item::The_Plant, -// Item::The_Psychic, Item::The_Serpent, Item::The_Tooth, -// Item::The_Wall, Item::The_Water, Item::The_Wheel, -// Item::The_Window}; diff --git a/immolate/items.hpp b/immolate/items.hpp deleted file mode 100644 index ded376d..0000000 --- a/immolate/items.hpp +++ /dev/null @@ -1,3578 +0,0 @@ -#ifndef ITEMS_HPP -#define ITEMS_HPP - -#include -#include -#include -#include -#include - -enum class Item { - RETRY, - - // Jokers - J_BEGIN, - - J_C_BEGIN, - Joker, - Greedy_Joker, - Lusty_Joker, - Wrathful_Joker, - Gluttonous_Joker, - Jolly_Joker, - Zany_Joker, - Mad_Joker, - Crazy_Joker, - Droll_Joker, - Sly_Joker, - Wily_Joker, - Clever_Joker, - Devious_Joker, - Crafty_Joker, - Half_Joker, - Credit_Card, - Banner, - Mystic_Summit, - _8_Ball, - Misprint, - Raised_Fist, - Chaos_the_Clown, - Scary_Face, - Abstract_Joker, - Delayed_Gratification, - Gros_Michel, - Even_Steven, - Odd_Todd, - Scholar, - Business_Card, - Supernova, - Ride_the_Bus, - Egg, - Runner, - Ice_Cream, - Splash, - Blue_Joker, - Faceless_Joker, - Green_Joker, - Superposition, - To_Do_List, - Cavendish, - Red_Card, - Square_Joker, - Riff_raff, - Photograph, - Reserved_Parking, - Mail_In_Rebate, - Hallucination, - Fortune_Teller, - Juggler, - Drunkard, - Golden_Joker, - Popcorn, - Walkie_Talkie, - Smiley_Face, - Golden_Ticket, - Swashbuckler, - Hanging_Chad, - Shoot_the_Moon, - J_C_END, - - J_U_BEGIN, - Joker_Stencil, - Four_Fingers, - Mime, - Ceremonial_Dagger, - Marble_Joker, - Loyalty_Card, - Dusk, - Fibonacci, - Steel_Joker, - Hack, - Pareidolia, - Space_Joker, - Burglar, - Blackboard, - Sixth_Sense, - Constellation, - Hiker, - Card_Sharp, - Madness, - Seance, - Shortcut, - Hologram, - Cloud_9, - Rocket, - Midas_Mask, - Luchador, - Gift_Card, - Turtle_Bean, - Erosion, - To_the_Moon, - Stone_Joker, - Lucky_Cat, - Bull, - Diet_Cola, - Trading_Card, - Flash_Card, - Spare_Trousers, - Ramen, - Seltzer, - Castle, - Mr_Bones, - Acrobat, - Sock_and_Buskin, - Troubadour, - Certificate, - Smeared_Joker, - Throwback, - Rough_Gem, - Bloodstone, - Arrowhead, - Onyx_Agate, - Glass_Joker, - Showman, - Flower_Pot, - Merry_Andy, - Oops_All_6s, - The_Idol, - Seeing_Double, - Matador, - Stuntman, - Satellite, - Cartomancer, - Astronomer, - Bootstraps, - J_U_END, - - J_R_BEGIN, - DNA, - Vampire, - Vagabond, - Baron, - Obelisk, - Baseball_Card, - Ancient_Joker, - Campfire, - Blueprint, - Wee_Joker, - Hit_the_Road, - The_Duo, - The_Trio, - The_Family, - The_Order, - The_Tribe, - Invisible_Joker, - Brainstorm, - Drivers_License, - Burnt_Joker, - J_R_END, - - J_L_BEGIN, - Canio, - Triboulet, - Yorick, - Chicot, - Perkeo, - J_L_END, - - J_END, - - // Vouchers - V_BEGIN, - Overstock, - Overstock_Plus, - Clearance_Sale, - Liquidation, - Hone, - Glow_Up, - Reroll_Surplus, - Reroll_Glut, - Crystal_Ball, - Omen_Globe, - Telescope, - Observatory, - Grabber, - Nacho_Tong, - Wasteful, - Recyclomancy, - Tarot_Merchant, - Tarot_Tycoon, - Planet_Merchant, - Planet_Tycoon, - Seed_Money, - Money_Tree, - Blank, - Antimatter, - Magic_Trick, - Illusion, - Hieroglyph, - Petroglyph, - Directors_Cut, - Retcon, - Paint_Brush, - Palette, - V_END, - - // Tarots - T_BEGIN, - The_Fool, - The_Magician, - The_High_Priestess, - The_Empress, - The_Emperor, - The_Hierophant, - The_Lovers, - The_Chariot, - Justice, - The_Hermit, - The_Wheel_of_Fortune, - Strength, - The_Hanged_Man, - Death, - Temperance, - The_Devil, - The_Tower, - The_Star, - The_Moon, - The_Sun, - Judgement, - The_World, - T_END, - - // Planets - P_BEGIN, - Mercury, - Venus, - Earth, - Mars, - Jupiter, - Saturn, - Uranus, - Neptune, - Pluto, - Planet_X, - Ceres, - Eris, - P_END, - - // Hands - H_BEGIN, - Pair, - Three_of_a_Kind, - Full_House, - Four_of_a_Kind, - Flush, - Straight, - Two_Pair, - Straight_Flush, - High_Card, - Five_of_a_Kind, - Flush_House, - Flush_Five, - H_END, - - // Spectrals - S_BEGIN, - Familiar, - Grim, - Incantation, - Talisman, - Aura, - Wraith, - Sigil, - Ouija, - Ectoplasm, - Immolate, - Ankh, - Deja_Vu, - Hex, - Trance, - Medium, - Cryptid, - The_Soul, - Black_Hole, - S_END, - - // Enhancements - ENHANCEMENT_BEGIN, - No_Enhancement, - Bonus_Card, - Mult_Card, - Wild_Card, - Glass_Card, - Steel_Card, - Stone_Card, - Gold_Card, - Lucky_Card, - ENHANCEMENT_END, - - // Seals - SEAL_BEGIN, - No_Seal, - Gold_Seal, - Red_Seal, - Blue_Seal, - Purple_Seal, - SEAL_END, - - // Editions - E_BEGIN, - No_Edition, - Foil, - Holographic, - Polychrome, - Negative, - E_END, - - // Booster Packs - PACK_BEGIN, - Arcana_Pack, - Jumbo_Arcana_Pack, - Mega_Arcana_Pack, - Celestial_Pack, - Jumbo_Celestial_Pack, - Mega_Celestial_Pack, - Standard_Pack, - Jumbo_Standard_Pack, - Mega_Standard_Pack, - Buffoon_Pack, - Jumbo_Buffoon_Pack, - Mega_Buffoon_Pack, - Spectral_Pack, - Jumbo_Spectral_Pack, - Mega_Spectral_Pack, - PACK_END, - - // Tags - TAG_BEGIN, - Uncommon_Tag, - Rare_Tag, - Negative_Tag, - Foil_Tag, - Holographic_Tag, - Polychrome_Tag, - Investment_Tag, - Voucher_Tag, - Boss_Tag, - Standard_Tag, - Charm_Tag, - Meteor_Tag, - Buffoon_Tag, - Handy_Tag, - Garbage_Tag, - Ethereal_Tag, - Coupon_Tag, - Double_Tag, - Juggle_Tag, - D6_Tag, - Top_up_Tag, - Speed_Tag, - Orbital_Tag, - Economy_Tag, - TAG_END, - - // Blinds - B_BEGIN, - Small_Blind, - Big_Blind, - The_Hook, - The_Ox, - The_House, - The_Wall, - The_Wheel, - The_Arm, - The_Club, - The_Fish, - The_Psychic, - The_Goad, - The_Water, - The_Window, - The_Manacle, - The_Eye, - The_Mouth, - The_Plant, - The_Serpent, - The_Pillar, - The_Needle, - The_Head, - The_Tooth, - The_Flint, - The_Mark, - B_F_BEGIN, - Amber_Acorn, - Verdant_Leaf, - Violet_Vessel, - Crimson_Heart, - Cerulean_Bell, - B_F_END, - B_END, - - // Suits - SUIT_BEGIN, - Hearts, - Clubs, - Diamonds, - Spades, - SUIT_END, - - // Ranks - RANK_BEGIN, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - Jack, - Queen, - King, - Ace, - RANK_END, - - // Cards - C_BEGIN, - C_2, - C_3, - C_4, - C_5, - C_6, - C_7, - C_8, - C_9, - C_A, - C_J, - C_K, - C_Q, - C_T, - D_2, - D_3, - D_4, - D_5, - D_6, - D_7, - D_8, - D_9, - D_A, - D_J, - D_K, - D_Q, - D_T, - H_2, - H_3, - H_4, - H_5, - H_6, - H_7, - H_8, - H_9, - H_A, - H_J, - H_K, - H_Q, - H_T, - S_2, - S_3, - S_4, - S_5, - S_6, - S_7, - S_8, - S_9, - S_A, - S_J, - S_K, - S_Q, - S_T, - C_END, - - // Decks - D_BEGIN, - Red_Deck, - Blue_Deck, - Yellow_Deck, - Green_Deck, - Black_Deck, - Magic_Deck, - Nebula_Deck, - Ghost_Deck, - Abandoned_Deck, - Checkered_Deck, - Zodiac_Deck, - Painted_Deck, - Anaglyph_Deck, - Plasma_Deck, - Erratic_Deck, - Challenge_Deck, - D_END, - - // Challenges - CHAL_BEGIN, - The_Omelette, - _15_Minute_City, - Rich_get_Richer, - On_a_Knifes_Edge, - X_ray_Vision, - Mad_World, - Luxury_Tax, - Non_Perishable, - Medusa, - Double_or_Nothing, - Typecast, - Inflation, - Bram_Poker, - Fragile, - Monolith, - Blast_Off, - Five_Card_Draw, - Golden_Needle, - Cruelty, - Jokerless, - CHAL_END, - - // Stakes - STAKE_BEGIN, - White_Stake, - Red_Stake, - Green_Stake, - Black_Stake, - Blue_Stake, - Purple_Stake, - Orange_Stake, - Gold_Stake, - STAKE_END, - - RARITY_BEGIN, - Common, - Uncommon, - Rare, - Legendary, - RARITY_END, - - TYPE_BEGIN, - T_Joker, - T_Tarot, - T_Planet, - T_Spectral, - T_Playing_Card, - TYPE_END, - - ITEMS_END -}; -inline std::string itemToString(Item i) { - switch (i) { - case Item::RETRY: - return "RETRY"; - case Item::J_BEGIN: - return "J BEGIN"; - case Item::J_C_BEGIN: - return "J C BEGIN"; - case Item::Joker: - return "Joker"; - case Item::Greedy_Joker: - return "Greedy Joker"; - case Item::Lusty_Joker: - return "Lusty Joker"; - case Item::Wrathful_Joker: - return "Wrathful Joker"; - case Item::Gluttonous_Joker: - return "Gluttonous Joker"; - case Item::Jolly_Joker: - return "Jolly Joker"; - case Item::Zany_Joker: - return "Zany Joker"; - case Item::Mad_Joker: - return "Mad Joker"; - case Item::Crazy_Joker: - return "Crazy Joker"; - case Item::Droll_Joker: - return "Droll Joker"; - case Item::Sly_Joker: - return "Sly Joker"; - case Item::Wily_Joker: - return "Wily Joker"; - case Item::Clever_Joker: - return "Clever Joker"; - case Item::Devious_Joker: - return "Devious Joker"; - case Item::Crafty_Joker: - return "Crafty Joker"; - case Item::Half_Joker: - return "Half Joker"; - case Item::Credit_Card: - return "Credit Card"; - case Item::Banner: - return "Banner"; - case Item::Mystic_Summit: - return "Mystic Summit"; - case Item::_8_Ball: - return "8 Ball"; - case Item::Misprint: - return "Misprint"; - case Item::Raised_Fist: - return "Raised Fist"; - case Item::Chaos_the_Clown: - return "Chaos the Clown"; - case Item::Scary_Face: - return "Scary Face"; - case Item::Abstract_Joker: - return "Abstract Joker"; - case Item::Delayed_Gratification: - return "Delayed Gratification"; - case Item::Gros_Michel: - return "Gros Michel"; - case Item::Even_Steven: - return "Even Steven"; - case Item::Odd_Todd: - return "Odd Todd"; - case Item::Scholar: - return "Scholar"; - case Item::Business_Card: - return "Business Card"; - case Item::Supernova: - return "Supernova"; - case Item::Ride_the_Bus: - return "Ride the Bus"; - case Item::Egg: - return "Egg"; - case Item::Runner: - return "Runner"; - case Item::Ice_Cream: - return "Ice Cream"; - case Item::Splash: - return "Splash"; - case Item::Blue_Joker: - return "Blue Joker"; - case Item::Faceless_Joker: - return "Faceless Joker"; - case Item::Green_Joker: - return "Green Joker"; - case Item::Superposition: - return "Superposition"; - case Item::To_Do_List: - return "To Do List"; - case Item::Cavendish: - return "Cavendish"; - case Item::Red_Card: - return "Red Card"; - case Item::Square_Joker: - return "Square Joker"; - case Item::Riff_raff: - return "Riff-raff"; - case Item::Photograph: - return "Photograph"; - case Item::Reserved_Parking: - return "Reserved Parking"; - case Item::Mail_In_Rebate: - return "Mail-In Rebate"; - case Item::Hallucination: - return "Hallucination"; - case Item::Fortune_Teller: - return "Fortune Teller"; - case Item::Juggler: - return "Juggler"; - case Item::Drunkard: - return "Drunkard"; - case Item::Golden_Joker: - return "Golden Joker"; - case Item::Popcorn: - return "Popcorn"; - case Item::Walkie_Talkie: - return "Walkie Talkie"; - case Item::Smiley_Face: - return "Smiley Face"; - case Item::Golden_Ticket: - return "Golden Ticket"; - case Item::Swashbuckler: - return "Swashbuckler"; - case Item::Hanging_Chad: - return "Hanging Chad"; - case Item::Shoot_the_Moon: - return "Shoot the Moon"; - case Item::J_C_END: - return "J C END"; - case Item::J_U_BEGIN: - return "J U BEGIN"; - case Item::Joker_Stencil: - return "Joker Stencil"; - case Item::Four_Fingers: - return "Four Fingers"; - case Item::Mime: - return "Mime"; - case Item::Ceremonial_Dagger: - return "Ceremonial Dagger"; - case Item::Marble_Joker: - return "Marble Joker"; - case Item::Loyalty_Card: - return "Loyalty Card"; - case Item::Dusk: - return "Dusk"; - case Item::Fibonacci: - return "Fibonacci"; - case Item::Steel_Joker: - return "Steel Joker"; - case Item::Hack: - return "Hack"; - case Item::Pareidolia: - return "Pareidolia"; - case Item::Space_Joker: - return "Space Joker"; - case Item::Burglar: - return "Burglar"; - case Item::Blackboard: - return "Blackboard"; - case Item::Sixth_Sense: - return "Sixth Sense"; - case Item::Constellation: - return "Constellation"; - case Item::Hiker: - return "Hiker"; - case Item::Card_Sharp: - return "Card Sharp"; - case Item::Madness: - return "Madness"; - case Item::Seance: - return "SΘance"; - case Item::Shortcut: - return "Shortcut"; - case Item::Hologram: - return "Hologram"; - case Item::Cloud_9: - return "Cloud 9"; - case Item::Rocket: - return "Rocket"; - case Item::Midas_Mask: - return "Midas Mask"; - case Item::Luchador: - return "Luchador"; - case Item::Gift_Card: - return "Gift Card"; - case Item::Turtle_Bean: - return "Turtle Bean"; - case Item::Erosion: - return "Erosion"; - case Item::To_the_Moon: - return "To the Moon"; - case Item::Stone_Joker: - return "Stone Joker"; - case Item::Lucky_Cat: - return "Lucky Cat"; - case Item::Bull: - return "Bull"; - case Item::Diet_Cola: - return "Diet Cola"; - case Item::Trading_Card: - return "Trading Card"; - case Item::Flash_Card: - return "Flash Card"; - case Item::Spare_Trousers: - return "Spare Trousers"; - case Item::Ramen: - return "Ramen"; - case Item::Seltzer: - return "Seltzer"; - case Item::Castle: - return "Castle"; - case Item::Mr_Bones: - return "Mr. Bones"; - case Item::Acrobat: - return "Acrobat"; - case Item::Sock_and_Buskin: - return "Sock and Buskin"; - case Item::Troubadour: - return "Troubadour"; - case Item::Certificate: - return "Certificate"; - case Item::Smeared_Joker: - return "Smeared Joker"; - case Item::Throwback: - return "Throwback"; - case Item::Rough_Gem: - return "Rough Gem"; - case Item::Bloodstone: - return "Bloodstone"; - case Item::Arrowhead: - return "Arrowhead"; - case Item::Onyx_Agate: - return "Onyx Agate"; - case Item::Glass_Joker: - return "Glass Joker"; - case Item::Showman: - return "Showman"; - case Item::Flower_Pot: - return "Flower Pot"; - case Item::Merry_Andy: - return "Merry Andy"; - case Item::Oops_All_6s: - return "Oops! All 6s"; - case Item::The_Idol: - return "The Idol"; - case Item::Seeing_Double: - return "Seeing Double"; - case Item::Matador: - return "Matador"; - case Item::Stuntman: - return "Stuntman"; - case Item::Satellite: - return "Satellite"; - case Item::Cartomancer: - return "Cartomancer"; - case Item::Astronomer: - return "Astronomer"; - case Item::Bootstraps: - return "Bootstraps"; - case Item::J_U_END: - return "J U END"; - case Item::J_R_BEGIN: - return "J R BEGIN"; - case Item::DNA: - return "DNA"; - case Item::Vampire: - return "Vampire"; - case Item::Vagabond: - return "Vagabond"; - case Item::Baron: - return "Baron"; - case Item::Obelisk: - return "Obelisk"; - case Item::Baseball_Card: - return "Baseball Card"; - case Item::Ancient_Joker: - return "Ancient Joker"; - case Item::Campfire: - return "Campfire"; - case Item::Blueprint: - return "Blueprint"; - case Item::Wee_Joker: - return "Wee Joker"; - case Item::Hit_the_Road: - return "Hit the Road"; - case Item::The_Duo: - return "The Duo"; - case Item::The_Trio: - return "The Trio"; - case Item::The_Family: - return "The Family"; - case Item::The_Order: - return "The Order"; - case Item::The_Tribe: - return "The Tribe"; - case Item::Invisible_Joker: - return "Invisible Joker"; - case Item::Brainstorm: - return "Brainstorm"; - case Item::Drivers_License: - return "Driver's License"; - case Item::Burnt_Joker: - return "Burnt Joker"; - case Item::J_R_END: - return "J R END"; - case Item::J_L_BEGIN: - return "J L BEGIN"; - case Item::Canio: - return "Canio"; - case Item::Triboulet: - return "Triboulet"; - case Item::Yorick: - return "Yorick"; - case Item::Chicot: - return "Chicot"; - case Item::Perkeo: - return "Perkeo"; - case Item::J_L_END: - return "J L END"; - case Item::J_END: - return "J END"; - case Item::V_BEGIN: - return "V BEGIN"; - case Item::Overstock: - return "Overstock"; - case Item::Overstock_Plus: - return "Overstock Plus"; - case Item::Clearance_Sale: - return "Clearance Sale"; - case Item::Liquidation: - return "Liquidation"; - case Item::Hone: - return "Hone"; - case Item::Glow_Up: - return "Glow Up"; - case Item::Reroll_Surplus: - return "Reroll Surplus"; - case Item::Reroll_Glut: - return "Reroll Glut"; - case Item::Crystal_Ball: - return "Crystal Ball"; - case Item::Omen_Globe: - return "Omen Globe"; - case Item::Telescope: - return "Telescope"; - case Item::Observatory: - return "Observatory"; - case Item::Grabber: - return "Grabber"; - case Item::Nacho_Tong: - return "Nacho Tong"; - case Item::Wasteful: - return "Wasteful"; - case Item::Recyclomancy: - return "Recyclomancy"; - case Item::Tarot_Merchant: - return "Tarot Merchant"; - case Item::Tarot_Tycoon: - return "Tarot Tycoon"; - case Item::Planet_Merchant: - return "Planet Merchant"; - case Item::Planet_Tycoon: - return "Planet Tycoon"; - case Item::Seed_Money: - return "Seed Money"; - case Item::Money_Tree: - return "Money Tree"; - case Item::Blank: - return "Blank"; - case Item::Antimatter: - return "Antimatter"; - case Item::Magic_Trick: - return "Magic Trick"; - case Item::Illusion: - return "Illusion"; - case Item::Hieroglyph: - return "Hieroglyph"; - case Item::Petroglyph: - return "Petroglyph"; - case Item::Directors_Cut: - return "Director's Cut"; - case Item::Retcon: - return "Retcon"; - case Item::Paint_Brush: - return "Paint Brush"; - case Item::Palette: - return "Palette"; - case Item::V_END: - return "V END"; - case Item::T_BEGIN: - return "T BEGIN"; - case Item::The_Fool: - return "The Fool"; - case Item::The_Magician: - return "The Magician"; - case Item::The_High_Priestess: - return "The High Priestess"; - case Item::The_Empress: - return "The Empress"; - case Item::The_Emperor: - return "The Emperor"; - case Item::The_Hierophant: - return "The Hierophant"; - case Item::The_Lovers: - return "The Lovers"; - case Item::The_Chariot: - return "The Chariot"; - case Item::Justice: - return "Justice"; - case Item::The_Hermit: - return "The Hermit"; - case Item::The_Wheel_of_Fortune: - return "The Wheel of Fortune"; - case Item::Strength: - return "Strength"; - case Item::The_Hanged_Man: - return "The Hanged Man"; - case Item::Death: - return "Death"; - case Item::Temperance: - return "Temperance"; - case Item::The_Devil: - return "The Devil"; - case Item::The_Tower: - return "The Tower"; - case Item::The_Star: - return "The Star"; - case Item::The_Moon: - return "The Moon"; - case Item::The_Sun: - return "The Sun"; - case Item::Judgement: - return "Judgement"; - case Item::The_World: - return "The World"; - case Item::T_END: - return "T END"; - case Item::P_BEGIN: - return "P BEGIN"; - case Item::Mercury: - return "Mercury"; - case Item::Venus: - return "Venus"; - case Item::Earth: - return "Earth"; - case Item::Mars: - return "Mars"; - case Item::Jupiter: - return "Jupiter"; - case Item::Saturn: - return "Saturn"; - case Item::Uranus: - return "Uranus"; - case Item::Neptune: - return "Neptune"; - case Item::Pluto: - return "Pluto"; - case Item::Planet_X: - return "Planet X"; - case Item::Ceres: - return "Ceres"; - case Item::Eris: - return "Eris"; - case Item::P_END: - return "P END"; - case Item::H_BEGIN: - return "H BEGIN"; - case Item::Pair: - return "Pair"; - case Item::Three_of_a_Kind: - return "Three of a Kind"; - case Item::Full_House: - return "Full House"; - case Item::Four_of_a_Kind: - return "Four of a Kind"; - case Item::Flush: - return "Flush"; - case Item::Straight: - return "Straight"; - case Item::Two_Pair: - return "Two Pair"; - case Item::Straight_Flush: - return "Straight Flush"; - case Item::High_Card: - return "High Card"; - case Item::Five_of_a_Kind: - return "Five of a Kind"; - case Item::Flush_House: - return "Flush House"; - case Item::Flush_Five: - return "Flush Five"; - case Item::H_END: - return "H END"; - case Item::S_BEGIN: - return "S BEGIN"; - case Item::Familiar: - return "Familiar"; - case Item::Grim: - return "Grim"; - case Item::Incantation: - return "Incantation"; - case Item::Talisman: - return "Talisman"; - case Item::Aura: - return "Aura"; - case Item::Wraith: - return "Wraith"; - case Item::Sigil: - return "Sigil"; - case Item::Ouija: - return "Ouija"; - case Item::Ectoplasm: - return "Ectoplasm"; - case Item::Immolate: - return "Immolate"; - case Item::Ankh: - return "Ankh"; - case Item::Deja_Vu: - return "Deja Vu"; - case Item::Hex: - return "Hex"; - case Item::Trance: - return "Trance"; - case Item::Medium: - return "Medium"; - case Item::Cryptid: - return "Cryptid"; - case Item::The_Soul: - return "The Soul"; - case Item::Black_Hole: - return "Black Hole"; - case Item::S_END: - return "S END"; - case Item::ENHANCEMENT_BEGIN: - return "ENHANCEMENT BEGIN"; - case Item::No_Enhancement: - return "No Enhancement"; - case Item::Bonus_Card: - return "Bonus Card"; - case Item::Mult_Card: - return "Mult Card"; - case Item::Wild_Card: - return "Wild Card"; - case Item::Glass_Card: - return "Glass Card"; - case Item::Steel_Card: - return "Steel Card"; - case Item::Stone_Card: - return "Stone Card"; - case Item::Gold_Card: - return "Gold Card"; - case Item::Lucky_Card: - return "Lucky Card"; - case Item::ENHANCEMENT_END: - return "ENHANCEMENT END"; - case Item::SEAL_BEGIN: - return "SEAL BEGIN"; - case Item::No_Seal: - return "No Seal"; - case Item::Gold_Seal: - return "Gold Seal"; - case Item::Red_Seal: - return "Red Seal"; - case Item::Blue_Seal: - return "Blue Seal"; - case Item::Purple_Seal: - return "Purple Seal"; - case Item::SEAL_END: - return "SEAL END"; - case Item::E_BEGIN: - return "E BEGIN"; - case Item::No_Edition: - return "No Edition"; - case Item::Foil: - return "Foil"; - case Item::Holographic: - return "Holographic"; - case Item::Polychrome: - return "Polychrome"; - case Item::Negative: - return "Negative"; - case Item::E_END: - return "E END"; - case Item::PACK_BEGIN: - return "PACK BEGIN"; - case Item::Arcana_Pack: - return "Arcana Pack"; - case Item::Jumbo_Arcana_Pack: - return "Jumbo Arcana Pack"; - case Item::Mega_Arcana_Pack: - return "Mega Arcana Pack"; - case Item::Celestial_Pack: - return "Celestial Pack"; - case Item::Jumbo_Celestial_Pack: - return "Jumbo Celestial Pack"; - case Item::Mega_Celestial_Pack: - return "Mega Celestial Pack"; - case Item::Standard_Pack: - return "Standard Pack"; - case Item::Jumbo_Standard_Pack: - return "Jumbo Standard Pack"; - case Item::Mega_Standard_Pack: - return "Mega Standard Pack"; - case Item::Buffoon_Pack: - return "Buffoon Pack"; - case Item::Jumbo_Buffoon_Pack: - return "Jumbo Buffoon Pack"; - case Item::Mega_Buffoon_Pack: - return "Mega Buffoon Pack"; - case Item::Spectral_Pack: - return "Spectral Pack"; - case Item::Jumbo_Spectral_Pack: - return "Jumbo Spectral Pack"; - case Item::Mega_Spectral_Pack: - return "Mega Spectral Pack"; - case Item::PACK_END: - return "PACK END"; - case Item::TAG_BEGIN: - return "TAG BEGIN"; - case Item::Uncommon_Tag: - return "Uncommon Tag"; - case Item::Rare_Tag: - return "Rare Tag"; - case Item::Negative_Tag: - return "Negative Tag"; - case Item::Foil_Tag: - return "Foil Tag"; - case Item::Holographic_Tag: - return "Holographic Tag"; - case Item::Polychrome_Tag: - return "Polychrome Tag"; - case Item::Investment_Tag: - return "Investment Tag"; - case Item::Voucher_Tag: - return "Voucher Tag"; - case Item::Boss_Tag: - return "Boss Tag"; - case Item::Standard_Tag: - return "Standard Tag"; - case Item::Charm_Tag: - return "Charm Tag"; - case Item::Meteor_Tag: - return "Meteor Tag"; - case Item::Buffoon_Tag: - return "Buffoon Tag"; - case Item::Handy_Tag: - return "Handy Tag"; - case Item::Garbage_Tag: - return "Garbage Tag"; - case Item::Ethereal_Tag: - return "Ethereal Tag"; - case Item::Coupon_Tag: - return "Coupon Tag"; - case Item::Double_Tag: - return "Double Tag"; - case Item::Juggle_Tag: - return "Juggle Tag"; - case Item::D6_Tag: - return "D6 Tag"; - case Item::Top_up_Tag: - return "Top-up Tag"; - case Item::Speed_Tag: - return "Speed Tag"; - case Item::Orbital_Tag: - return "Orbital Tag"; - case Item::Economy_Tag: - return "Economy Tag"; - case Item::TAG_END: - return "TAG END"; - case Item::B_BEGIN: - return "B BEGIN"; - case Item::Small_Blind: - return "Small Blind"; - case Item::Big_Blind: - return "Big Blind"; - case Item::The_Hook: - return "The Hook"; - case Item::The_Ox: - return "The Ox"; - case Item::The_House: - return "The House"; - case Item::The_Wall: - return "The Wall"; - case Item::The_Wheel: - return "The Wheel"; - case Item::The_Arm: - return "The Arm"; - case Item::The_Club: - return "The Club"; - case Item::The_Fish: - return "The Fish"; - case Item::The_Psychic: - return "The Psychic"; - case Item::The_Goad: - return "The Goad"; - case Item::The_Water: - return "The Water"; - case Item::The_Window: - return "The Window"; - case Item::The_Manacle: - return "The Manacle"; - case Item::The_Eye: - return "The Eye"; - case Item::The_Mouth: - return "The Mouth"; - case Item::The_Plant: - return "The Plant"; - case Item::The_Serpent: - return "The Serpent"; - case Item::The_Pillar: - return "The Pillar"; - case Item::The_Needle: - return "The Needle"; - case Item::The_Head: - return "The Head"; - case Item::The_Tooth: - return "The Tooth"; - case Item::The_Flint: - return "The Flint"; - case Item::The_Mark: - return "The Mark"; - case Item::B_F_BEGIN: - return "B F BEGIN"; - case Item::Amber_Acorn: - return "Amber Acorn"; - case Item::Verdant_Leaf: - return "Verdant Leaf"; - case Item::Violet_Vessel: - return "Violet Vessel"; - case Item::Crimson_Heart: - return "Crimson Heart"; - case Item::Cerulean_Bell: - return "Cerulean Bell"; - case Item::B_F_END: - return "B F END"; - case Item::B_END: - return "B END"; - case Item::SUIT_BEGIN: - return "SUIT BEGIN"; - case Item::Hearts: - return "Hearts"; - case Item::Clubs: - return "Clubs"; - case Item::Diamonds: - return "Diamonds"; - case Item::Spades: - return "Spades"; - case Item::SUIT_END: - return "SUIT END"; - case Item::RANK_BEGIN: - return "RANK BEGIN"; - case Item::_2: - return "2"; - case Item::_3: - return "3"; - case Item::_4: - return "4"; - case Item::_5: - return "5"; - case Item::_6: - return "6"; - case Item::_7: - return "7"; - case Item::_8: - return "8"; - case Item::_9: - return "9"; - case Item::_10: - return "10"; - case Item::Jack: - return "Jack"; - case Item::Queen: - return "Queen"; - case Item::King: - return "King"; - case Item::Ace: - return "Ace"; - case Item::RANK_END: - return "RANK END"; - case Item::C_BEGIN: - return "C BEGIN"; - case Item::C_2: - return "C 2"; - case Item::C_3: - return "C 3"; - case Item::C_4: - return "C 4"; - case Item::C_5: - return "C 5"; - case Item::C_6: - return "C 6"; - case Item::C_7: - return "C 7"; - case Item::C_8: - return "C 8"; - case Item::C_9: - return "C 9"; - case Item::C_A: - return "C A"; - case Item::C_J: - return "C J"; - case Item::C_K: - return "C K"; - case Item::C_Q: - return "C Q"; - case Item::C_T: - return "C T"; - case Item::D_2: - return "D 2"; - case Item::D_3: - return "D 3"; - case Item::D_4: - return "D 4"; - case Item::D_5: - return "D 5"; - case Item::D_6: - return "D 6"; - case Item::D_7: - return "D 7"; - case Item::D_8: - return "D 8"; - case Item::D_9: - return "D 9"; - case Item::D_A: - return "D A"; - case Item::D_J: - return "D J"; - case Item::D_K: - return "D K"; - case Item::D_Q: - return "D Q"; - case Item::D_T: - return "D T"; - case Item::H_2: - return "H 2"; - case Item::H_3: - return "H 3"; - case Item::H_4: - return "H 4"; - case Item::H_5: - return "H 5"; - case Item::H_6: - return "H 6"; - case Item::H_7: - return "H 7"; - case Item::H_8: - return "H 8"; - case Item::H_9: - return "H 9"; - case Item::H_A: - return "H A"; - case Item::H_J: - return "H J"; - case Item::H_K: - return "H K"; - case Item::H_Q: - return "H Q"; - case Item::H_T: - return "H T"; - case Item::S_2: - return "S 2"; - case Item::S_3: - return "S 3"; - case Item::S_4: - return "S 4"; - case Item::S_5: - return "S 5"; - case Item::S_6: - return "S 6"; - case Item::S_7: - return "S 7"; - case Item::S_8: - return "S 8"; - case Item::S_9: - return "S 9"; - case Item::S_A: - return "S A"; - case Item::S_J: - return "S J"; - case Item::S_K: - return "S K"; - case Item::S_Q: - return "S Q"; - case Item::S_T: - return "S T"; - case Item::C_END: - return "C END"; - case Item::D_BEGIN: - return "D BEGIN"; - case Item::Red_Deck: - return "Red Deck"; - case Item::Blue_Deck: - return "Blue Deck"; - case Item::Yellow_Deck: - return "Yellow Deck"; - case Item::Green_Deck: - return "Green Deck"; - case Item::Black_Deck: - return "Black Deck"; - case Item::Magic_Deck: - return "Magic Deck"; - case Item::Nebula_Deck: - return "Nebula Deck"; - case Item::Ghost_Deck: - return "Ghost Deck"; - case Item::Abandoned_Deck: - return "Abandoned Deck"; - case Item::Checkered_Deck: - return "Checkered Deck"; - case Item::Zodiac_Deck: - return "Zodiac Deck"; - case Item::Painted_Deck: - return "Painted Deck"; - case Item::Anaglyph_Deck: - return "Anaglyph Deck"; - case Item::Plasma_Deck: - return "Plasma Deck"; - case Item::Erratic_Deck: - return "Erratic Deck"; - case Item::Challenge_Deck: - return "Challenge Deck"; - case Item::D_END: - return "D END"; - case Item::CHAL_BEGIN: - return "CHAL BEGIN"; - case Item::The_Omelette: - return "The Omelette"; - case Item::_15_Minute_City: - return "15 Minute City"; - case Item::Rich_get_Richer: - return "Rich get Richer"; - case Item::On_a_Knifes_Edge: - return "On a Knife's Edge"; - case Item::X_ray_Vision: - return "X-ray Vision"; - case Item::Mad_World: - return "Mad World"; - case Item::Luxury_Tax: - return "Luxury Tax"; - case Item::Non_Perishable: - return "Non-Perishable"; - case Item::Medusa: - return "Medusa"; - case Item::Double_or_Nothing: - return "Double or Nothing"; - case Item::Typecast: - return "Typecast"; - case Item::Inflation: - return "Inflation"; - case Item::Bram_Poker: - return "Bram Poker"; - case Item::Fragile: - return "Fragile"; - case Item::Monolith: - return "Monolith"; - case Item::Blast_Off: - return "Blast Off"; - case Item::Five_Card_Draw: - return "Five-Card Draw"; - case Item::Golden_Needle: - return "Golden Needle"; - case Item::Cruelty: - return "Cruelty"; - case Item::Jokerless: - return "Jokerless"; - case Item::CHAL_END: - return "CHAL END"; - case Item::STAKE_BEGIN: - return "STAKE BEGIN"; - case Item::White_Stake: - return "White Stake"; - case Item::Red_Stake: - return "Red Stake"; - case Item::Green_Stake: - return "Green Stake"; - case Item::Black_Stake: - return "Black Stake"; - case Item::Blue_Stake: - return "Blue Stake"; - case Item::Purple_Stake: - return "Purple Stake"; - case Item::Orange_Stake: - return "Orange Stake"; - case Item::Gold_Stake: - return "Gold Stake"; - case Item::STAKE_END: - return "STAKE END"; - case Item::RARITY_BEGIN: - return "RARITY BEGIN"; - case Item::Common: - return "Common"; - case Item::Uncommon: - return "Uncommon"; - case Item::Rare: - return "Rare"; - case Item::Legendary: - return "Legendary"; - case Item::RARITY_END: - return "RARITY END"; - case Item::TYPE_BEGIN: - return "TYPE BEGIN"; - case Item::T_Joker: - return "T Joker"; - case Item::T_Tarot: - return "T Tarot"; - case Item::T_Planet: - return "T Planet"; - case Item::T_Spectral: - return "T Spectral"; - case Item::T_Playing_Card: - return "T Playing Card"; - case Item::TYPE_END: - return "TYPE END"; - default: - std::cout << "ERROR; stringToItem found no items... contact dev" - << std::endl; - EXIT_FAILURE; - } -} -inline Item stringToItem(std::string i) { - if (i == "RETRY") { - return Item::RETRY; - }; - if (i == "J BEGIN") { - return Item::J_BEGIN; - }; - if (i == "J C BEGIN") { - return Item::J_C_BEGIN; - }; - if (i == "Joker") { - return Item::Joker; - }; - if (i == "Greedy Joker") { - return Item::Greedy_Joker; - }; - if (i == "Lusty Joker") { - return Item::Lusty_Joker; - }; - if (i == "Wrathful Joker") { - return Item::Wrathful_Joker; - }; - if (i == "Gluttonous Joker") { - return Item::Gluttonous_Joker; - }; - if (i == "Jolly Joker") { - return Item::Jolly_Joker; - }; - if (i == "Zany Joker") { - return Item::Zany_Joker; - }; - if (i == "Mad Joker") { - return Item::Mad_Joker; - }; - if (i == "Crazy Joker") { - return Item::Crazy_Joker; - }; - if (i == "Droll Joker") { - return Item::Droll_Joker; - }; - if (i == "Sly Joker") { - return Item::Sly_Joker; - }; - if (i == "Wily Joker") { - return Item::Wily_Joker; - }; - if (i == "Clever Joker") { - return Item::Clever_Joker; - }; - if (i == "Devious Joker") { - return Item::Devious_Joker; - }; - if (i == "Crafty Joker") { - return Item::Crafty_Joker; - }; - if (i == "Half Joker") { - return Item::Half_Joker; - }; - if (i == "Credit Card") { - return Item::Credit_Card; - }; - if (i == "Banner") { - return Item::Banner; - }; - if (i == "Mystic Summit") { - return Item::Mystic_Summit; - }; - if (i == "8 Ball") { - return Item::_8_Ball; - }; - if (i == "Misprint") { - return Item::Misprint; - }; - if (i == "Raised Fist") { - return Item::Raised_Fist; - }; - if (i == "Chaos the Clown") { - return Item::Chaos_the_Clown; - }; - if (i == "Scary Face") { - return Item::Scary_Face; - }; - if (i == "Abstract Joker") { - return Item::Abstract_Joker; - }; - if (i == "Delayed Gratification") { - return Item::Delayed_Gratification; - }; - if (i == "Gros Michel") { - return Item::Gros_Michel; - }; - if (i == "Even Steven") { - return Item::Even_Steven; - }; - if (i == "Odd Todd") { - return Item::Odd_Todd; - }; - if (i == "Scholar") { - return Item::Scholar; - }; - if (i == "Business Card") { - return Item::Business_Card; - }; - if (i == "Supernova") { - return Item::Supernova; - }; - if (i == "Ride the Bus") { - return Item::Ride_the_Bus; - }; - if (i == "Egg") { - return Item::Egg; - }; - if (i == "Runner") { - return Item::Runner; - }; - if (i == "Ice Cream") { - return Item::Ice_Cream; - }; - if (i == "Splash") { - return Item::Splash; - }; - if (i == "Blue Joker") { - return Item::Blue_Joker; - }; - if (i == "Faceless Joker") { - return Item::Faceless_Joker; - }; - if (i == "Green Joker") { - return Item::Green_Joker; - }; - if (i == "Superposition") { - return Item::Superposition; - }; - if (i == "To Do List") { - return Item::To_Do_List; - }; - if (i == "Cavendish") { - return Item::Cavendish; - }; - if (i == "Red Card") { - return Item::Red_Card; - }; - if (i == "Square Joker") { - return Item::Square_Joker; - }; - if (i == "Riff-raff") { - return Item::Riff_raff; - }; - if (i == "Photograph") { - return Item::Photograph; - }; - if (i == "Reserved Parking") { - return Item::Reserved_Parking; - }; - if (i == "Mail-In Rebate") { - return Item::Mail_In_Rebate; - }; - if (i == "Hallucination") { - return Item::Hallucination; - }; - if (i == "Fortune Teller") { - return Item::Fortune_Teller; - }; - if (i == "Juggler") { - return Item::Juggler; - }; - if (i == "Drunkard") { - return Item::Drunkard; - }; - if (i == "Golden Joker") { - return Item::Golden_Joker; - }; - if (i == "Popcorn") { - return Item::Popcorn; - }; - if (i == "Walkie Talkie") { - return Item::Walkie_Talkie; - }; - if (i == "Smiley Face") { - return Item::Smiley_Face; - }; - if (i == "Golden Ticket") { - return Item::Golden_Ticket; - }; - if (i == "Swashbuckler") { - return Item::Swashbuckler; - }; - if (i == "Hanging Chad") { - return Item::Hanging_Chad; - }; - if (i == "Shoot the Moon") { - return Item::Shoot_the_Moon; - }; - if (i == "J C END") { - return Item::J_C_END; - }; - if (i == "J U BEGIN") { - return Item::J_U_BEGIN; - }; - if (i == "Joker Stencil") { - return Item::Joker_Stencil; - }; - if (i == "Four Fingers") { - return Item::Four_Fingers; - }; - if (i == "Mime") { - return Item::Mime; - }; - if (i == "Ceremonial Dagger") { - return Item::Ceremonial_Dagger; - }; - if (i == "Marble Joker") { - return Item::Marble_Joker; - }; - if (i == "Loyalty Card") { - return Item::Loyalty_Card; - }; - if (i == "Dusk") { - return Item::Dusk; - }; - if (i == "Fibonacci") { - return Item::Fibonacci; - }; - if (i == "Steel Joker") { - return Item::Steel_Joker; - }; - if (i == "Hack") { - return Item::Hack; - }; - if (i == "Pareidolia") { - return Item::Pareidolia; - }; - if (i == "Space Joker") { - return Item::Space_Joker; - }; - if (i == "Burglar") { - return Item::Burglar; - }; - if (i == "Blackboard") { - return Item::Blackboard; - }; - if (i == "Sixth Sense") { - return Item::Sixth_Sense; - }; - if (i == "Constellation") { - return Item::Constellation; - }; - if (i == "Hiker") { - return Item::Hiker; - }; - if (i == "Card Sharp") { - return Item::Card_Sharp; - }; - if (i == "Madness") { - return Item::Madness; - }; - if (i == "SΘance") { - return Item::Seance; - }; - if (i == "Shortcut") { - return Item::Shortcut; - }; - if (i == "Hologram") { - return Item::Hologram; - }; - if (i == "Cloud 9") { - return Item::Cloud_9; - }; - if (i == "Rocket") { - return Item::Rocket; - }; - if (i == "Midas Mask") { - return Item::Midas_Mask; - }; - if (i == "Luchador") { - return Item::Luchador; - }; - if (i == "Gift Card") { - return Item::Gift_Card; - }; - if (i == "Turtle Bean") { - return Item::Turtle_Bean; - }; - if (i == "Erosion") { - return Item::Erosion; - }; - if (i == "To the Moon") { - return Item::To_the_Moon; - }; - if (i == "Stone Joker") { - return Item::Stone_Joker; - }; - if (i == "Lucky Cat") { - return Item::Lucky_Cat; - }; - if (i == "Bull") { - return Item::Bull; - }; - if (i == "Diet Cola") { - return Item::Diet_Cola; - }; - if (i == "Trading Card") { - return Item::Trading_Card; - }; - if (i == "Flash Card") { - return Item::Flash_Card; - }; - if (i == "Spare Trousers") { - return Item::Spare_Trousers; - }; - if (i == "Ramen") { - return Item::Ramen; - }; - if (i == "Seltzer") { - return Item::Seltzer; - }; - if (i == "Castle") { - return Item::Castle; - }; - if (i == "Mr. Bones") { - return Item::Mr_Bones; - }; - if (i == "Acrobat") { - return Item::Acrobat; - }; - if (i == "Sock and Buskin") { - return Item::Sock_and_Buskin; - }; - if (i == "Troubadour") { - return Item::Troubadour; - }; - if (i == "Certificate") { - return Item::Certificate; - }; - if (i == "Smeared Joker") { - return Item::Smeared_Joker; - }; - if (i == "Throwback") { - return Item::Throwback; - }; - if (i == "Rough Gem") { - return Item::Rough_Gem; - }; - if (i == "Bloodstone") { - return Item::Bloodstone; - }; - if (i == "Arrowhead") { - return Item::Arrowhead; - }; - if (i == "Onyx Agate") { - return Item::Onyx_Agate; - }; - if (i == "Glass Joker") { - return Item::Glass_Joker; - }; - if (i == "Showman") { - return Item::Showman; - }; - if (i == "Flower Pot") { - return Item::Flower_Pot; - }; - if (i == "Merry Andy") { - return Item::Merry_Andy; - }; - if (i == "Oops! All 6s") { - return Item::Oops_All_6s; - }; - if (i == "The Idol") { - return Item::The_Idol; - }; - if (i == "Seeing Double") { - return Item::Seeing_Double; - }; - if (i == "Matador") { - return Item::Matador; - }; - if (i == "Stuntman") { - return Item::Stuntman; - }; - if (i == "Satellite") { - return Item::Satellite; - }; - if (i == "Cartomancer") { - return Item::Cartomancer; - }; - if (i == "Astronomer") { - return Item::Astronomer; - }; - if (i == "Bootstraps") { - return Item::Bootstraps; - }; - if (i == "J U END") { - return Item::J_U_END; - }; - if (i == "J R BEGIN") { - return Item::J_R_BEGIN; - }; - if (i == "DNA") { - return Item::DNA; - }; - if (i == "Vampire") { - return Item::Vampire; - }; - if (i == "Vagabond") { - return Item::Vagabond; - }; - if (i == "Baron") { - return Item::Baron; - }; - if (i == "Obelisk") { - return Item::Obelisk; - }; - if (i == "Baseball Card") { - return Item::Baseball_Card; - }; - if (i == "Ancient Joker") { - return Item::Ancient_Joker; - }; - if (i == "Campfire") { - return Item::Campfire; - }; - if (i == "Blueprint") { - return Item::Blueprint; - }; - if (i == "Wee Joker") { - return Item::Wee_Joker; - }; - if (i == "Hit the Road") { - return Item::Hit_the_Road; - }; - if (i == "The Duo") { - return Item::The_Duo; - }; - if (i == "The Trio") { - return Item::The_Trio; - }; - if (i == "The Family") { - return Item::The_Family; - }; - if (i == "The Order") { - return Item::The_Order; - }; - if (i == "The Tribe") { - return Item::The_Tribe; - }; - if (i == "Invisible Joker") { - return Item::Invisible_Joker; - }; - if (i == "Brainstorm") { - return Item::Brainstorm; - }; - if (i == "Driver's License") { - return Item::Drivers_License; - }; - if (i == "Burnt Joker") { - return Item::Burnt_Joker; - }; - if (i == "J R END") { - return Item::J_R_END; - }; - if (i == "J L BEGIN") { - return Item::J_L_BEGIN; - }; - if (i == "Canio") { - return Item::Canio; - }; - if (i == "Triboulet") { - return Item::Triboulet; - }; - if (i == "Yorick") { - return Item::Yorick; - }; - if (i == "Chicot") { - return Item::Chicot; - }; - if (i == "Perkeo") { - return Item::Perkeo; - }; - if (i == "J L END") { - return Item::J_L_END; - }; - if (i == "J END") { - return Item::J_END; - }; - if (i == "V BEGIN") { - return Item::V_BEGIN; - }; - if (i == "Overstock") { - return Item::Overstock; - }; - if (i == "Overstock Plus") { - return Item::Overstock_Plus; - }; - if (i == "Clearance Sale") { - return Item::Clearance_Sale; - }; - if (i == "Liquidation") { - return Item::Liquidation; - }; - if (i == "Hone") { - return Item::Hone; - }; - if (i == "Glow Up") { - return Item::Glow_Up; - }; - if (i == "Reroll Surplus") { - return Item::Reroll_Surplus; - }; - if (i == "Reroll Glut") { - return Item::Reroll_Glut; - }; - if (i == "Crystal Ball") { - return Item::Crystal_Ball; - }; - if (i == "Omen Globe") { - return Item::Omen_Globe; - }; - if (i == "Telescope") { - return Item::Telescope; - }; - if (i == "Observatory") { - return Item::Observatory; - }; - if (i == "Grabber") { - return Item::Grabber; - }; - if (i == "Nacho Tong") { - return Item::Nacho_Tong; - }; - if (i == "Wasteful") { - return Item::Wasteful; - }; - if (i == "Recyclomancy") { - return Item::Recyclomancy; - }; - if (i == "Tarot Merchant") { - return Item::Tarot_Merchant; - }; - if (i == "Tarot Tycoon") { - return Item::Tarot_Tycoon; - }; - if (i == "Planet Merchant") { - return Item::Planet_Merchant; - }; - if (i == "Planet Tycoon") { - return Item::Planet_Tycoon; - }; - if (i == "Seed Money") { - return Item::Seed_Money; - }; - if (i == "Money Tree") { - return Item::Money_Tree; - }; - if (i == "Blank") { - return Item::Blank; - }; - if (i == "Antimatter") { - return Item::Antimatter; - }; - if (i == "Magic Trick") { - return Item::Magic_Trick; - }; - if (i == "Illusion") { - return Item::Illusion; - }; - if (i == "Hieroglyph") { - return Item::Hieroglyph; - }; - if (i == "Petroglyph") { - return Item::Petroglyph; - }; - if (i == "Director's Cut") { - return Item::Directors_Cut; - }; - if (i == "Retcon") { - return Item::Retcon; - }; - if (i == "Paint Brush") { - return Item::Paint_Brush; - }; - if (i == "Palette") { - return Item::Palette; - }; - if (i == "V END") { - return Item::V_END; - }; - if (i == "T BEGIN") { - return Item::T_BEGIN; - }; - if (i == "The Fool") { - return Item::The_Fool; - }; - if (i == "The Magician") { - return Item::The_Magician; - }; - if (i == "The High Priestess") { - return Item::The_High_Priestess; - }; - if (i == "The Empress") { - return Item::The_Empress; - }; - if (i == "The Emperor") { - return Item::The_Emperor; - }; - if (i == "The Hierophant") { - return Item::The_Hierophant; - }; - if (i == "The Lovers") { - return Item::The_Lovers; - }; - if (i == "The Chariot") { - return Item::The_Chariot; - }; - if (i == "Justice") { - return Item::Justice; - }; - if (i == "The Hermit") { - return Item::The_Hermit; - }; - if (i == "The Wheel of Fortune") { - return Item::The_Wheel_of_Fortune; - }; - if (i == "Strength") { - return Item::Strength; - }; - if (i == "The Hanged Man") { - return Item::The_Hanged_Man; - }; - if (i == "Death") { - return Item::Death; - }; - if (i == "Temperance") { - return Item::Temperance; - }; - if (i == "The Devil") { - return Item::The_Devil; - }; - if (i == "The Tower") { - return Item::The_Tower; - }; - if (i == "The Star") { - return Item::The_Star; - }; - if (i == "The Moon") { - return Item::The_Moon; - }; - if (i == "The Sun") { - return Item::The_Sun; - }; - if (i == "Judgement") { - return Item::Judgement; - }; - if (i == "The World") { - return Item::The_World; - }; - if (i == "T END") { - return Item::T_END; - }; - if (i == "P BEGIN") { - return Item::P_BEGIN; - }; - if (i == "Mercury") { - return Item::Mercury; - }; - if (i == "Venus") { - return Item::Venus; - }; - if (i == "Earth") { - return Item::Earth; - }; - if (i == "Mars") { - return Item::Mars; - }; - if (i == "Jupiter") { - return Item::Jupiter; - }; - if (i == "Saturn") { - return Item::Saturn; - }; - if (i == "Uranus") { - return Item::Uranus; - }; - if (i == "Neptune") { - return Item::Neptune; - }; - if (i == "Pluto") { - return Item::Pluto; - }; - if (i == "Planet X") { - return Item::Planet_X; - }; - if (i == "Ceres") { - return Item::Ceres; - }; - if (i == "Eris") { - return Item::Eris; - }; - if (i == "P END") { - return Item::P_END; - }; - if (i == "H BEGIN") { - return Item::H_BEGIN; - }; - if (i == "Pair") { - return Item::Pair; - }; - if (i == "Three of a Kind") { - return Item::Three_of_a_Kind; - }; - if (i == "Full House") { - return Item::Full_House; - }; - if (i == "Four of a Kind") { - return Item::Four_of_a_Kind; - }; - if (i == "Flush") { - return Item::Flush; - }; - if (i == "Straight") { - return Item::Straight; - }; - if (i == "Two Pair") { - return Item::Two_Pair; - }; - if (i == "Straight Flush") { - return Item::Straight_Flush; - }; - if (i == "High Card") { - return Item::High_Card; - }; - if (i == "Five of a Kind") { - return Item::Five_of_a_Kind; - }; - if (i == "Flush House") { - return Item::Flush_House; - }; - if (i == "Flush Five") { - return Item::Flush_Five; - }; - if (i == "H END") { - return Item::H_END; - }; - if (i == "S BEGIN") { - return Item::S_BEGIN; - }; - if (i == "Familiar") { - return Item::Familiar; - }; - if (i == "Grim") { - return Item::Grim; - }; - if (i == "Incantation") { - return Item::Incantation; - }; - if (i == "Talisman") { - return Item::Talisman; - }; - if (i == "Aura") { - return Item::Aura; - }; - if (i == "Wraith") { - return Item::Wraith; - }; - if (i == "Sigil") { - return Item::Sigil; - }; - if (i == "Ouija") { - return Item::Ouija; - }; - if (i == "Ectoplasm") { - return Item::Ectoplasm; - }; - if (i == "Immolate") { - return Item::Immolate; - }; - if (i == "Ankh") { - return Item::Ankh; - }; - if (i == "Deja Vu") { - return Item::Deja_Vu; - }; - if (i == "Hex") { - return Item::Hex; - }; - if (i == "Trance") { - return Item::Trance; - }; - if (i == "Medium") { - return Item::Medium; - }; - if (i == "Cryptid") { - return Item::Cryptid; - }; - if (i == "The Soul") { - return Item::The_Soul; - }; - if (i == "Black Hole") { - return Item::Black_Hole; - }; - if (i == "S END") { - return Item::S_END; - }; - if (i == "ENHANCEMENT BEGIN") { - return Item::ENHANCEMENT_BEGIN; - }; - if (i == "No Enhancement") { - return Item::No_Enhancement; - }; - if (i == "Bonus Card") { - return Item::Bonus_Card; - }; - if (i == "Mult Card") { - return Item::Mult_Card; - }; - if (i == "Wild Card") { - return Item::Wild_Card; - }; - if (i == "Glass Card") { - return Item::Glass_Card; - }; - if (i == "Steel Card") { - return Item::Steel_Card; - }; - if (i == "Stone Card") { - return Item::Stone_Card; - }; - if (i == "Gold Card") { - return Item::Gold_Card; - }; - if (i == "Lucky Card") { - return Item::Lucky_Card; - }; - if (i == "ENHANCEMENT END") { - return Item::ENHANCEMENT_END; - }; - if (i == "SEAL BEGIN") { - return Item::SEAL_BEGIN; - }; - if (i == "No Seal") { - return Item::No_Seal; - }; - if (i == "Gold Seal") { - return Item::Gold_Seal; - }; - if (i == "Red Seal") { - return Item::Red_Seal; - }; - if (i == "Blue Seal") { - return Item::Blue_Seal; - }; - if (i == "Purple Seal") { - return Item::Purple_Seal; - }; - if (i == "SEAL END") { - return Item::SEAL_END; - }; - if (i == "E BEGIN") { - return Item::E_BEGIN; - }; - if (i == "No Edition") { - return Item::No_Edition; - }; - if (i == "Foil") { - return Item::Foil; - }; - if (i == "Holographic") { - return Item::Holographic; - }; - if (i == "Polychrome") { - return Item::Polychrome; - }; - if (i == "Negative") { - return Item::Negative; - }; - if (i == "E END") { - return Item::E_END; - }; - if (i == "PACK BEGIN") { - return Item::PACK_BEGIN; - }; - if (i == "Arcana Pack") { - return Item::Arcana_Pack; - }; - if (i == "Jumbo Arcana Pack") { - return Item::Jumbo_Arcana_Pack; - }; - if (i == "Mega Arcana Pack") { - return Item::Mega_Arcana_Pack; - }; - if (i == "Celestial Pack") { - return Item::Celestial_Pack; - }; - if (i == "Jumbo Celestial Pack") { - return Item::Jumbo_Celestial_Pack; - }; - if (i == "Mega Celestial Pack") { - return Item::Mega_Celestial_Pack; - }; - if (i == "Standard Pack") { - return Item::Standard_Pack; - }; - if (i == "Jumbo Standard Pack") { - return Item::Jumbo_Standard_Pack; - }; - if (i == "Mega Standard Pack") { - return Item::Mega_Standard_Pack; - }; - if (i == "Buffoon Pack") { - return Item::Buffoon_Pack; - }; - if (i == "Jumbo Buffoon Pack") { - return Item::Jumbo_Buffoon_Pack; - }; - if (i == "Mega Buffoon Pack") { - return Item::Mega_Buffoon_Pack; - }; - if (i == "Spectral Pack") { - return Item::Spectral_Pack; - }; - if (i == "Jumbo Spectral Pack") { - return Item::Jumbo_Spectral_Pack; - }; - if (i == "Mega Spectral Pack") { - return Item::Mega_Spectral_Pack; - }; - if (i == "PACK END") { - return Item::PACK_END; - }; - if (i == "TAG BEGIN") { - return Item::TAG_BEGIN; - }; - if (i == "Uncommon Tag") { - return Item::Uncommon_Tag; - }; - if (i == "Rare Tag") { - return Item::Rare_Tag; - }; - if (i == "Negative Tag") { - return Item::Negative_Tag; - }; - if (i == "Foil Tag") { - return Item::Foil_Tag; - }; - if (i == "Holographic Tag") { - return Item::Holographic_Tag; - }; - if (i == "Polychrome Tag") { - return Item::Polychrome_Tag; - }; - if (i == "Investment Tag") { - return Item::Investment_Tag; - }; - if (i == "Voucher Tag") { - return Item::Voucher_Tag; - }; - if (i == "Boss Tag") { - return Item::Boss_Tag; - }; - if (i == "Standard Tag") { - return Item::Standard_Tag; - }; - if (i == "Charm Tag") { - return Item::Charm_Tag; - }; - if (i == "Meteor Tag") { - return Item::Meteor_Tag; - }; - if (i == "Buffoon Tag") { - return Item::Buffoon_Tag; - }; - if (i == "Handy Tag") { - return Item::Handy_Tag; - }; - if (i == "Garbage Tag") { - return Item::Garbage_Tag; - }; - if (i == "Ethereal Tag") { - return Item::Ethereal_Tag; - }; - if (i == "Coupon Tag") { - return Item::Coupon_Tag; - }; - if (i == "Double Tag") { - return Item::Double_Tag; - }; - if (i == "Juggle Tag") { - return Item::Juggle_Tag; - }; - if (i == "D6 Tag") { - return Item::D6_Tag; - }; - if (i == "Top-up Tag") { - return Item::Top_up_Tag; - }; - if (i == "Speed Tag") { - return Item::Speed_Tag; - }; - if (i == "Orbital Tag") { - return Item::Orbital_Tag; - }; - if (i == "Economy Tag") { - return Item::Economy_Tag; - }; - if (i == "TAG END") { - return Item::TAG_END; - }; - if (i == "B BEGIN") { - return Item::B_BEGIN; - }; - if (i == "Small Blind") { - return Item::Small_Blind; - }; - if (i == "Big Blind") { - return Item::Big_Blind; - }; - if (i == "The Hook") { - return Item::The_Hook; - }; - if (i == "The Ox") { - return Item::The_Ox; - }; - if (i == "The House") { - return Item::The_House; - }; - if (i == "The Wall") { - return Item::The_Wall; - }; - if (i == "The Wheel") { - return Item::The_Wheel; - }; - if (i == "The Arm") { - return Item::The_Arm; - }; - if (i == "The Club") { - return Item::The_Club; - }; - if (i == "The Fish") { - return Item::The_Fish; - }; - if (i == "The Psychic") { - return Item::The_Psychic; - }; - if (i == "The Goad") { - return Item::The_Goad; - }; - if (i == "The Water") { - return Item::The_Water; - }; - if (i == "The Window") { - return Item::The_Window; - }; - if (i == "The Manacle") { - return Item::The_Manacle; - }; - if (i == "The Eye") { - return Item::The_Eye; - }; - if (i == "The Mouth") { - return Item::The_Mouth; - }; - if (i == "The Plant") { - return Item::The_Plant; - }; - if (i == "The Serpent") { - return Item::The_Serpent; - }; - if (i == "The Pillar") { - return Item::The_Pillar; - }; - if (i == "The Needle") { - return Item::The_Needle; - }; - if (i == "The Head") { - return Item::The_Head; - }; - if (i == "The Tooth") { - return Item::The_Tooth; - }; - if (i == "The Flint") { - return Item::The_Flint; - }; - if (i == "The Mark") { - return Item::The_Mark; - }; - if (i == "B F BEGIN") { - return Item::B_F_BEGIN; - }; - if (i == "Amber Acorn") { - return Item::Amber_Acorn; - }; - if (i == "Verdant Leaf") { - return Item::Verdant_Leaf; - }; - if (i == "Violet Vessel") { - return Item::Violet_Vessel; - }; - if (i == "Crimson Heart") { - return Item::Crimson_Heart; - }; - if (i == "Cerulean Bell") { - return Item::Cerulean_Bell; - }; - if (i == "B F END") { - return Item::B_F_END; - }; - if (i == "B END") { - return Item::B_END; - }; - if (i == "SUIT BEGIN") { - return Item::SUIT_BEGIN; - }; - if (i == "Hearts") { - return Item::Hearts; - }; - if (i == "Clubs") { - return Item::Clubs; - }; - if (i == "Diamonds") { - return Item::Diamonds; - }; - if (i == "Spades") { - return Item::Spades; - }; - if (i == "SUIT END") { - return Item::SUIT_END; - }; - if (i == "RANK BEGIN") { - return Item::RANK_BEGIN; - }; - if (i == "2") { - return Item::_2; - }; - if (i == "3") { - return Item::_3; - }; - if (i == "4") { - return Item::_4; - }; - if (i == "5") { - return Item::_5; - }; - if (i == "6") { - return Item::_6; - }; - if (i == "7") { - return Item::_7; - }; - if (i == "8") { - return Item::_8; - }; - if (i == "9") { - return Item::_9; - }; - if (i == "10") { - return Item::_10; - }; - if (i == "Jack") { - return Item::Jack; - }; - if (i == "Queen") { - return Item::Queen; - }; - if (i == "King") { - return Item::King; - }; - if (i == "Ace") { - return Item::Ace; - }; - if (i == "RANK END") { - return Item::RANK_END; - }; - if (i == "C BEGIN") { - return Item::C_BEGIN; - }; - if (i == "C 2") { - return Item::C_2; - }; - if (i == "C 3") { - return Item::C_3; - }; - if (i == "C 4") { - return Item::C_4; - }; - if (i == "C 5") { - return Item::C_5; - }; - if (i == "C 6") { - return Item::C_6; - }; - if (i == "C 7") { - return Item::C_7; - }; - if (i == "C 8") { - return Item::C_8; - }; - if (i == "C 9") { - return Item::C_9; - }; - if (i == "C A") { - return Item::C_A; - }; - if (i == "C J") { - return Item::C_J; - }; - if (i == "C K") { - return Item::C_K; - }; - if (i == "C Q") { - return Item::C_Q; - }; - if (i == "C T") { - return Item::C_T; - }; - if (i == "D 2") { - return Item::D_2; - }; - if (i == "D 3") { - return Item::D_3; - }; - if (i == "D 4") { - return Item::D_4; - }; - if (i == "D 5") { - return Item::D_5; - }; - if (i == "D 6") { - return Item::D_6; - }; - if (i == "D 7") { - return Item::D_7; - }; - if (i == "D 8") { - return Item::D_8; - }; - if (i == "D 9") { - return Item::D_9; - }; - if (i == "D A") { - return Item::D_A; - }; - if (i == "D J") { - return Item::D_J; - }; - if (i == "D K") { - return Item::D_K; - }; - if (i == "D Q") { - return Item::D_Q; - }; - if (i == "D T") { - return Item::D_T; - }; - if (i == "H 2") { - return Item::H_2; - }; - if (i == "H 3") { - return Item::H_3; - }; - if (i == "H 4") { - return Item::H_4; - }; - if (i == "H 5") { - return Item::H_5; - }; - if (i == "H 6") { - return Item::H_6; - }; - if (i == "H 7") { - return Item::H_7; - }; - if (i == "H 8") { - return Item::H_8; - }; - if (i == "H 9") { - return Item::H_9; - }; - if (i == "H A") { - return Item::H_A; - }; - if (i == "H J") { - return Item::H_J; - }; - if (i == "H K") { - return Item::H_K; - }; - if (i == "H Q") { - return Item::H_Q; - }; - if (i == "H T") { - return Item::H_T; - }; - if (i == "S 2") { - return Item::S_2; - }; - if (i == "S 3") { - return Item::S_3; - }; - if (i == "S 4") { - return Item::S_4; - }; - if (i == "S 5") { - return Item::S_5; - }; - if (i == "S 6") { - return Item::S_6; - }; - if (i == "S 7") { - return Item::S_7; - }; - if (i == "S 8") { - return Item::S_8; - }; - if (i == "S 9") { - return Item::S_9; - }; - if (i == "S A") { - return Item::S_A; - }; - if (i == "S J") { - return Item::S_J; - }; - if (i == "S K") { - return Item::S_K; - }; - if (i == "S Q") { - return Item::S_Q; - }; - if (i == "S T") { - return Item::S_T; - }; - if (i == "C END") { - return Item::C_END; - }; - if (i == "D BEGIN") { - return Item::D_BEGIN; - }; - if (i == "Red Deck") { - return Item::Red_Deck; - }; - if (i == "Blue Deck") { - return Item::Blue_Deck; - }; - if (i == "Yellow Deck") { - return Item::Yellow_Deck; - }; - if (i == "Green Deck") { - return Item::Green_Deck; - }; - if (i == "Black Deck") { - return Item::Black_Deck; - }; - if (i == "Magic Deck") { - return Item::Magic_Deck; - }; - if (i == "Nebula Deck") { - return Item::Nebula_Deck; - }; - if (i == "Ghost Deck") { - return Item::Ghost_Deck; - }; - if (i == "Abandoned Deck") { - return Item::Abandoned_Deck; - }; - if (i == "Checkered Deck") { - return Item::Checkered_Deck; - }; - if (i == "Zodiac Deck") { - return Item::Zodiac_Deck; - }; - if (i == "Painted Deck") { - return Item::Painted_Deck; - }; - if (i == "Anaglyph Deck") { - return Item::Anaglyph_Deck; - }; - if (i == "Plasma Deck") { - return Item::Plasma_Deck; - }; - if (i == "Erratic Deck") { - return Item::Erratic_Deck; - }; - if (i == "Challenge Deck") { - return Item::Challenge_Deck; - }; - if (i == "D END") { - return Item::D_END; - }; - if (i == "CHAL BEGIN") { - return Item::CHAL_BEGIN; - }; - if (i == "The Omelette") { - return Item::The_Omelette; - }; - if (i == "15 Minute City") { - return Item::_15_Minute_City; - }; - if (i == "Rich get Richer") { - return Item::Rich_get_Richer; - }; - if (i == "On a Knife's Edge") { - return Item::On_a_Knifes_Edge; - }; - if (i == "X-ray Vision") { - return Item::X_ray_Vision; - }; - if (i == "Mad World") { - return Item::Mad_World; - }; - if (i == "Luxury Tax") { - return Item::Luxury_Tax; - }; - if (i == "Non-Perishable") { - return Item::Non_Perishable; - }; - if (i == "Medusa") { - return Item::Medusa; - }; - if (i == "Double or Nothing") { - return Item::Double_or_Nothing; - }; - if (i == "Typecast") { - return Item::Typecast; - }; - if (i == "Inflation") { - return Item::Inflation; - }; - if (i == "Bram Poker") { - return Item::Bram_Poker; - }; - if (i == "Fragile") { - return Item::Fragile; - }; - if (i == "Monolith") { - return Item::Monolith; - }; - if (i == "Blast Off") { - return Item::Blast_Off; - }; - if (i == "Five-Card Draw") { - return Item::Five_Card_Draw; - }; - if (i == "Golden Needle") { - return Item::Golden_Needle; - }; - if (i == "Cruelty") { - return Item::Cruelty; - }; - if (i == "Jokerless") { - return Item::Jokerless; - }; - if (i == "CHAL END") { - return Item::CHAL_END; - }; - if (i == "STAKE BEGIN") { - return Item::STAKE_BEGIN; - }; - if (i == "White Stake") { - return Item::White_Stake; - }; - if (i == "Red Stake") { - return Item::Red_Stake; - }; - if (i == "Green Stake") { - return Item::Green_Stake; - }; - if (i == "Black Stake") { - return Item::Black_Stake; - }; - if (i == "Blue Stake") { - return Item::Blue_Stake; - }; - if (i == "Purple Stake") { - return Item::Purple_Stake; - }; - if (i == "Orange Stake") { - return Item::Orange_Stake; - }; - if (i == "Gold Stake") { - return Item::Gold_Stake; - }; - if (i == "STAKE END") { - return Item::STAKE_END; - }; - if (i == "RARITY BEGIN") { - return Item::RARITY_BEGIN; - }; - if (i == "Common") { - return Item::Common; - }; - if (i == "Uncommon") { - return Item::Uncommon; - }; - if (i == "Rare") { - return Item::Rare; - }; - if (i == "Legendary") { - return Item::Legendary; - }; - if (i == "RARITY END") { - return Item::RARITY_END; - }; - if (i == "TYPE BEGIN") { - return Item::TYPE_BEGIN; - }; - if (i == "T Joker") { - return Item::T_Joker; - }; - if (i == "T Tarot") { - return Item::T_Tarot; - }; - if (i == "T Planet") { - return Item::T_Planet; - }; - if (i == "T Spectral") { - return Item::T_Spectral; - }; - if (i == "T Playing Card") { - return Item::T_Playing_Card; - }; - if (i == "TYPE END") { - return Item::TYPE_END; - }; - return Item::RETRY; -} - -// Structs for storing information -struct ShopInstance { - double jokerRate; - double tarotRate; - double planetRate; - double playingCardRate; - double spectralRate; - ShopInstance() { - jokerRate = 20; - tarotRate = 4; - planetRate = 4; - playingCardRate = 0; - spectralRate = 0; - }; - ShopInstance(double j, double t, double p, double c, double s) { - jokerRate = j; - tarotRate = t; - planetRate = p; - playingCardRate = c; - spectralRate = s; - } - double getTotalRate() { - return jokerRate + tarotRate + planetRate + playingCardRate + spectralRate; - } -}; - -struct JokerStickers { - bool eternal; - bool perishable; - bool rental; - JokerStickers() { - eternal = false; - perishable = false; - rental = false; - }; - JokerStickers(bool e, bool p, bool r) { - eternal = e; - perishable = p; - rental = r; - } -}; - -struct JokerData { - Item joker; - Item rarity; - Item edition; - JokerStickers stickers; - JokerData() { - joker = Item::Joker; - rarity = Item::Common; - edition = Item::No_Edition; - stickers = JokerStickers(); - }; - JokerData(Item j, Item r, Item e, JokerStickers s) { - joker = j; - rarity = r; - edition = e; - stickers = s; - }; -}; - -struct ShopItem { - Item type; - Item item; - JokerData jokerData; - ShopItem() { - type = Item::T_Tarot; - item = Item::The_Fool; - }; - ShopItem(Item t, Item i) { - type = t; - item = i; - }; - ShopItem(Item t, Item i, JokerData j) { - type = t; - item = i; - jokerData = j; - }; -}; - -struct WeightedItem { - Item item; - double weight; - WeightedItem(Item i, double w) { - item = i; - weight = w; - }; -}; - -struct Pack { - Item type; - int size; - int choices; - Pack(Item t, int s, int c) { - type = t; - size = s; - choices = c; - } -}; - -struct Card { - Item base; - Item enhancement; - Item edition; - Item seal; - Card(Item b, Item n, Item e, Item s) { - base = b; - enhancement = n; - edition = e; - seal = s; - } -}; - -constexpr inline std::array ENHANCEMENTS = { - Item::Bonus_Card, Item::Mult_Card, Item::Wild_Card, Item::Glass_Card, - Item::Steel_Card, Item::Stone_Card, Item::Gold_Card, Item::Lucky_Card}; - -constexpr inline std::array CARDS = { - Item::C_2, Item::C_3, Item::C_4, Item::C_5, Item::C_6, Item::C_7, Item::C_8, - Item::C_9, Item::C_A, Item::C_J, Item::C_K, Item::C_Q, Item::C_T, Item::D_2, - Item::D_3, Item::D_4, Item::D_5, Item::D_6, Item::D_7, Item::D_8, Item::D_9, - Item::D_A, Item::D_J, Item::D_K, Item::D_Q, Item::D_T, Item::H_2, Item::H_3, - Item::H_4, Item::H_5, Item::H_6, Item::H_7, Item::H_8, Item::H_9, Item::H_A, - Item::H_J, Item::H_K, Item::H_Q, Item::H_T, Item::S_2, Item::S_3, Item::S_4, - Item::S_5, Item::S_6, Item::S_7, Item::S_8, Item::S_9, Item::S_A, Item::S_J, - Item::S_K, Item::S_Q, Item::S_T}; - -constexpr inline std::array SUITS = {Item::Spades, Item::Hearts, - Item::Clubs, Item::Diamonds}; - -constexpr inline std::array RANKS = { - Item::_2, Item::_3, Item::_4, Item::_5, Item::_6, - Item::_7, Item::_8, Item::_9, Item::_10, Item::Jack, - Item::Queen, Item::King, Item::Ace}; - -inline std::array PACKS = { - WeightedItem(Item::RETRY, 22.42), // total - WeightedItem(Item::Arcana_Pack, 4), - WeightedItem(Item::Jumbo_Arcana_Pack, 2), - WeightedItem(Item::Mega_Arcana_Pack, 0.5), - WeightedItem(Item::Celestial_Pack, 4), - WeightedItem(Item::Jumbo_Celestial_Pack, 2), - WeightedItem(Item::Mega_Celestial_Pack, 0.5), - WeightedItem(Item::Standard_Pack, 4), - WeightedItem(Item::Jumbo_Standard_Pack, 2), - WeightedItem(Item::Mega_Standard_Pack, 0.5), - WeightedItem(Item::Buffoon_Pack, 1.2), - WeightedItem(Item::Jumbo_Buffoon_Pack, 0.6), - WeightedItem(Item::Mega_Buffoon_Pack, 0.15), - WeightedItem(Item::Spectral_Pack, 0.6), - WeightedItem(Item::Jumbo_Spectral_Pack, 0.3), - WeightedItem(Item::Mega_Spectral_Pack, 0.07)}; - -constexpr inline std::array TAROTS = {Item::The_Fool, - Item::The_Magician, - Item::The_High_Priestess, - Item::The_Empress, - Item::The_Emperor, - Item::The_Hierophant, - Item::The_Lovers, - Item::The_Chariot, - Item::Justice, - Item::The_Hermit, - Item::The_Wheel_of_Fortune, - Item::Strength, - Item::The_Hanged_Man, - Item::Death, - Item::Temperance, - Item::The_Devil, - Item::The_Tower, - Item::The_Star, - Item::The_Moon, - Item::The_Sun, - Item::Judgement, - Item::The_World}; - -constexpr inline std::array PLANETS = { - Item::Mercury, Item::Venus, Item::Earth, Item::Mars, - Item::Jupiter, Item::Saturn, Item::Uranus, Item::Neptune, - Item::Pluto, Item::Planet_X, Item::Ceres, Item::Eris}; - -constexpr inline std::array COMMON_JOKERS_100 = { - Item::Joker, - Item::Greedy_Joker, - Item::Lusty_Joker, - Item::Wrathful_Joker, - Item::Gluttonous_Joker, - Item::Jolly_Joker, - Item::Zany_Joker, - Item::Mad_Joker, - Item::Crazy_Joker, - Item::Droll_Joker, - Item::Sly_Joker, - Item::Wily_Joker, - Item::Clever_Joker, - Item::Devious_Joker, - Item::Crafty_Joker, - Item::Half_Joker, - Item::Credit_Card, - Item::Banner, - Item::Mystic_Summit, - Item::_8_Ball, - Item::Misprint, - Item::Raised_Fist, - Item::Chaos_the_Clown, - Item::Scary_Face, - Item::Abstract_Joker, - Item::Delayed_Gratification, - Item::Gros_Michel, - Item::Even_Steven, - Item::Odd_Todd, - Item::Scholar, - Item::Business_Card, - Item::Supernova, - Item::Ride_the_Bus, - Item::Egg, - Item::Runner, - Item::Ice_Cream, - Item::Splash, - Item::Blue_Joker, - Item::Faceless_Joker, - Item::Green_Joker, - Item::Superposition, - Item::To_Do_List, - Item::Cavendish, - Item::Red_Card, - Item::Square_Joker, - Item::Riff_raff, - Item::Photograph, - Item::Mail_In_Rebate, - Item::Hallucination, - Item::Fortune_Teller, - Item::Juggler, - Item::Drunkard, - Item::Golden_Joker, - Item::Popcorn, - Item::Walkie_Talkie, - Item::Smiley_Face, - Item::Golden_Ticket, - Item::Swashbuckler, - Item::Hanging_Chad, - Item::Shoot_the_Moon}; - -constexpr inline std::array COMMON_JOKERS = { - Item::Joker, - Item::Greedy_Joker, - Item::Lusty_Joker, - Item::Wrathful_Joker, - Item::Gluttonous_Joker, - Item::Jolly_Joker, - Item::Zany_Joker, - Item::Mad_Joker, - Item::Crazy_Joker, - Item::Droll_Joker, - Item::Sly_Joker, - Item::Wily_Joker, - Item::Clever_Joker, - Item::Devious_Joker, - Item::Crafty_Joker, - Item::Half_Joker, - Item::Credit_Card, - Item::Banner, - Item::Mystic_Summit, - Item::_8_Ball, - Item::Misprint, - Item::Raised_Fist, - Item::Chaos_the_Clown, - Item::Scary_Face, - Item::Abstract_Joker, - Item::Delayed_Gratification, - Item::Gros_Michel, - Item::Even_Steven, - Item::Odd_Todd, - Item::Scholar, - Item::Business_Card, - Item::Supernova, - Item::Ride_the_Bus, - Item::Egg, - Item::Runner, - Item::Ice_Cream, - Item::Splash, - Item::Blue_Joker, - Item::Faceless_Joker, - Item::Green_Joker, - Item::Superposition, - Item::To_Do_List, - Item::Cavendish, - Item::Red_Card, - Item::Square_Joker, - Item::Riff_raff, - Item::Photograph, - Item::Reserved_Parking, - Item::Mail_In_Rebate, - Item::Hallucination, - Item::Fortune_Teller, - Item::Juggler, - Item::Drunkard, - Item::Golden_Joker, - Item::Popcorn, - Item::Walkie_Talkie, - Item::Smiley_Face, - Item::Golden_Ticket, - Item::Swashbuckler, - Item::Hanging_Chad, - Item::Shoot_the_Moon, -}; - -constexpr inline std::array UNCOMMON_JOKERS_100 = { - Item::Joker_Stencil, Item::Four_Fingers, - Item::Mime, Item::Ceremonial_Dagger, - Item::Marble_Joker, Item::Loyalty_Card, - Item::Dusk, Item::Fibonacci, - Item::Steel_Joker, Item::Hack, - Item::Pareidolia, Item::Space_Joker, - Item::Burglar, Item::Blackboard, - Item::Constellation, Item::Hiker, - Item::Card_Sharp, Item::Madness, - Item::Vampire, Item::Shortcut, - Item::Hologram, Item::Vagabond, - Item::Cloud_9, Item::Rocket, - Item::Midas_Mask, Item::Luchador, - Item::Gift_Card, Item::Turtle_Bean, - Item::Erosion, Item::Reserved_Parking, - Item::To_the_Moon, Item::Stone_Joker, - Item::Lucky_Cat, Item::Bull, - Item::Diet_Cola, Item::Trading_Card, - Item::Flash_Card, Item::Spare_Trousers, - Item::Ramen, Item::Seltzer, - Item::Castle, Item::Mr_Bones, - Item::Acrobat, Item::Sock_and_Buskin, - Item::Troubadour, Item::Certificate, - Item::Smeared_Joker, Item::Throwback, - Item::Rough_Gem, Item::Bloodstone, - Item::Arrowhead, Item::Onyx_Agate, - Item::Glass_Joker, Item::Showman, - Item::Flower_Pot, Item::Merry_Andy, - Item::Oops_All_6s, Item::The_Idol, - Item::Seeing_Double, Item::Matador, - Item::Stuntman, Item::Satellite, - Item::Cartomancer, Item::Astronomer, - Item::Burnt_Joker, Item::Bootstraps}; - -constexpr inline std::array UNCOMMON_JOKERS = { - Item::Joker_Stencil, Item::Four_Fingers, - Item::Mime, Item::Ceremonial_Dagger, - Item::Marble_Joker, Item::Loyalty_Card, - Item::Dusk, Item::Fibonacci, - Item::Steel_Joker, Item::Hack, - Item::Pareidolia, Item::Space_Joker, - Item::Burglar, Item::Blackboard, - Item::Sixth_Sense, Item::Constellation, - Item::Hiker, Item::Card_Sharp, - Item::Madness, Item::Seance, - Item::Vampire, Item::Shortcut, - Item::Hologram, Item::Cloud_9, - Item::Rocket, Item::Midas_Mask, - Item::Luchador, Item::Gift_Card, - Item::Turtle_Bean, Item::Erosion, - Item::To_the_Moon, Item::Stone_Joker, - Item::Lucky_Cat, Item::Bull, - Item::Diet_Cola, Item::Trading_Card, - Item::Flash_Card, Item::Spare_Trousers, - Item::Ramen, Item::Seltzer, - Item::Castle, Item::Mr_Bones, - Item::Acrobat, Item::Sock_and_Buskin, - Item::Troubadour, Item::Certificate, - Item::Smeared_Joker, Item::Throwback, - Item::Rough_Gem, Item::Bloodstone, - Item::Arrowhead, Item::Onyx_Agate, - Item::Glass_Joker, Item::Showman, - Item::Flower_Pot, Item::Merry_Andy, - Item::Oops_All_6s, Item::The_Idol, - Item::Seeing_Double, Item::Matador, - Item::Satellite, Item::Cartomancer, - Item::Astronomer, Item::Bootstraps, -}; - -constexpr inline std::array RARE_JOKERS_100 = {Item::DNA, - Item::Sixth_Sense, - Item::Seance, - Item::Baron, - Item::Obelisk, - Item::Baseball_Card, - Item::Ancient_Joker, - Item::Campfire, - Item::Blueprint, - Item::Wee_Joker, - Item::Hit_the_Road, - Item::The_Duo, - Item::The_Trio, - Item::The_Family, - Item::The_Order, - Item::The_Tribe, - Item::Invisible_Joker, - Item::Brainstorm, - Item::Drivers_License}; - -constexpr inline std::array RARE_JOKERS = { - Item::DNA, - Item::Vagabond, - Item::Baron, - Item::Obelisk, - Item::Baseball_Card, - Item::Ancient_Joker, - Item::Campfire, - Item::Blueprint, - Item::Wee_Joker, - Item::Hit_the_Road, - Item::The_Duo, - Item::The_Trio, - Item::The_Family, - Item::The_Order, - Item::The_Tribe, - Item::Stuntman, - Item::Invisible_Joker, - Item::Brainstorm, - Item::Drivers_License, - Item::Burnt_Joker, -}; - -constexpr inline std::array LEGENDARY_JOKERS = { - Item::Canio, Item::Triboulet, Item::Yorick, Item::Chicot, Item::Perkeo}; - -constexpr inline std::array VOUCHERS = { - Item::Overstock, Item::Overstock_Plus, Item::Clearance_Sale, - Item::Liquidation, Item::Hone, Item::Glow_Up, - Item::Reroll_Surplus, Item::Reroll_Glut, Item::Crystal_Ball, - Item::Omen_Globe, Item::Telescope, Item::Observatory, - Item::Grabber, Item::Nacho_Tong, Item::Wasteful, - Item::Recyclomancy, Item::Tarot_Merchant, Item::Tarot_Tycoon, - Item::Planet_Merchant, Item::Planet_Tycoon, Item::Seed_Money, - Item::Money_Tree, Item::Blank, Item::Antimatter, - Item::Magic_Trick, Item::Illusion, Item::Hieroglyph, - Item::Petroglyph, Item::Directors_Cut, Item::Retcon, - Item::Paint_Brush, Item::Palette}; - -constexpr inline std::array SPECTRALS = { - Item::Familiar, Item::Grim, Item::Incantation, Item::Talisman, - Item::Aura, Item::Wraith, Item::Sigil, Item::Ouija, - Item::Ectoplasm, Item::Immolate, Item::Ankh, Item::Deja_Vu, - Item::Hex, Item::Trance, Item::Medium, Item::Cryptid, - Item::RETRY, // Soul - Item::RETRY // Black_Hole -}; - -constexpr inline std::array TAGS = { - Item::Uncommon_Tag, Item::Rare_Tag, Item::Negative_Tag, - Item::Foil_Tag, Item::Holographic_Tag, Item::Polychrome_Tag, - Item::Investment_Tag, Item::Voucher_Tag, Item::Boss_Tag, - Item::Standard_Tag, Item::Charm_Tag, Item::Meteor_Tag, - Item::Buffoon_Tag, Item::Handy_Tag, Item::Garbage_Tag, - Item::Ethereal_Tag, Item::Coupon_Tag, Item::Double_Tag, - Item::Juggle_Tag, Item::D6_Tag, Item::Top_up_Tag, - Item::Speed_Tag, Item::Orbital_Tag, Item::Economy_Tag}; - -constexpr inline std::array BOSSES = { - Item::The_Arm, Item::The_Club, Item::The_Eye, - Item::Amber_Acorn, Item::Cerulean_Bell, Item::Crimson_Heart, - Item::Verdant_Leaf, Item::Violet_Vessel, Item::The_Fish, - Item::The_Flint, Item::The_Goad, Item::The_Head, - Item::The_Hook, Item::The_House, Item::The_Manacle, - Item::The_Mark, Item::The_Mouth, Item::The_Needle, - Item::The_Ox, Item::The_Pillar, Item::The_Plant, - Item::The_Psychic, Item::The_Serpent, Item::The_Tooth, - Item::The_Wall, Item::The_Water, Item::The_Wheel, - Item::The_Window}; - -#endif \ No newline at end of file diff --git a/immolate/main.cpp b/immolate/main.cpp deleted file mode 100644 index 5467468..0000000 --- a/immolate/main.cpp +++ /dev/null @@ -1,282 +0,0 @@ -#include "functions.hpp" -#include "search.hpp" -#include -#include -#include - -long filter(Instance inst) { - long legendaries = 0; - inst.nextPack(1); - for (int p = 1; p <= 3; p++) { - Pack pack = packInfo(inst.nextPack(1)); - if (pack.type == Item::Arcana_Pack) { - auto packContents = inst.nextArcanaPack(pack.size, 1); - for (int x = 0; x < pack.size; x++) { - if (packContents[x] == Item::The_Soul) - legendaries++; - } - } - if (pack.type == Item::Spectral_Pack) { - auto packContents = inst.nextSpectralPack(pack.size, 1); - for (int x = 0; x < pack.size; x++) { - if (packContents[x] == Item::The_Soul) - legendaries++; - } - } - } - return legendaries; -}; - -long filter_perkeo_observatory(Instance inst) { - if (inst.nextVoucher(1) == Item::Telescope) { - inst.activateVoucher(Item::Telescope); - if (inst.nextVoucher(2) != Item::Observatory) - return 0; - } else - return 0; - int antes[5] = {1, 1, 2, 2, 2}; - for (int i = 0; i < 5; i++) { - Pack pack = packInfo(inst.nextPack(antes[i])); - std::vector packContents; - if (pack.type == Item::Arcana_Pack) { - packContents = inst.nextArcanaPack(pack.size, antes[i]); - } else if (pack.type == Item::Spectral_Pack) { - packContents = inst.nextSpectralPack(pack.size, antes[i]); - } else - continue; - for (int x = 0; x < pack.size; x++) { - if (packContents[x] == Item::The_Soul && - inst.nextJoker(ItemSource::Soul, antes[i], true).joker == - Item::Perkeo) - return 1; - } - } - return 0; -} - -long filter_negative_tag(Instance inst) { - // Note: If the score cutoff was passed as a variable, this code could be - // significantly optimized - int maxAnte = 20; - int score = 0; - for (int i = 2; i <= maxAnte; i++) { - if (inst.nextTag(i) == Item::Negative_Tag) - score++; - } - return score; -} - -long filter_lucky(Instance inst) { - for (int i = 0; i < 7; i++) { - if (inst.random(RandomType::Lucky_Money) >= 1.0/15) { - return 0; - } - } - return 1; -} - -long filter_suas_speedrun(Instance inst) { - // First four cards in shop must include Mr. Bones, Merry Andy, and Luchador - bool bones = false, andy = false, luchador = false; - for (int i = 0; i < 4; i++) { - ShopItem item = inst.nextShopItem(2); - if (item.item == Item::Mr_Bones) - bones = true; - if (item.item == Item::Merry_Andy) - andy = true; - if (item.item == Item::Luchador) - luchador = true; - } - if (!bones || !andy || !luchador) - return 0; - // Ante 1 must have a Coupon Tag - inst.initLocks(1, false, true); - bool coupon = false; - for (int i = 0; i < 2; i++) { - if (inst.nextTag(1) == Item::Coupon_Tag) - coupon = true; - } - if (!coupon) - return 1; - // Ante 2 Boss must be The Wall - inst.nextBoss(1); - inst.initUnlocks(2, false); - if (inst.nextBoss(2) != Item::The_Wall) - return 2; - return 3; -} - -long filter_cavendish(Instance inst) { - inst.initLocks(1, false, false); - // Check for a Charm Tag (Arcana Pack) - if (inst.nextTag(1) != Item::Charm_Tag) - return 0; - // Check for a Judgement within that pack - std::vector packContents = inst.nextArcanaPack(5, 1); - bool hasJudgement = false; - for (int i = 0; i < 5; i++) { - if (packContents[i] == Item::Judgement) - hasJudgement = true; - } - if (!hasJudgement) - return 1; - // Check for Gros Michel - if (inst.nextJoker(ItemSource::Judgement, 1, false).joker != Item::Gros_Michel) - return 2; - // Check for Gros Michel break - if (inst.random(RandomType::Gros_Michel) >= 1.0/6) - return 3; - // Check for Cavendish in first shop - if (inst.nextShopItem(1).item != Item::Cavendish || inst.nextShopItem(1).item != Item::Cavendish) - return 4; - // Check for Cavendish break - if (inst.random(RandomType::Cavendish) < 1.0/1000) - return 9999; - return 5; -} - -long filter_blank(Instance inst) { return 0; } - -// These won't be permanent filters, just ones I sub in and out while JSON -// filters aren't ready yet -long filter_test(Instance inst) { - // Four Fingers, Shortcut, and Smeared Joker in first two antes - // (https://discord.com/channels/1325151824638120007/1326284714125955183) - bool fingers = false; - bool shortcut = false; - bool smeared = false; - // 4 chances in Ante 1, 6 chances in Ante 2, so no rerolling - for (int i = 0; i < 4; i++) { - ShopItem item = inst.nextShopItem(1); - if (item.item == Item::Four_Fingers) { - fingers = true; - }; - if (item.item == Item::Shortcut) { - shortcut = true; - }; - if (item.item == Item::Smeared_Joker) { - smeared = true; - }; - } - for (int i = 0; i < 6; i++) { - ShopItem item = inst.nextShopItem(2); - if (item.item == Item::Four_Fingers) { - fingers = true; - }; - if (item.item == Item::Shortcut) { - shortcut = true; - }; - if (item.item == Item::Smeared_Joker) { - smeared = true; - }; - } - if (fingers && shortcut && smeared) { - return 1; - } - return 0; -} - -// Benchmark function -// Runs 1 billion seeds of perkeo observatory -// And prints total time and seeds per second -void benchmark() { - long total = 0; - long start = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - Search search(filter_perkeo_observatory, "IMMOLATE", 12, 1000000000); - search.highScore = 10; // No output - search.printDelay = 100000000000; - search.search(); - long end = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - std::cout << "------LONGER TESTING------\n"; - std::cout << "Total time: " << end - start << "ms\n"; - std::cout << "Seeds per second: " << std::fixed << std::setprecision(0) - << 1000000000 / ((end - start) / 1000.0) << "\n"; -} - -void benchmark_quick() { - long total = 0; - long start = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - Search search(filter_perkeo_observatory, "IMMOLATE", 12, 100000000); - search.highScore = 10; // No output - search.printDelay = 100000000000; - search.search(); - long end = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - std::cout << "----PERKEO OBSERVATORY----\n"; - std::cout << "Total time: " << end - start << "ms\n"; - std::cout << "Seeds per second: " << std::fixed << std::setprecision(0) - << 100000000 / ((end - start) / 1000.0) << "\n"; -} - -void benchmark_quick_lucky() { - long total = 0; - long start = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - Search search(filter_lucky, "IMMOLATE", 12, 100000000); - search.highScore = 10; // No output - search.printDelay = 100000000000; - search.search(); - long end = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - std::cout << "-------LUCKY CARDS-------\n"; - std::cout << "Total time: " << end - start << "ms\n"; - std::cout << "Seeds per second: " << std::fixed << std::setprecision(0) - << 100000000 / ((end - start) / 1000.0) << "\n"; -} - -void benchmark_single() { - long total = 0; - long start = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - Search search(filter_perkeo_observatory, "IMMOLATE", 1, 10000000); - search.highScore = 10; // No output - search.printDelay = 100000000000; - search.search(); - long end = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - std::cout << "----SINGLE THREADED PO----\n"; - std::cout << "Total time: " << end - start << "ms\n"; - std::cout << "Seeds per second: " << std::fixed << std::setprecision(0) - << 10000000 / ((end - start) / 1000.0) << "\n"; -} - -void benchmark_blank() { - long total = 0; - long start = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - Search search(filter_blank, "IMMOLATE", 12, 100000000); - search.printDelay = 100000000000; // No output - search.search(); - long end = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - std::cout << "-------BLANK FILTER-------\n"; - std::cout << "Total time: " << end - start << "ms\n"; - std::cout << "Seeds per second: " << std::fixed << std::setprecision(0) - << 100000000 / ((end - start) / 1000.0) << "\n"; -} - -int main() { - /*benchmark_single(); - benchmark_quick(); - benchmark_quick_lucky(); - benchmark_blank(); - benchmark();*/ - Search search(filter_cavendish, "11111J31", 8, 2318107019761); - search.highScore = 5; - search.printDelay = 2318107019761; - search.search(); - return 1; -} \ No newline at end of file diff --git a/immolate/rng.cpp b/immolate/rng.cpp deleted file mode 100644 index 6facfb1..0000000 --- a/immolate/rng.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "rng.hpp" - -const std::string ItemSource::Shop = "sho"; -const std::string ItemSource::Emperor = "emp"; -const std::string ItemSource::High_Priestess = "pri"; -const std::string ItemSource::Judgement = "jud"; -const std::string ItemSource::Wraith = "wra"; -const std::string ItemSource::Arcana_Pack = "ar1"; -const std::string ItemSource::Omen_Globe = "ar2"; -const std::string ItemSource::Celestial_Pack = "pl1"; -const std::string ItemSource::Spectral_Pack = "spe"; -const std::string ItemSource::Standard_Pack = "sta"; -const std::string ItemSource::Buffoon_Pack = "buf"; -const std::string ItemSource::Vagabond = "vag"; -const std::string ItemSource::Superposition = "sup"; -const std::string ItemSource::_8_Ball = "8ba"; -const std::string ItemSource::Seance = "sea"; -const std::string ItemSource::Sixth_Sense = "sixth"; -const std::string ItemSource::Top_Up = "top"; -const std::string ItemSource::Rare_Tag = "rta"; -const std::string ItemSource::Uncommon_Tag = "uta"; -const std::string ItemSource::Purple_Seal = "8ba"; -const std::string ItemSource::Soul = "sou"; -const std::string ItemSource::Riff_Raff = "rif"; -const std::string ItemSource::Cartomancer = "car"; - -const std::string RandomType::Joker_Common = "Joker1"; -const std::string RandomType::Joker_Uncommon = "Joker2"; -const std::string RandomType::Joker_Rare = "Joker3"; -const std::string RandomType::Joker_Legendary = "Joker4"; -const std::string RandomType::Joker_Rarity = "rarity"; -const std::string RandomType::Joker_Edition = "edi"; -const std::string RandomType::Misprint = "misprint"; -const std::string RandomType::Standard_Has_Enhancement = "stdset"; -const std::string RandomType::Enhancement = "Enhanced"; -const std::string RandomType::Card = "front"; -const std::string RandomType::Standard_Edition = "standard_edition"; -const std::string RandomType::Standard_Has_Seal = "stdseal"; -const std::string RandomType::Standard_Seal = "stdsealtype"; -const std::string RandomType::Shop_Pack = "shop_pack"; -const std::string RandomType::Tarot = "Tarot"; -const std::string RandomType::Spectral = "Spectral"; -const std::string RandomType::Tags = "Tag"; -const std::string RandomType::Shuffle_New_Round = "nr"; -const std::string RandomType::Card_Type = "cdt"; -const std::string RandomType::Planet = "Planet"; -const std::string RandomType::Lucky_Mult = "lucky_mult"; -const std::string RandomType::Lucky_Money = "lucky_money"; -const std::string RandomType::Sigil = "sigil"; -const std::string RandomType::Ouija = "ouija"; -const std::string RandomType::Wheel_of_Fortune = "wheel_of_fortune"; -const std::string RandomType::Gros_Michel = "gros_michel"; -const std::string RandomType::Cavendish = "cavendish"; -const std::string RandomType::Voucher = "Voucher"; -const std::string RandomType::Voucher_Tag = "Voucher_fromtag"; -const std::string RandomType::Orbital_Tag = "orbital"; -const std::string RandomType::Soul = "soul_"; -const std::string RandomType::Erratic = "erratic"; -const std::string RandomType::Eternal = - "stake_shop_joker_eternal"; // Eternal jokers pre 1.0.1 -const std::string RandomType::Perishable = "ssjp"; -const std::string RandomType::Rental = "ssjr"; -const std::string RandomType::Eternal_Perishable = "etperpoll"; -const std::string RandomType::Rental_Pack = "packssjr"; -const std::string RandomType::Eternal_Perishable_Pack = "packetper"; -const std::string RandomType::Boss = "boss"; -const std::string RandomType::Omen_Globe = "omen_globe"; \ No newline at end of file diff --git a/immolate/rng.hpp b/immolate/rng.hpp deleted file mode 100644 index df4085b..0000000 --- a/immolate/rng.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef RNG_HPP -#define RNG_HPP - -#include - -struct ItemSource { - static const std::string Shop; - static const std::string Emperor; - static const std::string High_Priestess; - static const std::string Judgement; - static const std::string Wraith; - static const std::string Arcana_Pack; - static const std::string Omen_Globe; - static const std::string Celestial_Pack; - static const std::string Spectral_Pack; - static const std::string Standard_Pack; - static const std::string Buffoon_Pack; - static const std::string Vagabond; - static const std::string Superposition; - static const std::string _8_Ball; - static const std::string Seance; - static const std::string Sixth_Sense; - static const std::string Top_Up; - static const std::string Rare_Tag; - static const std::string Uncommon_Tag; - static const std::string Purple_Seal; - static const std::string Soul; - static const std::string Riff_Raff; - static const std::string Cartomancer; -}; - -struct RandomType { - static const std::string Joker_Common; - static const std::string Joker_Uncommon; - static const std::string Joker_Rare; - static const std::string Joker_Legendary; - static const std::string Joker_Rarity; - static const std::string Joker_Edition; - static const std::string Misprint; - static const std::string Standard_Has_Enhancement; - static const std::string Enhancement; - static const std::string Card; - static const std::string Standard_Edition; - static const std::string Standard_Has_Seal; - static const std::string Standard_Seal; - static const std::string Shop_Pack; - static const std::string Tarot; - static const std::string Spectral; - static const std::string Tags; - static const std::string Shuffle_New_Round; - static const std::string Card_Type; - static const std::string Planet; - static const std::string Lucky_Mult; - static const std::string Lucky_Money; - static const std::string Sigil; - static const std::string Ouija; - static const std::string Wheel_of_Fortune; - static const std::string Gros_Michel; - static const std::string Cavendish; - static const std::string Voucher; - static const std::string Voucher_Tag; - static const std::string Orbital_Tag; - static const std::string Soul; - static const std::string Erratic; - static const std::string Eternal; // Eternal jokers pre 1.0.1 - static const std::string Perishable; - static const std::string Rental; - static const std::string Eternal_Perishable; - static const std::string Rental_Pack; - static const std::string Eternal_Perishable_Pack; - static const std::string Boss; - static const std::string Omen_Globe; -}; - -#endif // RNG_HPP \ No newline at end of file diff --git a/immolate/search.cpp b/immolate/search.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/immolate/search.hpp b/immolate/search.hpp deleted file mode 100644 index 4c96329..0000000 --- a/immolate/search.hpp +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef SEARCH_HPP -#define SEARCH_HPP - -#include "instance.hpp" -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - - -const long long BLOCK_SIZE = 1000000; - -class Search { -public: - std::atomic seedsProcessed{0}; - std::atomic highScore{1}; - long long printDelay = 10000000; - std::function filter; - std::atomic found{false}; // Atomic flag to signal when a solution is found - Seed foundSeed; // Store the found seed - bool exitOnFind = false; - long long startSeed; - int numThreads; - long long numSeeds; - std::mutex mtx; - std::atomic nextBlock{0}; // Shared index for the next block to be processed - - Search(std::function f) { - filter = f; - startSeed = 0; - numThreads = 1; - numSeeds = 2318107019761; - } - - Search(std::function f, int t) { - filter = f; - startSeed = 0; - numThreads = t; - numSeeds = 2318107019761; - } - - Search(std::function f, int t, long long n) { - filter = f; - startSeed = 0; - numThreads = t; - numSeeds = n; - }; - Search(std::function f, std::string seed, int t, long long n) { - filter = f; - startSeed = Seed(seed).getID(); - numThreads = t; - numSeeds = n; - }; - - void searchBlock(long long start, long long end) { - Seed s = Seed(start); - Instance inst(s); - for (long long i = start; i < end; ++i) { - if (found) return; // Exit if a solution is found - // Perform the search on the seed - int result = filter(inst); - if (result >= highScore) { - std::lock_guard lock(mtx); - highScore = result; - foundSeed = s; - std::cout << "Found seed: " << s.tostring() << " (" << result << ")" - << std::endl; - if (exitOnFind) { - found = true; - return; - } - } - seedsProcessed++; - if (seedsProcessed % printDelay == 0) { - std::cout << "Seeds processed: " << seedsProcessed << std::endl; - } - inst.next(); - } - } - - std::string search() { - std::vector threads; - long long totalBlocks = (numSeeds + BLOCK_SIZE - 1) / BLOCK_SIZE; - for (int t = 0; t < numThreads; t++) { - threads.emplace_back([this, totalBlocks]() { - while (true) { - long long block = nextBlock.fetch_add(1); - if (block >= totalBlocks) break; - long long start = block * BLOCK_SIZE + startSeed; - long long end = std::min(start + BLOCK_SIZE, numSeeds + startSeed); - searchBlock(start, end); - } - }); - } - - for (auto& thread : threads) { - thread.join(); - } - - return foundSeed.tostring(); - } -}; - -#endif \ No newline at end of file diff --git a/immolate/seed.cpp b/immolate/seed.cpp deleted file mode 100644 index 0595dba..0000000 --- a/immolate/seed.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include "seed.hpp" -#include "util.hpp" -#include - -Seed::Seed() { - seed.fill(-1); - length = 0; - for (int i = 0; i < 8; i++) { - cache[i].fill(-1); - } -} - -Seed::Seed(std::string strSeed) { - seed.fill(-1); - length = strSeed.size(); - for (int i = 0; i < 8; i++) { - cache[i].fill(-1); - } - // Note: Assumes this is safe - for (long unsigned int i = 0; i < strSeed.size(); i++) { - seed[strSeed.size() - 1 - i] = charSeeds[strSeed[i]]; - } -} - -Seed::Seed(long long id) { - length = 0; - for (int i = 0; i < 8; i++) { - cache[i].fill(-1); - } - for (int i = 0; i < 8; i++) { - if (id > 0) { - length++; - seed[i] = (id - 1) / idCoeff[i]; - id -= 1 + seed[i] * idCoeff[i]; - } else { - seed[i] = -1; - } - } -} - -std::string Seed::tostring() { - std::string strSeed; - for (int i = 7; i >= 0; i--) { - if (seed[i] != -1) { - strSeed.push_back(seedChars[seed[i]]); - } - } - return strSeed; -} - -void Seed::debugprint() { - for (int i = 0; i < 8; i++) { - std::cout << seed[i] << " "; - } - std::cout << std::endl; -} - -long long Seed::getID() { - long long id = 0; - for (int i = 0; i <= 7; i++) { - if (seed[i] >= 0) { - id += idCoeff[i] * seed[i] + 1; - } - } - return id; -} - -void Seed::next() { - if (length < 8) { - seed[length] = 0; - length++; - } else { - int i = 7; - while (i >= 0) { - cache[i].fill(-1); - if (seed[i] == 34) { - seed[i] = -1; - length--; - } else { - seed[i]++; - break; - } - i--; - } - } -} - -// Not optimized for performance -// I don't think this will need to be implemented in searching -void Seed::next(int x) { - long long newID = (getID() + x) % 2318107019761; - *this = Seed(newID); -} - -double Seed::pseudohash(int prefixLength) { - if (length == 0) return 1; //Empty seed edge case - - if (cache[length-1][prefixLength+length-1] == -1) { - int i = length - 2; - while (i >= 0 && cache[i][prefixLength+length-1] == -1) { - i--; - } - if (i == -1) { - cache[0][prefixLength+length-1] = pseudostep(seedChars[seed[0]], prefixLength+length, 1); - i = 0; - } - for (int j = i+1; j < length; j++) { - cache[j][prefixLength+length-1] = pseudostep(seedChars[seed[j]], prefixLength+length-j, cache[j-1][prefixLength+length-1]); - } - } - return cache[length-1][prefixLength+length-1]; -} \ No newline at end of file diff --git a/immolate/seed.hpp b/immolate/seed.hpp deleted file mode 100644 index 8e184be..0000000 --- a/immolate/seed.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef SEED_HPP -#define SEED_HPP - -#include -#include - -// Seed helper class -// Caches hashing info recursively to save speed -// Because of that, also has an interesting order for seeds: -// , 1, 11, 111, ..., 11111111, 21111111, 31111111, ..., Z1111111, -// 2111111, 12111111, ..., ZZ111111, 211111, ..., ZZZZZZZZ -const std::string seedChars = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -const std::array charSeeds = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, - 8, -1, -1, -1, -1, -1, -1, -1, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -}; -const std::array idCoeff = { - 66231629136, 1892332261, 54066636, 1544761, 44136, 1261, 36, 1}; - -struct Seed { - // -1 is blank, 0 to 34 represent valid characters - // To aid in hashing, stored right to left - std::array seed; - - int length; - - // The cache. Stored as [position in seed][length of string] - std::array, 8> cache; - - Seed(); - Seed(std::string strSeed); - Seed(long long id); - - std::string tostring(); - void debugprint(); - long long getID(); - - void next(); - void next(int x); - - double pseudohash(int prefixLength); -}; - -#endif // SEED_HPP \ No newline at end of file diff --git a/immolate/util.cpp b/immolate/util.cpp deleted file mode 100644 index 82df8f6..0000000 --- a/immolate/util.cpp +++ /dev/null @@ -1,178 +0,0 @@ -#include "util.hpp" -#include -#include - -LuaRandom::LuaRandom(double seed) { - double d = seed; - uint64_t r = 0x11090601; - for (int i = 0; i < 4; i++) { - uint64_t m = 1ull << (r & 255); - r >>= 8; - d = d * 3.14159265358979323846 + 2.7182818284590452354; - dbllong u; - u.dbl = d; - if (u.ulong < m) - u.ulong += m; - state[i] = u.ulong; - } - for (int i = 0; i < 10; i++) { - _randint(); - } -} - -LuaRandom::LuaRandom() { LuaRandom(0); } - -uint64_t LuaRandom::_randint() { - uint64_t z = 0; - uint64_t r = 0; - z = state[0]; - z = (((z << 31ull) ^ z) >> 45ull) ^ ((z & (MAX_UINT64 << 1ull)) << 18ull); - r ^= z; - state[0] = z; - z = state[1]; - z = (((z << 19ull) ^ z) >> 30ull) ^ ((z & (MAX_UINT64 << 6ull)) << 28ull); - r ^= z; - state[1] = z; - z = state[2]; - z = (((z << 24ull) ^ z) >> 48ull) ^ ((z & (MAX_UINT64 << 9ull)) << 7ull); - r ^= z; - state[2] = z; - z = state[3]; - z = (((z << 21ull) ^ z) >> 39ull) ^ ((z & (MAX_UINT64 << 17ull)) << 8ull); - r ^= z; - state[3] = z; - return r; -} - -uint64_t LuaRandom::randdblmem() { - return (_randint() & 4503599627370495ull) | 4607182418800017408ull; -} - -double LuaRandom::random() { - dbllong u; - u.ulong = randdblmem(); - return u.dbl - 1.0; -} - -int LuaRandom::randint(int min, int max) { - return (int)(random() * (max - min + 1)) + min; -} - -int portable_clzll(uint64_t x) { - if (x == 0) - return 64; // Undefined for 0, by convention we return 64 - -#if defined(__GNUC__) || defined(__clang__) - return __builtin_clzll(x); -#elif defined(_MSC_VER) - unsigned long index; - if (_BitScanReverse64(&index, x)) { - return 63 - index; - } - return 64; -#else - // Fallback for other compilers (manual bit manipulation) - int n = 0; - if (x <= 0x00000000FFFFFFFF) { - n += 32; - x <<= 32; - } - if (x <= 0x0000FFFFFFFFFFFF) { - n += 16; - x <<= 16; - } - if (x <= 0x00FFFFFFFFFFFFFF) { - n += 8; - x <<= 8; - } - if (x <= 0x0FFFFFFFFFFFFFFF) { - n += 4; - x <<= 4; - } - if (x <= 0x3FFFFFFFFFFFFFFF) { - n += 2; - x <<= 2; - } - if (x <= 0x7FFFFFFFFFFFFFFF) { - n += 1; - } - return n; -#endif -} - -double fract(double x) { - uint64_t x_int; - - std::memcpy(&x_int, &x, sizeof(x_int)); - - uint64_t expo = (x_int & DBL_EXPO) >> DBL_MANT_SZ; - if (expo < DBL_EXPO_BIAS) { - return x; - } - if (expo == ((1 << DBL_EXPO_SZ) - 1)) { - return std::numeric_limits::quiet_NaN(); - } - uint64_t expo_biased = expo - DBL_EXPO_BIAS; - if (expo_biased >= DBL_MANT_SZ) { - return 0; - } - uint64_t mant = x_int & DBL_MANT; - uint64_t frac_mant = mant & ((1ull << (DBL_MANT_SZ - expo_biased)) - 1); - if (frac_mant == 0) { - return 0; - } - uint64_t frac_lzcnt = portable_clzll(frac_mant) - (64 - DBL_MANT_SZ); - uint64_t res_expo = (expo - frac_lzcnt - 1) << DBL_MANT_SZ; - uint64_t res_mant = (frac_mant << (frac_lzcnt + 1)) & DBL_MANT; - uint64_t res = res_expo | res_mant; - - double result; - std::memcpy(&result, &res, sizeof(result)); - return result; -} - -double pseudohash(std::string s) { - double num = 1; - for (size_t i = s.length(); i > 0; i--) { - num = fract(1.1239285023 / num * s[i - 1] * 3.141592653589793116 + - 3.141592653589793116 * i); - } - return num; -} - -double pseudohash_from(std::string s, double num) { - for (size_t i = s.length(); i > 0; i--) { - num = fract(1.1239285023 / num * s[i - 1] * 3.141592653589793116 + - 3.141592653589793116 * i); - } - return num; -} - -double pseudostep(char s, int pos, double num) { - return fract(1.1239285023 / num * s * 3.141592653589793116 + - 3.141592653589793116 * pos); -} - -std::string anteToString(int a) { - if (a < 10) - return {(char)(0x30 + a)}; - else - return {(char)(0x30 + a / 10), (char)(0x30 + a % 10)}; -} - -const double inv_prec = std::pow(10.0, 13); -const double two_inv_prec = std::pow(2.0, 13); -const double five_inv_prec = std::pow(5.0, 13); - -double round13(double x) { - double normal_case = std::round(x * inv_prec) / inv_prec; - if (normal_case == - (std::round(std::nextafter(x, -1) * inv_prec) / inv_prec)) { - return normal_case; - } - double truncated = fract(x * two_inv_prec) * five_inv_prec; - if (fract(truncated) >= 0.5) { - return (std::floor(x * inv_prec) + 1) / inv_prec; - } - return std::floor(x * inv_prec) / inv_prec; -} \ No newline at end of file diff --git a/immolate/util.hpp b/immolate/util.hpp deleted file mode 100644 index 4fbbe4e..0000000 --- a/immolate/util.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef UTIL_HPP -#define UTIL_HPP - -#include -#include -#include - -const uint64_t MAX_UINT64 = 18446744073709551615ull; - -typedef union DoubleLong { - double dbl; - uint64_t ulong; -} dbllong; - -struct LuaRandom { - uint64_t state[4]; - LuaRandom(double seed); - LuaRandom(); - uint64_t _randint(); - uint64_t randdblmem(); - double random(); - int randint(int min, int max); -}; - -#define DBL_EXPO 0x7FF0000000000000 -#define DBL_MANT 0x000FFFFFFFFFFFFF - -#define DBL_EXPO_SZ 11 -#define DBL_MANT_SZ 52 - -#define DBL_EXPO_BIAS 1023 - -#if defined(_MSC_VER) -#include -#pragma intrinsic(_BitScanReverse64) -#endif - -int portable_clzll(uint64_t x); -double fract(double x); -double pseudohash(std::string s); -double pseudohash_from(std::string s, double num); -double pseudostep(char s, int pos, double num); -std::string anteToString(int a); -double round13(double x); - -#endif // UTIL_HPP \ No newline at end of file From b28a0e3ef77409e87e4d6cad27dcd02c5adec4ab Mon Sep 17 00:00:00 2001 From: OceanRamen Date: Sun, 14 Jun 2026 02:36:06 +0100 Subject: [PATCH 3/5] Add core functionality for the Immolate project - Implement main logic in main.cpp, including various filters for game mechanics. - Introduce RNG functionality in rng.cpp and rng.hpp to handle random number generation. - Create search functionality in search.cpp and search.hpp for efficient seed searching. - Develop seed management in seed.cpp and seed.hpp to handle seed representation and manipulation. - Add utility functions in util.cpp and util.hpp for mathematical operations and random number handling. - Establish a benchmark system to evaluate performance across different filters and configurations. --- .github/copilot-instructions.md | 25 + .github/ui_modding.md | 136 ++ immolate/functions.cpp | 11 + immolate/functions.hpp | 639 ++++++ immolate/immolate.cpp | 246 +++ immolate/immolate.hpp | 143 ++ immolate/instance.hpp | 135 ++ immolate/items.cpp | 356 +++ immolate/items.hpp | 3578 +++++++++++++++++++++++++++++++ immolate/main.cpp | 282 +++ immolate/rng.cpp | 67 + immolate/rng.hpp | 75 + immolate/search.cpp | 0 immolate/search.hpp | 111 + immolate/seed.cpp | 112 + immolate/seed.hpp | 49 + immolate/util.cpp | 178 ++ immolate/util.hpp | 46 + 18 files changed, 6189 insertions(+) create mode 100644 .github/copilot-instructions.md create mode 100644 .github/ui_modding.md create mode 100644 immolate/functions.cpp create mode 100644 immolate/functions.hpp create mode 100644 immolate/immolate.cpp create mode 100644 immolate/immolate.hpp create mode 100644 immolate/instance.hpp create mode 100644 immolate/items.cpp create mode 100644 immolate/items.hpp create mode 100644 immolate/main.cpp create mode 100644 immolate/rng.cpp create mode 100644 immolate/rng.hpp create mode 100644 immolate/search.cpp create mode 100644 immolate/search.hpp create mode 100644 immolate/seed.cpp create mode 100644 immolate/seed.hpp create mode 100644 immolate/util.cpp create mode 100644 immolate/util.hpp diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..14cb8a5 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,25 @@ +# Brainstorm Copilot Instructions + +- **Purpose**: Balatro mod that automates rerolls to find seeds matching user-selected filters (tags, vouchers, packs) and restarts runs accordingly. Main logic lives in [Core/Brainstorm.lua](Core/Brainstorm.lua); a working options tab is in [UI/ui.lua](UI/ui.lua); older prototypes live under [Debug](Debug). +- **Load flow**: The mod is injected via [lovely.toml](lovely.toml): `nativefs.lua` is loaded before `main.lua`, `Core/Brainstorm.lua` is appended to `main.lua`, and `Brainstorm.init()` runs after the profile load patch in `game.lua`. +- **Globals & patching**: `Brainstorm.init()` discovers its own path, loads config, and executes `UI/ui.lua`. Core behaviors monkeypatch `Controller:key_press_update`, `Game:update`, and `create_UIBox_round_scores_row` while calling the originals; keep these reference patterns when extending patches. +- **Config storage**: Defaults sit in `Brainstorm.DEFAULT_CONFIG` and are serialized to `config.lua` via `STR_PACK/STR_UNPACK` using `nativefs`. Config load merges defaults and drops dead fields (e.g., the old `keybind_autoreroll`); when adding settings, seed defaults, keep backward compatibility, and call `Brainstorm.writeConfig()` (UI exit already does this). +- **Controls**: Holding the modifier (`lctrl` by default) + `r` triggers a manual reroll; modifier + `a` toggles auto-reroll. `keybind_autoreroll` is a plain `love.keyboard.isDown` check; respect current bindings when changing input logic. +- **Manual reroll path**: `Brainstorm.reroll()` deletes and restarts the run with the current stake/seed/challenge context, preserving seeded/challenge flags on `G.GAME`. Keep these assignments if you alter reroll behavior. +- **Auto-reroll loop**: `Game:update` drives `Brainstorm.autoReroll()` on a dynamic interval derived from `ar_prefs.spf_int` (1000 ⇒ 0.01s, 500 ⇒ 0.02s) while `Brainstorm.ar_active` is true; a status text is shown after 60 frames via `Brainstorm.attentionText()` and cleared in `removeAttentionText()`. +- **Seed search implementation**: `Brainstorm.autoReroll()` FFI-loads `Immolate.dll` and calls `brainstorm(seed, pack, tag, souls)` (DLL ignores voucher/observatory/perkeo in current builds); keep the Lua `ffi.cdef` in sync with the DLL you ship. It writes `G.GAME.used_filter` and `filter_info` for downstream display; keep these fields if you change the flow. If the DLL is missing or lacks the symbol, auto-reroll now disables itself instead of crashing. +- **Immolate source**: The C++ sources live in [immolate/](immolate); `immolate.cpp` wires globals `BRAINSTORM_PACK/TAG/SOULS` into a `Search` over seeds and exports `brainstorm`/`brainstorm_cpp` plus `free_result`. Search walks blocks of 1,000,000 seeds, is multi-threaded, and stops on the first seed meeting the filter when `exitOnFind` is true. Filters currently check first-pack type, first-tag match, and require N souls from the first Mega Arcana pack; add new filters there if the DLL needs to evolve. +- **DLL version drift**: The shipped `Immolate.dll` may be older than the sources under [immolate/](immolate). Treat the C++ tree as reference, but verify exports/signatures against the actual DLL before changing `ffi.cdef` or call sites. +- **Filters & prefs**: Search options live under `Brainstorm.config.ar_filters` (pack, tag, voucher ids/names, soul skips, instant observatory/perkeo) and `ar_prefs` (seeds-per-frame). UI callbacks in [UI/ui.lua](UI/ui.lua) update these and immediately persist. +- **UI integration**: `create_tabs` is patched to append a "Brainstorm" tab (only when `tab_h == 7.05`, matching the options screen). It uses base-game helpers (`create_option_cycle`, `create_toggle`) to render cycles/toggles. `G.FUNCS.exit_overlay_menu` is wrapped to save config on close. +- **Display tweaks**: `create_UIBox_round_scores_row` colors the seed label red when seeded and blue when a filtered seed was used; preserve this branch if altering round score rows. +- **Vendored dependencies**: [nativefs.lua](nativefs.lua) is a bundled filesystem shim (LuaJIT+FFI); avoid modifications unless fixing I/O. [Immolate.dll](Immolate.dll) is an external binary providing the seed search routine. [steamodded_compat.lua](steamodded_compat.lua) only carries mod metadata. +- **Styling & formatting**: Lua uses Stylua ([stylua.toml](stylua.toml), [\.vscode/settings.json](.vscode/settings.json)) with 2-space indents, 80-col preference, and double-quote bias. See [style_guide.md](style_guide.md) for naming (snake_case vars, camelCase functions, PascalCase classes) and general conventions. +- **Adding settings**: 1) Extend `Brainstorm.config` defaults. 2) Wire UI controls in [UI/ui.lua](UI/ui.lua) with `G.FUNCS.change_*` callbacks that update config and call `Brainstorm.writeConfig()`. 3) Ensure new values are passed into `autoReroll()` if they influence search. +- **Touching globals**: Many helpers assume global tables (`G`, `G.FUNCS`, `G.UIT`). Cache originals before wrapping and re-call them to avoid breaking base game behavior. +- **Debug folder**: [Debug/settings.lua](Debug/settings.lua) and [Debug/ui.lua](Debug/ui.lua) are experimental layouts; treat them as reference prototypes, not the active UI. +- **Testing workflow**: No automated tests. Validation is manual by running Balatro/Steamodded with this mod enabled; exercise keybinds and the "Brainstorm" options tab to confirm config persistence and reroll behavior. +- **Release/versioning**: Current version string is `Brainstorm v2.2.0-alpha` in `Brainstorm.VERSION`; update this and `lovely.toml`/headers if you ship releases. +- **Safety tips**: Avoid touching `config.lua` by hand; prefer the UI or defaults. Keep non-ASCII out of sources. Do not remove calls to `Brainstorm.writeConfig()` around UI exits. + +If any part of this feels unclear or incomplete, tell me which sections to expand or examples to add. diff --git a/.github/ui_modding.md b/.github/ui_modding.md new file mode 100644 index 0000000..7118845 --- /dev/null +++ b/.github/ui_modding.md @@ -0,0 +1,136 @@ +# Balatro UI Modding Guide + +This document explains how to build custom UI using Balatro’s built-in UI engine, with a focus on creating custom settings tabs and reusable controls. + +## Mental model + +- The UI is a tree of nodes built from plain Lua tables. You pass that tree into `UIBox{definition=..., config=...}` to instantiate it. +- Node types are enumerated in `G.UIT` (see `globals.lua`): + - `T` text, `B` box, `C` column, `R` row, `O` object (Sprite/DynaText/etc.), `ROOT`, `S` slider, `I` input. +- Every node table uses keys: + - `n` (required): the node type from `G.UIT.*`. + - `config` (optional): alignment, padding, colors, callbacks, refs, ids. + - `nodes` (optional): array of child nodes. +- Layout is resolved by `UIElement:set_alignments` (engine/ui.lua). `config.align` letters: `c` center vertically, `m` center horizontally, `b` bottom, `r` right. Top/left are default. `padding` defaults to `G.UIT.padding`. +- `ref_table` + `ref_value` binds UI to live data; text and object refs auto-refresh when values change. +- Interactivity uses `config.button = 'name'` to invoke `G.FUNCS.name`. Helpers wire `hover`, `shadow`, etc. for you. + +## Useful engine locations + +- Node enums: `globals.lua` (`self.UIT = { T=1, B=2, C=3, R=4, O=5, ROOT=7, S=8, I=9 }`). +- UIBox creation, sizing, alignment: `engine/ui.lua` (class `UIBox`, `UIElement`). +- Input/callbacks for buttons, sliders, toggles, option cycles: `functions/button_callbacks.lua`. +- Prefab builders (sliders, toggles, option cycles, tabs, buttons): `functions/UI_definitions.lua` near the helper definitions. +- Settings tabs pattern: `create_UIBox_settings` and `G.UIDEF.settings_tab` in `functions/UI_definitions.lua`. + +## Core building blocks (prefabs) + +Use these helpers instead of raw nodes where possible: + +- **Button**: `UIBox_button(args)` → clickable pill. + + - Args: `button` (G.FUNCS name), `label` array, `colour`, `minw/minh`, `choice/chosen` for toggle-style buttons, `focus_args` for controller nav. + +- **Slider**: `create_slider(args)` → drag or discrete slider bound to `ref_table/ref_value`. + + - Required: `ref_table`, `ref_value`, `min`, `max` (numbers). Optional: `label`, `w/h`, `callback`, `decimal_places`, `colour`. + - Behavior implemented by `G.FUNCS.slider` and `G.FUNCS.slider_descreet`. + +- **Toggle**: `create_toggle(args)` → checkbox-style toggle bound to `ref_table/ref_value`. + + - Required: `ref_table`, `ref_value`, `label`. + - Optional: `callback` (runs on change), `info` (array of extra text lines), `active_colour`, `inactive_colour`, `scale`. + - Behavior: `G.FUNCS.toggle_button` and `G.FUNCS.toggle`. + +- **Option Cycle**: `create_option_cycle(args)` → left/right cycle with pips. + + - Required: `options` (array), `current_option` (1-based), `opt_callback` (G.FUNCS name or nil). + - Optional: `label`, `info`, `w/h`, `scale`, `cycle_shoulders` (adds shoulder prompts), `no_pips`. + - Behavior: `G.FUNCS.option_cycle` updates `current_option` and `current_option_val`, fires `opt_callback`. + +- **Tabs**: `create_tabs(args)` → tab strip + content area. + + - Each tab entry: `{ label=..., chosen=bool, tab_definition_function=fn, tab_definition_function_args=... }`. + - `create_tabs` instantiates the chosen tab’s definition into `tab_contents`. + - Useful args: `tab_h`, `tab_w`, `tab_alignment`, `snap_to_nav`, `no_shoulders`. + +- **Overlay shell**: `create_UIBox_generic_options(args)` → modal frame with optional back button and infotip slot. + + - Args: `contents` (array or single node), `back_func`, `colour`, `bg_colour`, `outline_colour`, `no_back`, `snap_back`. + +- **Dyn container**: `UIBox_dyn_container(inner_table, horizontal, colour_override, background_override, flipped, padding)` → framed grouping block. + +- **Text input**: `create_text_input(args)` for simple keyboard input; binds to `ref_table/ref_value` with max length, prompt text. + +## Making a custom settings tab (example) + +Add a new tab alongside existing ones in `create_UIBox_settings` and define its builder. Example: + +```lua +-- 1) Define your tab builder (anywhere after G.UIDEF exists) +function G.UIDEF.settings_tab_fancy() + return {n=G.UIT.ROOT, config={align="cm", padding=0.05, colour=G.C.CLEAR}, nodes={ + create_toggle({label="Enable Fancy Mode", ref_table=G.SETTINGS, ref_value="fancy_mode", callback=function(val) + G.FUNCS.apply_fancy_mode(val) + end}), + create_slider({label="Fancy Intensity", w=4, h=0.4, ref_table=G.SETTINGS, ref_value="fancy_intensity", min=0, max=100, callback="apply_fancy_intensity"}), + create_option_cycle({label="Fancy Style", options={"Soft","Bold","Loud"}, current_option=1, opt_callback="set_fancy_style"}) + }} +end + +-- 2) Insert the tab into the settings tabs list (in create_UIBox_settings) +tabs[#tabs+1] = { + label = "Fancy", + tab_definition_function = G.UIDEF.settings_tab_fancy, + tab_definition_function_args = nil +} +``` + +Then implement the callbacks you referenced (e.g., `G.FUNCS.apply_fancy_mode`, `G.FUNCS.apply_fancy_intensity`, `G.FUNCS.set_fancy_style`). They’ll receive the cycle/slider/toggle configs or values per the existing callbacks in `button_callbacks.lua`. + +## Making a standalone modal/panel + +1. Build your content nodes using rows/cols and prefabs: + +```lua +local content = { + UIBox_button({label={"Do Thing"}, button="my_action", minw=3}), + create_toggle({label="Flag", ref_table=G.SETTINGS, ref_value="my_flag"}), + create_slider({label="Value", w=4, h=0.4, ref_table=G.SETTINGS, ref_value="my_val", min=0, max=10}) +} +``` + +1. Wrap in `create_UIBox_generic_options({contents = content, back_func = "exit_overlay_menu"})` and pass that as the `definition` to a new `UIBox` to show the overlay. + +## Binding data and IDs + +- Use `ref_table/ref_value` on `T` nodes to auto-update text when values change. +- Use `id` in `config` to fetch elements later with `UIBox:get_UIE_by_ID(id)`. +- Objects (`n=G.UIT.O`) can wrap `Sprite`, `DynaText`, or another `UIBox` via `config.object`. + +## Controller focus + +- `focus_args` on interactive nodes controls navigation; helpers set sensible defaults: sliders (`type='slider'`), cycles (`type='cycle'`), tabs (`type='tab'`), buttons (`nav='wide'`, etc.). +- `snap_to_nav=true` on `create_tabs` helps initial focus within overlays. + +## Gotchas + +- Buttons need `hover=true` (helpers do this) and a `button` string to trigger a callback. +- If you bind text to changing data, a length change triggers a layout recalc; avoid `no_recalc` unless you really need fixed width. +- `id` values must be unique within a UIBox tree. +- Colors are premultiplied alpha tables; `colour[4]` near zero hides the element. + +## Where to look in code + +- Prefab helpers: `functions/UI_definitions.lua` (slider/toggle/cycle/tabs/buttons). +- Input + callbacks: `functions/button_callbacks.lua`. +- Core UI tree + layout: `engine/ui.lua`. +- Node enums/constants: `globals.lua` (G.UIT, colors in G.C). + +## Extending further + +- You can nest `UIBox` instances via `n=G.UIT.O` with `config.object = UIBox{definition=...}` to embed sub-UIs. +- `UIBox_dyn_container` gives quick framed blocks for grouped options. +- Use `create_text_input` if you need player text entry (e.g., seeds, names). + +Keep everything data-first: assemble tables, wire callbacks in `G.FUNCS`, and let the engine handle layout and interaction. diff --git a/immolate/functions.cpp b/immolate/functions.cpp new file mode 100644 index 0000000..11ee471 --- /dev/null +++ b/immolate/functions.cpp @@ -0,0 +1,11 @@ +#include "functions.hpp" + +std::vector PACK_INFO = { + Pack(Item::Arcana_Pack, 3, 1), Pack(Item::Arcana_Pack, 5, 1), + Pack(Item::Arcana_Pack, 5, 2), Pack(Item::Celestial_Pack, 3, 1), + Pack(Item::Celestial_Pack, 5, 1), Pack(Item::Celestial_Pack, 5, 2), + Pack(Item::Standard_Pack, 3, 1), Pack(Item::Standard_Pack, 5, 1), + Pack(Item::Standard_Pack, 5, 2), Pack(Item::Buffoon_Pack, 2, 1), + Pack(Item::Buffoon_Pack, 4, 1), Pack(Item::Buffoon_Pack, 4, 2), + Pack(Item::Spectral_Pack, 2, 1), Pack(Item::Spectral_Pack, 4, 1), + Pack(Item::Spectral_Pack, 4, 2)}; diff --git a/immolate/functions.hpp b/immolate/functions.hpp new file mode 100644 index 0000000..f9599bd --- /dev/null +++ b/immolate/functions.hpp @@ -0,0 +1,639 @@ +#ifndef FUNCTIONS_HPP +#define FUNCTIONS_HPP + +#include "instance.hpp" +#include "rng.hpp" +#include + +// Note: Technically, marking everything as inline is not a proper fix. Ideally, +// we'd place these correctly into hpp and cpp files BUT, i want to have sanity + +// Helper functions +inline void Instance::lock(Item item) { locked[(int)item] = true; } +inline void Instance::unlock(Item item) { locked[(int)item] = false; } +inline bool Instance::isLocked(Item item) { return locked[(int)item]; } + +// Lock initializers +inline void Instance::initLocks(int ante, bool freshProfile, bool freshRun) { + if (ante < 2) { + lock(Item::The_Mouth); + lock(Item::The_Fish); + lock(Item::The_Wall); + lock(Item::The_House); + lock(Item::The_Mark); + lock(Item::The_Wheel); + lock(Item::The_Arm); + lock(Item::The_Water); + lock(Item::The_Needle); + lock(Item::The_Flint); + lock(Item::Negative_Tag); + lock(Item::Standard_Tag); + lock(Item::Meteor_Tag); + lock(Item::Buffoon_Tag); + lock(Item::Handy_Tag); + lock(Item::Garbage_Tag); + lock(Item::Ethereal_Tag); + lock(Item::Top_up_Tag); + lock(Item::Orbital_Tag); + } + if (ante < 3) { + lock(Item::The_Tooth); + lock(Item::The_Eye); + } + if (ante < 4) { + lock(Item::The_Plant); + } + if (ante < 5) { + lock(Item::The_Serpent); + } + if (ante < 6) { + lock(Item::The_Ox); + } + if (freshProfile) { + // Tags + lock(Item::Negative_Tag); + lock(Item::Foil_Tag); + lock(Item::Holographic_Tag); + lock(Item::Polychrome_Tag); + lock(Item::Rare_Tag); + + // Jokers + lock(Item::Golden_Ticket); + lock(Item::Mr_Bones); + lock(Item::Acrobat); + lock(Item::Sock_and_Buskin); + lock(Item::Swashbuckler); + lock(Item::Troubadour); + lock(Item::Certificate); + lock(Item::Smeared_Joker); + lock(Item::Throwback); + lock(Item::Hanging_Chad); + lock(Item::Rough_Gem); + lock(Item::Bloodstone); + lock(Item::Arrowhead); + lock(Item::Onyx_Agate); + lock(Item::Glass_Joker); + lock(Item::Showman); + lock(Item::Flower_Pot); + lock(Item::Blueprint); + lock(Item::Wee_Joker); + lock(Item::Merry_Andy); + lock(Item::Oops_All_6s); + lock(Item::The_Idol); + lock(Item::Seeing_Double); + lock(Item::Matador); + lock(Item::Hit_the_Road); + lock(Item::The_Duo); + lock(Item::The_Trio); + lock(Item::The_Family); + lock(Item::The_Order); + lock(Item::The_Tribe); + lock(Item::Stuntman); + lock(Item::Invisible_Joker); + lock(Item::Brainstorm); + lock(Item::Satellite); + lock(Item::Shoot_the_Moon); + lock(Item::Drivers_License); + lock(Item::Cartomancer); + lock(Item::Astronomer); + lock(Item::Burnt_Joker); + lock(Item::Bootstraps); + + // Vouchers + lock(Item::Overstock_Plus); + lock(Item::Liquidation); + lock(Item::Glow_Up); + lock(Item::Reroll_Glut); + lock(Item::Omen_Globe); + lock(Item::Observatory); + lock(Item::Nacho_Tong); + lock(Item::Recyclomancy); + lock(Item::Tarot_Tycoon); + lock(Item::Planet_Tycoon); + lock(Item::Money_Tree); + lock(Item::Antimatter); + lock(Item::Illusion); + lock(Item::Petroglyph); + lock(Item::Retcon); + lock(Item::Palette); + } + + // Locked in start of run + if (freshRun) { + // Require hand discoveries + lock(Item::Planet_X); + lock(Item::Ceres); + lock(Item::Eris); + lock(Item::Five_of_a_Kind); + lock(Item::Flush_House); + lock(Item::Flush_Five); + + // Requires specific card enhancement + lock(Item::Stone_Joker); // Stone + lock(Item::Steel_Joker); // Steel + lock(Item::Glass_Joker); // Glass + lock(Item::Golden_Ticket); // Gold + lock(Item::Lucky_Cat); // Lucky + + // Requires Gros Michel death + lock(Item::Cavendish); + + // Vouchers + lock(Item::Overstock_Plus); + lock(Item::Liquidation); + lock(Item::Glow_Up); + lock(Item::Reroll_Glut); + lock(Item::Omen_Globe); + lock(Item::Observatory); + lock(Item::Nacho_Tong); + lock(Item::Recyclomancy); + lock(Item::Tarot_Tycoon); + lock(Item::Planet_Tycoon); + lock(Item::Money_Tree); + lock(Item::Antimatter); + lock(Item::Illusion); + lock(Item::Petroglyph); + lock(Item::Retcon); + lock(Item::Palette); + } +} +inline void Instance::initUnlocks(int ante, bool freshProfile) { + if (ante == 2) { + unlock(Item::The_Mouth); + unlock(Item::The_Fish); + unlock(Item::The_Wall); + unlock(Item::The_House); + unlock(Item::The_Mark); + unlock(Item::The_Wheel); + unlock(Item::The_Arm); + unlock(Item::The_Water); + unlock(Item::The_Needle); + unlock(Item::The_Flint); + if (!freshProfile) + unlock(Item::Negative_Tag); + unlock(Item::Standard_Tag); + unlock(Item::Meteor_Tag); + unlock(Item::Buffoon_Tag); + unlock(Item::Handy_Tag); + unlock(Item::Garbage_Tag); + unlock(Item::Ethereal_Tag); + unlock(Item::Top_up_Tag); + unlock(Item::Orbital_Tag); + } + if (ante == 3) { + unlock(Item::The_Tooth); + unlock(Item::The_Eye); + } + if (ante == 4) { + unlock(Item::The_Plant); + } + if (ante == 5) { + unlock(Item::The_Serpent); + } + if (ante == 6) { + unlock(Item::The_Ox); + } +} + +// Card Generators +inline Item Instance::nextTarot(std::string source, int ante, bool soulable) { + std::string anteStr = anteToString(ante); + if (soulable && (params.showman || !isLocked(Item::The_Soul)) && + random(RandomType::Soul + RandomType::Tarot + anteStr) > 0.997) { + return Item::The_Soul; + } + return randchoice(RandomType::Tarot + source + anteStr, TAROTS); +} + +inline Item Instance::nextPlanet(std::string source, int ante, bool soulable) { + std::string anteStr = anteToString(ante); + if (soulable && (params.showman || !isLocked(Item::Black_Hole)) && + random(RandomType::Soul + RandomType::Planet + anteStr) > 0.997) { + return Item::Black_Hole; + } + return randchoice(RandomType::Planet + source + anteStr, PLANETS); +} + +inline Item Instance::nextSpectral(std::string source, int ante, + bool soulable) { + std::string anteStr = anteToString(ante); + if (soulable) { + Item forcedKey = Item::RETRY; + if ((params.showman || !isLocked(Item::The_Soul)) && + random(RandomType::Soul + RandomType::Spectral + anteStr) > 0.997) + forcedKey = Item::The_Soul; + if ((params.showman || !isLocked(Item::Black_Hole)) && + random(RandomType::Soul + RandomType::Spectral + anteStr) > 0.997) + forcedKey = Item::Black_Hole; + if (forcedKey != Item::RETRY) + return forcedKey; + } + return randchoice(RandomType::Spectral + source + anteStr, SPECTRALS); +} + +inline JokerData Instance::nextJoker(std::string source, int ante, + bool hasStickers) { + std::string anteStr = anteToString(ante); + + // Get rarity + Item rarity; + if (source == ItemSource::Soul) + rarity = Item::Legendary; + else if (source == ItemSource::Wraith) + rarity = Item::Rare; + else if (source == ItemSource::Rare_Tag) + rarity = Item::Rare; + else if (source == ItemSource::Uncommon_Tag) + rarity = Item::Uncommon; + else { + double rarityPoll = random(RandomType::Joker_Rarity + anteStr + source); + if (rarityPoll > 0.95) + rarity = Item::Rare; + else if (rarityPoll > 0.7) + rarity = Item::Uncommon; + else + rarity = Item::Common; + } + + // Get edition + int editionRate = 1; + if (isVoucherActive(Item::Glow_Up)) + editionRate = 4; + else if (isVoucherActive(Item::Hone)) + editionRate = 2; + Item edition; + double editionPoll = random(RandomType::Joker_Edition + source + anteStr); + if (editionPoll > 0.997) { + edition = Item::Negative; + } else if (editionPoll > 1 - 0.006 * editionRate) { + edition = Item::Polychrome; + } else if (editionPoll > 1 - 0.02 * editionRate) { + edition = Item::Holographic; + } else if (editionPoll > 1 - 0.04 * editionRate) { + edition = Item::Foil; + } else { + edition = Item::No_Edition; + } + + // Get next joker + Item joker; + if (rarity == Item::Legendary) { + if (params.version > 10099) { + joker = randchoice(RandomType::Joker_Legendary, LEGENDARY_JOKERS); + } else { + joker = randchoice(RandomType::Joker_Legendary + source + anteStr, + LEGENDARY_JOKERS); + } + } else if (rarity == Item::Rare) { + if (params.version > 10099) { + joker = + randchoice(RandomType::Joker_Rare + source + anteStr, RARE_JOKERS); + } else { + joker = randchoice(RandomType::Joker_Rare + source + anteStr, + RARE_JOKERS_100); + } + } else if (rarity == Item::Uncommon) { + if (params.version > 10099) { + joker = randchoice(RandomType::Joker_Uncommon + source + anteStr, + UNCOMMON_JOKERS); + } else { + joker = randchoice(RandomType::Joker_Uncommon + source + anteStr, + UNCOMMON_JOKERS_100); + } + } else if (rarity == Item::Common) { + if (params.version > 10099) { + joker = randchoice(RandomType::Joker_Common + source + anteStr, + COMMON_JOKERS); + } else { + joker = randchoice(RandomType::Joker_Common + source + anteStr, + COMMON_JOKERS_100); + } + } + + // Get next joker stickers + JokerStickers stickers = JokerStickers(); + if (hasStickers) { + if (params.version > 10099) { + double stickerPoll = random(((source == ItemSource::Buffoon_Pack) + ? RandomType::Eternal_Perishable_Pack + : RandomType::Eternal_Perishable) + + anteStr); + if (stickerPoll > 0.7 && params.stake >= Item::Black_Stake) { + if (joker != Item::Gros_Michel && joker != Item::Ice_Cream && + joker != Item::Cavendish && joker != Item::Luchador && + joker != Item::Turtle_Bean && joker != Item::Diet_Cola && + joker != Item::Popcorn && joker != Item::Ramen && + joker != Item::Seltzer && joker != Item::Mr_Bones && + joker != Item::Invisible_Joker) { + stickers.eternal = true; + } + } + if (stickerPoll > 0.4 && stickerPoll <= 0.7 && + params.stake >= Item::Orange_Stake && + joker != Item::Ceremonial_Dagger && joker != Item::Ride_the_Bus && + joker != Item::Runner && joker != Item::Constellation && + joker != Item::Green_Joker && joker != Item::Red_Card && + joker != Item::Madness && joker != Item::Square_Joker && + joker != Item::Vampire && joker != Item::Rocket && + joker != Item::Obelisk && joker != Item::Lucky_Cat && + joker != Item::Flash_Card && joker != Item::Spare_Trousers && + joker != Item::Castle && joker != Item::Wee_Joker) { + stickers.perishable = true; + } + if (params.stake >= Item::Gold_Stake) { + stickers.rental = random(((source == ItemSource::Buffoon_Pack) + ? RandomType::Rental_Pack + : RandomType::Rental) + + anteStr) > 0.7; + } + } else { + if (params.stake >= Item::Black_Stake) { + if (joker != Item::Gros_Michel && joker != Item::Ice_Cream && + joker != Item::Cavendish && joker != Item::Luchador && + joker != Item::Turtle_Bean && joker != Item::Diet_Cola && + joker != Item::Popcorn && joker != Item::Ramen && + joker != Item::Seltzer && joker != Item::Mr_Bones && + joker != Item::Invisible_Joker) { + stickers.eternal = random(RandomType::Eternal + anteStr) > 0.7; + } + } + } + } + + return JokerData(joker, rarity, edition, stickers); +} + +// Shop Logic +inline ShopInstance Instance::getShopInstance() { + double tarotRate = 4; + double planetRate = 4; + double playingCardRate = 0; + double spectralRate = 0; + if (params.deck == Item::Ghost_Deck) { + spectralRate = 2; + } + if (isVoucherActive(Item::Tarot_Tycoon)) { + tarotRate = 32; + } else if (isVoucherActive(Item::Tarot_Merchant)) { + tarotRate = 9.6; + } + if (isVoucherActive(Item::Planet_Tycoon)) { + planetRate = 32; + } else if (isVoucherActive(Item::Planet_Merchant)) { + planetRate = 9.6; + } + if (isVoucherActive(Item::Magic_Trick)) { + playingCardRate = 4; + } + + return ShopInstance(20, tarotRate, planetRate, playingCardRate, spectralRate); +}; + +inline Item shopItemType(ShopInstance shop, double cdtPoll) { + if (cdtPoll < shop.jokerRate) { + return Item::T_Joker; + } + cdtPoll -= shop.jokerRate; + + if (cdtPoll < shop.tarotRate) { + return Item::T_Tarot; + } + cdtPoll -= shop.tarotRate; + + if (cdtPoll < shop.planetRate) { + return Item::T_Planet; + } + cdtPoll -= shop.planetRate; + + if (cdtPoll < shop.playingCardRate) { + return Item::T_Playing_Card; + } + + return Item::T_Spectral; +} + +inline ShopItem Instance::nextShopItem(int ante) { + std::string anteStr = anteToString(ante); + + ShopInstance shop = getShopInstance(); + double cdtPoll = + random(RandomType::Card_Type + anteStr) * shop.getTotalRate(); + Item type = shopItemType(shop, cdtPoll); + + if (type == Item::T_Joker) { + JokerData jkr = nextJoker(ItemSource::Shop, ante, true); + return ShopItem(type, jkr.joker, jkr); + } else if (type == Item::T_Tarot) { + return ShopItem(type, nextTarot(ItemSource::Shop, ante, false)); + } else if (type == Item::T_Planet) { + return ShopItem(type, nextPlanet(ItemSource::Shop, ante, false)); + } else if (type == Item::T_Spectral) { + return ShopItem(type, nextSpectral(ItemSource::Shop, ante, false)); + } + // Todo: Magic Trick support + return ShopItem(); +} + +// Packs and Pack Contents +inline Item Instance::nextPack(int ante) { + if (ante <= 2 && !cache.generatedFirstPack && params.version > 10099) { + cache.generatedFirstPack = true; + return Item::Buffoon_Pack; + } + std::string anteStr = anteToString(ante); + return randweightedchoice(RandomType::Shop_Pack + anteStr, PACKS); +} + +extern std::vector PACK_INFO; + +inline Pack packInfo(Item pack) { + return PACK_INFO[(int)pack - (int)Item::Arcana_Pack]; +} + +inline Card Instance::nextStandardCard(int ante) { + std::string anteStr = anteToString(ante); + + // Enhancement + Item enhancement; + if (random(RandomType::Standard_Has_Enhancement + anteStr) <= 0.6) { + enhancement = Item::No_Enhancement; + } else { + enhancement = randchoice(RandomType::Enhancement + + ItemSource::Standard_Pack + anteStr, + ENHANCEMENTS); + } + + // Base + Item base = + randchoice(RandomType::Card + ItemSource::Standard_Pack + anteStr, CARDS); + + // Edition + Item edition; + double editionPoll = random(RandomType::Standard_Edition + anteStr); + if (editionPoll > 0.988) + edition = Item::Polychrome; + else if (editionPoll > 0.96) + edition = Item::Holographic; + else if (editionPoll > 0.92) + edition = Item::Foil; + else + edition = Item::No_Edition; + + // Seal + Item seal; + if (random(RandomType::Standard_Has_Seal + anteStr) <= 0.8) { + seal = Item::No_Seal; + } else { + double sealPoll = random(RandomType::Standard_Seal + anteStr); + if (sealPoll > 0.75) { + seal = Item::Red_Seal; + } else if (sealPoll > 0.5) { + seal = Item::Blue_Seal; + } else if (sealPoll > 0.25) { + seal = Item::Gold_Seal; + } else { + seal = Item::Purple_Seal; + } + } + + return Card(base, enhancement, edition, seal); +}; + +inline std::vector Instance::nextArcanaPack(int size, int ante) { + std::vector pack; + for (int i = 0; i < size; i++) { + if (isVoucherActive(Item::Omen_Globe) && + random(RandomType::Omen_Globe) > 0.8) { + pack.push_back(nextSpectral(ItemSource::Omen_Globe, ante, true)); + } else { + pack.push_back(nextTarot(ItemSource::Arcana_Pack, ante, true)); + } + if (!params.showman) { + lock(pack[i]); + } + } + for (int i = 0; i < size; i++) { + unlock(pack[i]); + } + return pack; +}; + +inline std::vector Instance::nextCelestialPack(int size, int ante) { + std::vector pack; + for (int i = 0; i < size; i++) { + pack.push_back(nextPlanet(ItemSource::Celestial_Pack, ante, true)); + if (!params.showman) + lock(pack[i]); + } + for (int i = 0; i < size; i++) { + unlock(pack[i]); + } + return pack; +}; + +inline std::vector Instance::nextSpectralPack(int size, int ante) { + std::vector pack; + for (int i = 0; i < size; i++) { + pack.push_back(nextSpectral(ItemSource::Spectral_Pack, ante, true)); + if (!params.showman) + lock(pack[i]); + } + for (int i = 0; i < size; i++) { + unlock(pack[i]); + } + return pack; +}; + +inline std::vector Instance::nextStandardPack(int size, int ante) { + std::vector pack; + for (int i = 0; i < size; i++) { + pack.push_back(nextStandardCard(ante)); + } + return pack; +}; + +inline std::vector Instance::nextBuffoonPack(int size, int ante) { + std::vector pack; + for (int i = 0; i < size; i++) { + pack.push_back(nextJoker(ItemSource::Buffoon_Pack, ante, true)); + if (!params.showman) + lock(pack[i].joker); + } + for (int i = 0; i < size; i++) { + unlock(pack[i].joker); + } + return pack; +}; + +// Misc +inline bool Instance::isVoucherActive(Item voucher) { + return params.vouchers[(int)voucher - (int)Item::Overstock]; +} + +inline void Instance::activateVoucher(Item voucher) { + params.vouchers[(int)voucher - (int)Item::Overstock] = true; + lock(voucher); + // Unlock next level voucher + for (unsigned long int i = 0; i < VOUCHERS.size(); i += 2) { + if (VOUCHERS[i] == voucher) { + unlock(VOUCHERS[i + 1]); + }; + }; +}; + +inline Item Instance::nextVoucher(int ante) { + return randchoice(RandomType::Voucher + anteToString(ante), VOUCHERS); +} + +inline void Instance::setDeck(Item deck) { + params.deck = deck; + if (deck == Item::Magic_Deck) { + activateVoucher(Item::Crystal_Ball); + } + if (deck == Item::Nebula_Deck) { + activateVoucher(Item::Telescope); + } + if (deck == Item::Zodiac_Deck) { + activateVoucher(Item::Tarot_Merchant); + activateVoucher(Item::Planet_Merchant); + activateVoucher(Item::Overstock); + } +} + +inline void Instance::setStake(Item stake) { params.stake = stake; } + +inline Item Instance::nextTag(int ante) { + return randchoice(RandomType::Tags + anteToString(ante), TAGS); +} + +inline Item Instance::nextBoss(int ante) { + constexpr int MAX_BOSSES = + 16; // Adjust this value based on the maximum number of bosses you expect + std::array bossPool; + int numBosses = 0; + + for (unsigned long int i = 0; i < BOSSES.size(); i++) { + if (!isLocked(BOSSES[i])) { + if ((ante % 8 == 0 && BOSSES[i] > Item::B_F_BEGIN) || + (ante % 8 != 0 && BOSSES[i] < Item::B_F_BEGIN)) { + bossPool[numBosses++] = BOSSES[i]; + } + } + } + + if (numBosses == 0) { + for (unsigned long int i = 0; i < BOSSES.size(); i++) { + if ((ante % 8 == 0 && BOSSES[i] > Item::B_F_BEGIN) || + (ante % 8 != 0 && BOSSES[i] < Item::B_F_BEGIN)) { + unlock(BOSSES[i]); + } + } + return nextBoss(ante); + } + + Item chosenBoss = randchoice("boss", bossPool); + lock(chosenBoss); + return chosenBoss; +} + +#endif \ No newline at end of file diff --git a/immolate/immolate.cpp b/immolate/immolate.cpp new file mode 100644 index 0000000..82719d6 --- /dev/null +++ b/immolate/immolate.cpp @@ -0,0 +1,246 @@ +#include "functions.hpp" +#include "minijson.hpp" +#include "search.hpp" +#include +#include + +Item BRAINSTORM_PACK = Item::RETRY; +Item BRAINSTORM_TAG = Item::Charm_Tag; +long BRAINSTORM_SOULS = 1; + +long filter(Instance inst) { + if (BRAINSTORM_PACK != Item::RETRY) { + inst.cache.generatedFirstPack = true; // we don't care about Pack 1 + if (inst.nextPack(1) != BRAINSTORM_PACK) { + return 0; + } + } + if (BRAINSTORM_TAG != Item::RETRY) { + if (inst.nextTag(1) != BRAINSTORM_TAG) { + return 0; + } + } + if (BRAINSTORM_SOULS > 0) { + for (int i = 1; i <= BRAINSTORM_SOULS; i++) { + auto tarots = inst.nextArcanaPack(5, 1); // Mega Arcana Pack + bool found_soul = false; + for (int t = 0; t < 5; t++) { + if (tarots[t] == Item::The_Soul) { + found_soul = true; + break; + } + } + if (!found_soul) { + return 0; + } + } + } + return 1; +}; + +IMMOLATE_API std::string brainstorm_cpp(std::string seed, std::string pack, +std::string tag, double souls) { BRAINSTORM_PACK = stringToItem(pack); + BRAINSTORM_TAG = stringToItem(tag); + BRAINSTORM_SOULS = souls; + Search search(filter, seed, 1, 100000000); + search.exitOnFind = true; + return search.search(); +} + +struct Step { + std::string op; + mini_json::Value args; +}; + +static Item parseItemSafe(const mini_json::Value &v) { + if (!v.isString()) return Item::RETRY; + return stringToItem(v.getString()); +} + +static bool matchesItem(const Item actual, const mini_json::Value &args) { + const auto &eq = args["equals"]; + if (eq.isString() && actual != parseItemSafe(eq)) return false; + const auto &inArr = args["in"]; + if (inArr.isArray()) { + bool ok = false; + for (const auto &el : inArr.array) { + if (el.isString() && actual == parseItemSafe(el)) { + ok = true; + break; + } + } + if (!ok) return false; + } + return true; +} + +static bool matchesJoker(const JokerData &jd, const mini_json::Value &match) { + if (!match.isObject()) return true; + if (match["joker"].isString() && jd.joker != parseItemSafe(match["joker"])) return false; + if (match["rarity"].isString() && jd.rarity != parseItemSafe(match["rarity"])) return false; + if (match["edition"].isString() && jd.edition != parseItemSafe(match["edition"])) return false; + const auto &stickers = match["stickers"]; + if (stickers.isObject()) { + if (stickers["eternal"].isBool() && jd.stickers.eternal != stickers["eternal"].getBool()) return false; + if (stickers["perishable"].isBool() && jd.stickers.perishable != stickers["perishable"].getBool()) return false; + if (stickers["rental"].isBool() && jd.stickers.rental != stickers["rental"].getBool()) return false; + } + return true; +} + +static long long getNumber(const mini_json::Value &v, long long def) { + return v.isNumber() ? static_cast(v.number) : def; +} + +static bool applyStep(const Step &step, Instance &inst) { + const auto &args = step.args; + if (step.op == "tag") { + int idx = static_cast(getNumber(args["index"], 1)); + Item val = inst.nextTag(idx); + return matchesItem(val, args); + } + if (step.op == "pack") { + int idx = static_cast(getNumber(args["index"], 1)); + Item val = inst.nextPack(idx); + return matchesItem(val, args); + } + if (step.op == "voucher") { + int idx = static_cast(getNumber(args["index"], 1)); + Item val = inst.nextVoucher(idx); + if (!matchesItem(val, args)) return false; + if (args["activate"].getBool(false)) { + inst.activateVoucher(val); + } + return true; + } + if (step.op == "boss") { + int idx = static_cast(getNumber(args["index"], 1)); + Item val = inst.nextBoss(idx); + return matchesItem(val, args); + } + if (step.op == "joker") { + int draw = static_cast(getNumber(args["draw"], 1)); + int ante = static_cast(getNumber(args["ante"], 1)); + bool stickers = args["has_stickers"].getBool(true); + std::string source = args["source"].getString("Brainstorm_Joker"); + JokerData jd = inst.nextJoker(source, ante, stickers); + // Advance draws if draw > 1 + for (int i = 1; i < draw; i++) { + inst.nextJoker(source, ante, stickers); + } + return matchesJoker(jd, args["match"]); + } + if (step.op == "joker_window") { + int limit = static_cast(getNumber(args["limit"], 1)); + int ante = static_cast(getNumber(args["ante"], 1)); + bool stickers = args["has_stickers"].getBool(true); + std::string source = args["source"].getString("Brainstorm_Joker_Window"); + const auto &match = args["any"]["match"].isObject() ? args["any"]["match"] : args["match"]; + for (int i = 0; i < limit; i++) { + JokerData jd = inst.nextJoker(source, ante, stickers); + if (matchesJoker(jd, match)) return true; + } + return false; + } + if (step.op == "state") { + std::string field = args["field"].getString(""); + if (field == "id") { + long long id = inst.seed.getID(); + const auto &eq = args["equals"]; + if (eq.isNumber() && id != static_cast(eq.number)) return false; + const auto &range = args["range"]; + if (range.isArray() && range.array.size() >= 2) { + long long lo = static_cast(range.array[0].number); + long long hi = static_cast(range.array[1].number); + if (id < lo || id > hi) return false; + } + } + return true; + } + if (step.op == "set") { + const auto &deck = args["deck"]; + const auto &stake = args["stake"]; + if (deck.isString()) inst.setDeck(stringToItem(deck.getString())); + if (stake.isString() || stake.isNumber()) { + if (stake.isString()) { + inst.setStake(stringToItem(stake.getString())); + } else { + inst.setStake(static_cast(static_cast(stake.number))); + } + } + return true; + } + // Unknown op -> fail safely + return false; +} + +static void collectSteps(const mini_json::Value &node, std::vector &out) { + if (node.isObject() && node["all"].isArray()) { + for (const auto &child : node["all"].array) { + collectSteps(child, out); + } + return; + } + if (node.isObject() && node["op"].isString()) { + Step s; + s.op = node["op"].getString(); + s.args = node["args"]; + out.push_back(s); + } +} + +static const char *dupCString(const std::string &str) { + char *c_result = (char *)malloc(str.length() + 1); + if (!c_result) return nullptr; + std::strcpy(c_result, str.c_str()); + return c_result; +} + +IMMOLATE_API const char *brainstorm_query(const char *seed, + const char *query_json) { + std::string seed_str = seed ? seed : ""; + std::string query_str = query_json ? query_json : ""; + + mini_json::Value root; + if (!mini_json::parse(query_str, root) || !root.isObject()) { + return dupCString(""); + } + + std::vector steps; + collectSteps(root["filter"], steps); + if (steps.empty()) { + return dupCString(""); + } + + const auto &search = root["search"]; + int threads = static_cast(getNumber(search["threads"], 1)); + long long max_seeds = getNumber(search["max_seeds"], 100000000); + bool exit_on_find = search["exit_on_find"].getBool(true); + + Search s([steps](Instance inst) { + for (const auto &step : steps) { + if (!applyStep(step, inst)) return 0; + } + return 1; + }, seed_str, threads, max_seeds > 0 ? max_seeds : 100000000); + s.exitOnFind = exit_on_find; + std::string result = s.search(); + return dupCString(result); +} + +extern "C" { + IMMOLATE_API const char* brainstorm(const char* seed, const char* pack, +const char* tag, double souls) { std::string cpp_seed(seed); std::string +cpp_pack(pack); std::string cpp_tag(tag); std::string result = +brainstorm_cpp(cpp_seed, cpp_pack, cpp_tag, souls); + + char* c_result = (char*)malloc(result.length() + 1); + strcpy(c_result, result.c_str()); + + return c_result; + } + + IMMOLATE_API void free_result(const char* result) { + free((void*)result); + } +} diff --git a/immolate/immolate.hpp b/immolate/immolate.hpp new file mode 100644 index 0000000..8cd5188 --- /dev/null +++ b/immolate/immolate.hpp @@ -0,0 +1,143 @@ + +#include +#ifdef _WIN32 +#ifdef BUILDING_DLL +#define IMMOLATE_API __declspec(dllexport) +#else +#define IMMOLATE_API __declspec(dllimport) +#endif +#else +#define IMMOLATE_API +#endif + +// Declare the functions with IMMOLATE_API +IMMOLATE_API std::string brainstorm_cpp(std::string seed, std::string pack, + std::string tag, double souls); +IMMOLATE_API const char *brainstorm_query(const char *seed, + const char *query_json); +extern "C" { +IMMOLATE_API const char *brainstorm(const char *seed, const char *pack, + const char *tag, double souls); +IMMOLATE_API void free_result(const char *result); +} + +#ifdef __EMSCRIPTEN__ +#include +using namespace emscripten; +EMSCRIPTEN_BINDINGS(Immolate) { + // instance.hpp + register_vector("VectorStr"); + register_vector("VectorJkr"); + register_vector("VectorCrd"); + class_("InstParams") + .constructor<>() + .constructor() + .property("deck", &InstParams::deck) + .property("stake", &InstParams::stake) + .property("showman", &InstParams::showman) + .property("vouchers", &InstParams::vouchers) + .property("version", &InstParams::version); + class_("Instance") + .constructor() + .function("get_node", &Instance::get_node) + .function("random", &Instance::random) + .function("randint", &Instance::randint) + .function("randchoice", &Instance::randchoice) + .property("params", &Instance::params) + .property("seed", &Instance::seed) + + // functions.hpp + .function("lock", &Instance::lock) + .function("unlock", &Instance::unlock) + .function("isLocked", &Instance::isLocked) + .function("initLocks", &Instance::initLocks) + .function("initUnlocks", &Instance::initUnlocks) + .function("nextTarot", &Instance::nextTarot) + .function("nextPlanet", &Instance::nextPlanet) + .function("nextSpectral", &Instance::nextSpectral) + .function("nextJoker", &Instance::nextJoker) + .function("getShopInstance", &Instance::getShopInstance) + .function("nextShopItem", &Instance::nextShopItem) + .function("nextPack", &Instance::nextPack) + .function("nextStandardCard", &Instance::nextStandardCard) + .function("nextArcanaPack", &Instance::nextArcanaPack) + .function("nextCelestialPack", &Instance::nextCelestialPack) + .function("nextSpectralPack", &Instance::nextSpectralPack) + .function("nextBuffoonPack", &Instance::nextBuffoonPack) + .function("nextStandardPack", &Instance::nextStandardPack) + .function("isVoucherActive", &Instance::isVoucherActive) + .function("activateVoucher", &Instance::activateVoucher) + .function("nextVoucher", &Instance::nextVoucher) + .function("setDeck", &Instance::setDeck) + .function("setStake", &Instance::setStake) + .function("nextTag", &Instance::nextTag) + .function("nextBoss", &Instance::nextBoss); + function("packInfo", &packInfo); + + // items.hpp + class_("ShopInstance") + .constructor<>() + .constructor() + .function("getTotalRate", &ShopInstance::getTotalRate) + .property("jokerRate", &ShopInstance::jokerRate) + .property("tarotRate", &ShopInstance::tarotRate) + .property("planetRate", &ShopInstance::planetRate) + .property("playingCardRate", &ShopInstance::playingCardRate) + .property("spectralRate", &ShopInstance::spectralRate); + class_("JokerStickers") + .constructor<>() + .constructor() + .property("eternal", &JokerStickers::eternal) + .property("perishable", &JokerStickers::perishable) + .property("rental", &JokerStickers::rental); + class_("JokerData") + .constructor<>() + .constructor() + .property("joker", &JokerData::joker) + .property("rarity", &JokerData::rarity) + .property("edition", &JokerData::edition) + .property("stickers", &JokerData::stickers); + class_("ShopItem") + .constructor<>() + .constructor() + .constructor() + .property("type", &ShopItem::type) + .property("item", &ShopItem::item) + .property("jokerData", &ShopItem::jokerData); + class_("WeightedItem") + .constructor() + .property("item", &WeightedItem::item) + .property("weight", &WeightedItem::weight); + class_("Pack") + .constructor() + .property("type", &Pack::type) + .property("size", &Pack::size) + .property("choices", &Pack::choices); + class_("Card") + .constructor() + .property("base", &Card::base) + .property("enhancement", &Card::enhancement) + .property("edition", &Card::edition) + .property("seal", &Card::seal); + constant("ENHANCEMENTS", &ENHANCEMENTS); + constant("CARDS", &CARDS); + constant("SUITS", &SUITS); + constant("RANKS", &RANKS); + constant("TAROTS", &TAROTS); + constant("PLANETS", &PLANETS); + constant("COMMON_JOKERS", &COMMON_JOKERS); + constant("UNCOMMON_JOKERS", &UNCOMMON_JOKERS); + constant("RARE_JOKERS", &RARE_JOKERS); + constant("LEGENDARY_JOKERS", &LEGENDARY_JOKERS); + constant("VOUCHERS", &VOUCHERS); + constant("SPECTRALS", &SPECTRALS); + constant("TAGS", &TAGS); + constant("BOSSES", &BOSSES); + + // util.hpp + function("pseudohash", &pseudohash) class_("LuaRandom") + .constructor<>() + .constructor() + .function("random", &LuaRandom::random); +} +#endif diff --git a/immolate/instance.hpp b/immolate/instance.hpp new file mode 100644 index 0000000..4489a81 --- /dev/null +++ b/immolate/instance.hpp @@ -0,0 +1,135 @@ +#include "items.hpp" +#include "seed.hpp" +#include "util.hpp" +#include +#include +#pragma once + +struct Cache { + std::map nodes; + bool generatedFirstPack = false; +}; + +struct InstParams { + Item deck; + Item stake; + bool showman; + int sixesFactor; + long version; + bool vouchers[32] = {false}; + InstParams() { + deck = Item::Red_Deck; + stake = Item::White_Stake; + showman = false; + sixesFactor = 1; + version = 10103; // 1.0.1c + } + InstParams(Item d, Item s, bool show, long v) { + deck = d; + stake = s; + showman = show; + sixesFactor = 1; + version = v; + } +}; + +struct Instance { + bool locked[(int)Item::ITEMS_END] = {false}; + Seed &seed; + double hashedSeed; + Cache cache; + InstParams params; + LuaRandom rng; + Instance(Seed &s) : seed(s) { + hashedSeed = s.pseudohash(0); + params = InstParams(); + rng = LuaRandom(0); + }; + void reset(Seed &s) { // This is slow, use next() unless necessary + seed = s; + hashedSeed = s.pseudohash(0); + params = InstParams(); + cache.nodes + .clear(); // Somehow `clear` is faster than swapping with empty map + cache.generatedFirstPack = false; + }; + void next() { + seed.next(); + hashedSeed = seed.pseudohash(0); + params = InstParams(); + cache.nodes.clear(); + cache.generatedFirstPack = false; + } + double get_node(std::string ID) { + if (cache.nodes.count(ID) == 0) { + cache.nodes[ID] = pseudohash_from(ID, seed.pseudohash(ID.length())); + } + cache.nodes[ID] = + round13(fract(cache.nodes[ID] * 1.72431234 + 2.134453429141)); + return (cache.nodes[ID] + hashedSeed) / 2; + } + double random(std::string ID) { + rng = LuaRandom(get_node(ID)); + return rng.random(); + } + int randint(std::string ID, int min, int max) { + rng = LuaRandom(get_node(ID)); + return rng.randint(min, max); + } + template + Item randchoice(std::string ID, const std::array &items) { + rng = LuaRandom(get_node(ID)); + Item item = items[rng.randint(0, items.size() - 1)]; + if ((params.showman == false && isLocked(item)) || item == Item::RETRY) { + int resample = 2; + while (true) { + rng = LuaRandom(get_node(ID + "_resample" + anteToString(resample))); + Item item = items[rng.randint(0, items.size() - 1)]; + resample++; + if ((item != Item::RETRY && !isLocked(item)) || resample > 1000) + return item; + } + } + return item; + } + template + Item randweightedchoice(std::string ID, + const std::array &items) { + rng = LuaRandom(get_node(ID)); + double poll = rng.random() * items[0].weight; + int idx = 1; + double weight = 0; + while (weight < poll) { + weight += items[idx].weight; + idx++; + } + return items[idx - 1].item; + } + + // Functions defined in functions.hpp + void lock(Item item); + void unlock(Item item); + bool isLocked(Item item); + void initLocks(int ante, bool freshProfile, bool freshRun); + void initUnlocks(int ante, bool freshProfile); + Item nextTarot(std::string source, int ante, bool soulable); + Item nextPlanet(std::string source, int ante, bool soulable); + Item nextSpectral(std::string source, int ante, bool soulable); + JokerData nextJoker(std::string source, int ante, bool hasStickers); + ShopInstance getShopInstance(); + ShopItem nextShopItem(int ante); + Item nextPack(int ante); + std::vector nextArcanaPack(int size, int ante); + std::vector nextCelestialPack(int size, int ante); + std::vector nextSpectralPack(int size, int ante); + std::vector nextBuffoonPack(int size, int ante); + std::vector nextStandardPack(int size, int ante); + Card nextStandardCard(int ante); + bool isVoucherActive(Item voucher); + void activateVoucher(Item voucher); + Item nextVoucher(int ante); + void setDeck(Item deck); + void setStake(Item stake); + Item nextTag(int ante); + Item nextBoss(int ante); +}; \ No newline at end of file diff --git a/immolate/items.cpp b/immolate/items.cpp new file mode 100644 index 0000000..eb9a04e --- /dev/null +++ b/immolate/items.cpp @@ -0,0 +1,356 @@ +// #include "items.hpp" + +// std::vector ENHANCEMENTS = { +// Item::Bonus_Card, Item::Mult_Card, Item::Wild_Card, Item::Glass_Card, +// Item::Steel_Card, Item::Stone_Card, Item::Gold_Card, Item::Lucky_Card}; + +// std::vector CARDS = { +// Item::C_2, Item::C_3, Item::C_4, Item::C_5, Item::C_6, Item::C_7, +// Item::C_8, Item::C_9, Item::C_A, Item::C_J, Item::C_K, Item::C_Q, +// Item::C_T, Item::D_2, Item::D_3, Item::D_4, Item::D_5, Item::D_6, +// Item::D_7, Item::D_8, Item::D_9, Item::D_A, Item::D_J, Item::D_K, +// Item::D_Q, Item::D_T, Item::H_2, Item::H_3, Item::H_4, Item::H_5, +// Item::H_6, Item::H_7, Item::H_8, Item::H_9, Item::H_A, Item::H_J, +// Item::H_K, Item::H_Q, Item::H_T, Item::S_2, Item::S_3, Item::S_4, +// Item::S_5, Item::S_6, Item::S_7, Item::S_8, Item::S_9, Item::S_A, +// Item::S_J, Item::S_K, Item::S_Q, Item::S_T}; + +// std::vector SUITS = {Item::Spades, Item::Hearts, Item::Clubs, +// Item::Diamonds}; + +// std::vector RANKS = {Item::_2, Item::_3, Item::_4, Item::_5, +// Item::_6, Item::_7, Item::_8, Item::_9, +// Item::_10, Item::Jack, Item::Queen, Item::King, +// Item::Ace}; + +// std::vector PACKS = { +// WeightedItem(Item::RETRY, 22.42), // total +// WeightedItem(Item::Arcana_Pack, 4), +// WeightedItem(Item::Jumbo_Arcana_Pack, 2), +// WeightedItem(Item::Mega_Arcana_Pack, 0.5), +// WeightedItem(Item::Celestial_Pack, 4), +// WeightedItem(Item::Jumbo_Celestial_Pack, 2), +// WeightedItem(Item::Mega_Celestial_Pack, 0.5), +// WeightedItem(Item::Standard_Pack, 4), +// WeightedItem(Item::Jumbo_Standard_Pack, 2), +// WeightedItem(Item::Mega_Standard_Pack, 0.5), +// WeightedItem(Item::Buffoon_Pack, 1.2), +// WeightedItem(Item::Jumbo_Buffoon_Pack, 0.6), +// WeightedItem(Item::Mega_Buffoon_Pack, 0.15), +// WeightedItem(Item::Spectral_Pack, 0.6), +// WeightedItem(Item::Jumbo_Spectral_Pack, 0.3), +// WeightedItem(Item::Mega_Spectral_Pack, 0.07)}; + +// std::vector TAROTS = {Item::The_Fool, +// Item::The_Magician, +// Item::The_High_Priestess, +// Item::The_Empress, +// Item::The_Emperor, +// Item::The_Hierophant, +// Item::The_Lovers, +// Item::The_Chariot, +// Item::Justice, +// Item::The_Hermit, +// Item::The_Wheel_of_Fortune, +// Item::Strength, +// Item::The_Hanged_Man, +// Item::Death, +// Item::Temperance, +// Item::The_Devil, +// Item::The_Tower, +// Item::The_Star, +// Item::The_Moon, +// Item::The_Sun, +// Item::Judgement, +// Item::The_World}; + +// std::vector PLANETS = {Item::Mercury, Item::Venus, Item::Earth, +// Item::Mars, Item::Jupiter, Item::Saturn, +// Item::Uranus, Item::Neptune, Item::Pluto, +// Item::Planet_X, Item::Ceres, Item::Eris}; + +// std::vector COMMON_JOKERS_100 = {Item::Joker, +// Item::Greedy_Joker, +// Item::Lusty_Joker, +// Item::Wrathful_Joker, +// Item::Gluttonous_Joker, +// Item::Jolly_Joker, +// Item::Zany_Joker, +// Item::Mad_Joker, +// Item::Crazy_Joker, +// Item::Droll_Joker, +// Item::Sly_Joker, +// Item::Wily_Joker, +// Item::Clever_Joker, +// Item::Devious_Joker, +// Item::Crafty_Joker, +// Item::Half_Joker, +// Item::Credit_Card, +// Item::Banner, +// Item::Mystic_Summit, +// Item::_8_Ball, +// Item::Misprint, +// Item::Raised_Fist, +// Item::Chaos_the_Clown, +// Item::Scary_Face, +// Item::Abstract_Joker, +// Item::Delayed_Gratification, +// Item::Gros_Michel, +// Item::Even_Steven, +// Item::Odd_Todd, +// Item::Scholar, +// Item::Business_Card, +// Item::Supernova, +// Item::Ride_the_Bus, +// Item::Egg, +// Item::Runner, +// Item::Ice_Cream, +// Item::Splash, +// Item::Blue_Joker, +// Item::Faceless_Joker, +// Item::Green_Joker, +// Item::Superposition, +// Item::To_Do_List, +// Item::Cavendish, +// Item::Red_Card, +// Item::Square_Joker, +// Item::Riff_raff, +// Item::Photograph, +// Item::Mail_In_Rebate, +// Item::Hallucination, +// Item::Fortune_Teller, +// Item::Juggler, +// Item::Drunkard, +// Item::Golden_Joker, +// Item::Popcorn, +// Item::Walkie_Talkie, +// Item::Smiley_Face, +// Item::Golden_Ticket, +// Item::Swashbuckler, +// Item::Hanging_Chad, +// Item::Shoot_the_Moon}; + +// std::vector COMMON_JOKERS = { +// Item::Joker, +// Item::Greedy_Joker, +// Item::Lusty_Joker, +// Item::Wrathful_Joker, +// Item::Gluttonous_Joker, +// Item::Jolly_Joker, +// Item::Zany_Joker, +// Item::Mad_Joker, +// Item::Crazy_Joker, +// Item::Droll_Joker, +// Item::Sly_Joker, +// Item::Wily_Joker, +// Item::Clever_Joker, +// Item::Devious_Joker, +// Item::Crafty_Joker, +// Item::Half_Joker, +// Item::Credit_Card, +// Item::Banner, +// Item::Mystic_Summit, +// Item::_8_Ball, +// Item::Misprint, +// Item::Raised_Fist, +// Item::Chaos_the_Clown, +// Item::Scary_Face, +// Item::Abstract_Joker, +// Item::Delayed_Gratification, +// Item::Gros_Michel, +// Item::Even_Steven, +// Item::Odd_Todd, +// Item::Scholar, +// Item::Business_Card, +// Item::Supernova, +// Item::Ride_the_Bus, +// Item::Egg, +// Item::Runner, +// Item::Ice_Cream, +// Item::Splash, +// Item::Blue_Joker, +// Item::Faceless_Joker, +// Item::Green_Joker, +// Item::Superposition, +// Item::To_Do_List, +// Item::Cavendish, +// Item::Red_Card, +// Item::Square_Joker, +// Item::Riff_raff, +// Item::Photograph, +// Item::Reserved_Parking, +// Item::Mail_In_Rebate, +// Item::Hallucination, +// Item::Fortune_Teller, +// Item::Juggler, +// Item::Drunkard, +// Item::Golden_Joker, +// Item::Popcorn, +// Item::Walkie_Talkie, +// Item::Smiley_Face, +// Item::Golden_Ticket, +// Item::Swashbuckler, +// Item::Hanging_Chad, +// Item::Shoot_the_Moon, +// }; + +// std::vector UNCOMMON_JOKERS_100 = { +// Item::Joker_Stencil, Item::Four_Fingers, +// Item::Mime, Item::Ceremonial_Dagger, +// Item::Marble_Joker, Item::Loyalty_Card, +// Item::Dusk, Item::Fibonacci, +// Item::Steel_Joker, Item::Hack, +// Item::Pareidolia, Item::Space_Joker, +// Item::Burglar, Item::Blackboard, +// Item::Constellation, Item::Hiker, +// Item::Card_Sharp, Item::Madness, +// Item::Vampire, Item::Shortcut, +// Item::Hologram, Item::Vagabond, +// Item::Cloud_9, Item::Rocket, +// Item::Midas_Mask, Item::Luchador, +// Item::Gift_Card, Item::Turtle_Bean, +// Item::Erosion, Item::Reserved_Parking, +// Item::To_the_Moon, Item::Stone_Joker, +// Item::Lucky_Cat, Item::Bull, +// Item::Diet_Cola, Item::Trading_Card, +// Item::Flash_Card, Item::Spare_Trousers, +// Item::Ramen, Item::Seltzer, +// Item::Castle, Item::Mr_Bones, +// Item::Acrobat, Item::Sock_and_Buskin, +// Item::Troubadour, Item::Certificate, +// Item::Smeared_Joker, Item::Throwback, +// Item::Rough_Gem, Item::Bloodstone, +// Item::Arrowhead, Item::Onyx_Agate, +// Item::Glass_Joker, Item::Showman, +// Item::Flower_Pot, Item::Merry_Andy, +// Item::Oops_All_6s, Item::The_Idol, +// Item::Seeing_Double, Item::Matador, +// Item::Stuntman, Item::Satellite, +// Item::Cartomancer, Item::Astronomer, +// Item::Burnt_Joker, Item::Bootstraps}; + +// std::vector UNCOMMON_JOKERS = { +// Item::Joker_Stencil, Item::Four_Fingers, +// Item::Mime, Item::Ceremonial_Dagger, +// Item::Marble_Joker, Item::Loyalty_Card, +// Item::Dusk, Item::Fibonacci, +// Item::Steel_Joker, Item::Hack, +// Item::Pareidolia, Item::Space_Joker, +// Item::Burglar, Item::Blackboard, +// Item::Sixth_Sense, Item::Constellation, +// Item::Hiker, Item::Card_Sharp, +// Item::Madness, Item::Seance, +// Item::Vampire, Item::Shortcut, +// Item::Hologram, Item::Cloud_9, +// Item::Rocket, Item::Midas_Mask, +// Item::Luchador, Item::Gift_Card, +// Item::Turtle_Bean, Item::Erosion, +// Item::To_the_Moon, Item::Stone_Joker, +// Item::Lucky_Cat, Item::Bull, +// Item::Diet_Cola, Item::Trading_Card, +// Item::Flash_Card, Item::Spare_Trousers, +// Item::Ramen, Item::Seltzer, +// Item::Castle, Item::Mr_Bones, +// Item::Acrobat, Item::Sock_and_Buskin, +// Item::Troubadour, Item::Certificate, +// Item::Smeared_Joker, Item::Throwback, +// Item::Rough_Gem, Item::Bloodstone, +// Item::Arrowhead, Item::Onyx_Agate, +// Item::Glass_Joker, Item::Showman, +// Item::Flower_Pot, Item::Merry_Andy, +// Item::Oops_All_6s, Item::The_Idol, +// Item::Seeing_Double, Item::Matador, +// Item::Satellite, Item::Cartomancer, +// Item::Astronomer, Item::Bootstraps, +// }; + +// std::vector RARE_JOKERS_100 = {Item::DNA, +// Item::Sixth_Sense, +// Item::Seance, +// Item::Baron, +// Item::Obelisk, +// Item::Baseball_Card, +// Item::Ancient_Joker, +// Item::Campfire, +// Item::Blueprint, +// Item::Wee_Joker, +// Item::Hit_the_Road, +// Item::The_Duo, +// Item::The_Trio, +// Item::The_Family, +// Item::The_Order, +// Item::The_Tribe, +// Item::Invisible_Joker, +// Item::Brainstorm, +// Item::Drivers_License}; + +// std::vector RARE_JOKERS = { +// Item::DNA, +// Item::Vagabond, +// Item::Baron, +// Item::Obelisk, +// Item::Baseball_Card, +// Item::Ancient_Joker, +// Item::Campfire, +// Item::Blueprint, +// Item::Wee_Joker, +// Item::Hit_the_Road, +// Item::The_Duo, +// Item::The_Trio, +// Item::The_Family, +// Item::The_Order, +// Item::The_Tribe, +// Item::Stuntman, +// Item::Invisible_Joker, +// Item::Brainstorm, +// Item::Drivers_License, +// Item::Burnt_Joker, +// }; + +// std::vector LEGENDARY_JOKERS = {Item::Canio, Item::Triboulet, +// Item::Yorick, Item::Chicot, +// Item::Perkeo}; + +// std::vector VOUCHERS = { +// Item::Overstock, Item::Overstock_Plus, Item::Clearance_Sale, +// Item::Liquidation, Item::Hone, Item::Glow_Up, +// Item::Reroll_Surplus, Item::Reroll_Glut, Item::Crystal_Ball, +// Item::Omen_Globe, Item::Telescope, Item::Observatory, +// Item::Grabber, Item::Nacho_Tong, Item::Wasteful, +// Item::Recyclomancy, Item::Tarot_Merchant, Item::Tarot_Tycoon, +// Item::Planet_Merchant, Item::Planet_Tycoon, Item::Seed_Money, +// Item::Money_Tree, Item::Blank, Item::Antimatter, +// Item::Magic_Trick, Item::Illusion, Item::Hieroglyph, +// Item::Petroglyph, Item::Directors_Cut, Item::Retcon, +// Item::Paint_Brush, Item::Palette}; + +// std::vector SPECTRALS = { +// Item::Familiar, Item::Grim, Item::Incantation, Item::Talisman, +// Item::Aura, Item::Wraith, Item::Sigil, Item::Ouija, +// Item::Ectoplasm, Item::Immolate, Item::Ankh, Item::Deja_Vu, +// Item::Hex, Item::Trance, Item::Medium, Item::Cryptid, +// Item::RETRY, // Soul +// Item::RETRY // Black_Hole +// }; + +// std::vector TAGS = { +// Item::Uncommon_Tag, Item::Rare_Tag, Item::Negative_Tag, +// Item::Foil_Tag, Item::Holographic_Tag, Item::Polychrome_Tag, +// Item::Investment_Tag, Item::Voucher_Tag, Item::Boss_Tag, +// Item::Standard_Tag, Item::Charm_Tag, Item::Meteor_Tag, +// Item::Buffoon_Tag, Item::Handy_Tag, Item::Garbage_Tag, +// Item::Ethereal_Tag, Item::Coupon_Tag, Item::Double_Tag, +// Item::Juggle_Tag, Item::D6_Tag, Item::Top_up_Tag, +// Item::Speed_Tag, Item::Orbital_Tag, Item::Economy_Tag}; + +// std::vector BOSSES = { +// Item::The_Arm, Item::The_Club, Item::The_Eye, +// Item::Amber_Acorn, Item::Cerulean_Bell, Item::Crimson_Heart, +// Item::Verdant_Leaf, Item::Violet_Vessel, Item::The_Fish, +// Item::The_Flint, Item::The_Goad, Item::The_Head, +// Item::The_Hook, Item::The_House, Item::The_Manacle, +// Item::The_Mark, Item::The_Mouth, Item::The_Needle, +// Item::The_Ox, Item::The_Pillar, Item::The_Plant, +// Item::The_Psychic, Item::The_Serpent, Item::The_Tooth, +// Item::The_Wall, Item::The_Water, Item::The_Wheel, +// Item::The_Window}; diff --git a/immolate/items.hpp b/immolate/items.hpp new file mode 100644 index 0000000..ded376d --- /dev/null +++ b/immolate/items.hpp @@ -0,0 +1,3578 @@ +#ifndef ITEMS_HPP +#define ITEMS_HPP + +#include +#include +#include +#include +#include + +enum class Item { + RETRY, + + // Jokers + J_BEGIN, + + J_C_BEGIN, + Joker, + Greedy_Joker, + Lusty_Joker, + Wrathful_Joker, + Gluttonous_Joker, + Jolly_Joker, + Zany_Joker, + Mad_Joker, + Crazy_Joker, + Droll_Joker, + Sly_Joker, + Wily_Joker, + Clever_Joker, + Devious_Joker, + Crafty_Joker, + Half_Joker, + Credit_Card, + Banner, + Mystic_Summit, + _8_Ball, + Misprint, + Raised_Fist, + Chaos_the_Clown, + Scary_Face, + Abstract_Joker, + Delayed_Gratification, + Gros_Michel, + Even_Steven, + Odd_Todd, + Scholar, + Business_Card, + Supernova, + Ride_the_Bus, + Egg, + Runner, + Ice_Cream, + Splash, + Blue_Joker, + Faceless_Joker, + Green_Joker, + Superposition, + To_Do_List, + Cavendish, + Red_Card, + Square_Joker, + Riff_raff, + Photograph, + Reserved_Parking, + Mail_In_Rebate, + Hallucination, + Fortune_Teller, + Juggler, + Drunkard, + Golden_Joker, + Popcorn, + Walkie_Talkie, + Smiley_Face, + Golden_Ticket, + Swashbuckler, + Hanging_Chad, + Shoot_the_Moon, + J_C_END, + + J_U_BEGIN, + Joker_Stencil, + Four_Fingers, + Mime, + Ceremonial_Dagger, + Marble_Joker, + Loyalty_Card, + Dusk, + Fibonacci, + Steel_Joker, + Hack, + Pareidolia, + Space_Joker, + Burglar, + Blackboard, + Sixth_Sense, + Constellation, + Hiker, + Card_Sharp, + Madness, + Seance, + Shortcut, + Hologram, + Cloud_9, + Rocket, + Midas_Mask, + Luchador, + Gift_Card, + Turtle_Bean, + Erosion, + To_the_Moon, + Stone_Joker, + Lucky_Cat, + Bull, + Diet_Cola, + Trading_Card, + Flash_Card, + Spare_Trousers, + Ramen, + Seltzer, + Castle, + Mr_Bones, + Acrobat, + Sock_and_Buskin, + Troubadour, + Certificate, + Smeared_Joker, + Throwback, + Rough_Gem, + Bloodstone, + Arrowhead, + Onyx_Agate, + Glass_Joker, + Showman, + Flower_Pot, + Merry_Andy, + Oops_All_6s, + The_Idol, + Seeing_Double, + Matador, + Stuntman, + Satellite, + Cartomancer, + Astronomer, + Bootstraps, + J_U_END, + + J_R_BEGIN, + DNA, + Vampire, + Vagabond, + Baron, + Obelisk, + Baseball_Card, + Ancient_Joker, + Campfire, + Blueprint, + Wee_Joker, + Hit_the_Road, + The_Duo, + The_Trio, + The_Family, + The_Order, + The_Tribe, + Invisible_Joker, + Brainstorm, + Drivers_License, + Burnt_Joker, + J_R_END, + + J_L_BEGIN, + Canio, + Triboulet, + Yorick, + Chicot, + Perkeo, + J_L_END, + + J_END, + + // Vouchers + V_BEGIN, + Overstock, + Overstock_Plus, + Clearance_Sale, + Liquidation, + Hone, + Glow_Up, + Reroll_Surplus, + Reroll_Glut, + Crystal_Ball, + Omen_Globe, + Telescope, + Observatory, + Grabber, + Nacho_Tong, + Wasteful, + Recyclomancy, + Tarot_Merchant, + Tarot_Tycoon, + Planet_Merchant, + Planet_Tycoon, + Seed_Money, + Money_Tree, + Blank, + Antimatter, + Magic_Trick, + Illusion, + Hieroglyph, + Petroglyph, + Directors_Cut, + Retcon, + Paint_Brush, + Palette, + V_END, + + // Tarots + T_BEGIN, + The_Fool, + The_Magician, + The_High_Priestess, + The_Empress, + The_Emperor, + The_Hierophant, + The_Lovers, + The_Chariot, + Justice, + The_Hermit, + The_Wheel_of_Fortune, + Strength, + The_Hanged_Man, + Death, + Temperance, + The_Devil, + The_Tower, + The_Star, + The_Moon, + The_Sun, + Judgement, + The_World, + T_END, + + // Planets + P_BEGIN, + Mercury, + Venus, + Earth, + Mars, + Jupiter, + Saturn, + Uranus, + Neptune, + Pluto, + Planet_X, + Ceres, + Eris, + P_END, + + // Hands + H_BEGIN, + Pair, + Three_of_a_Kind, + Full_House, + Four_of_a_Kind, + Flush, + Straight, + Two_Pair, + Straight_Flush, + High_Card, + Five_of_a_Kind, + Flush_House, + Flush_Five, + H_END, + + // Spectrals + S_BEGIN, + Familiar, + Grim, + Incantation, + Talisman, + Aura, + Wraith, + Sigil, + Ouija, + Ectoplasm, + Immolate, + Ankh, + Deja_Vu, + Hex, + Trance, + Medium, + Cryptid, + The_Soul, + Black_Hole, + S_END, + + // Enhancements + ENHANCEMENT_BEGIN, + No_Enhancement, + Bonus_Card, + Mult_Card, + Wild_Card, + Glass_Card, + Steel_Card, + Stone_Card, + Gold_Card, + Lucky_Card, + ENHANCEMENT_END, + + // Seals + SEAL_BEGIN, + No_Seal, + Gold_Seal, + Red_Seal, + Blue_Seal, + Purple_Seal, + SEAL_END, + + // Editions + E_BEGIN, + No_Edition, + Foil, + Holographic, + Polychrome, + Negative, + E_END, + + // Booster Packs + PACK_BEGIN, + Arcana_Pack, + Jumbo_Arcana_Pack, + Mega_Arcana_Pack, + Celestial_Pack, + Jumbo_Celestial_Pack, + Mega_Celestial_Pack, + Standard_Pack, + Jumbo_Standard_Pack, + Mega_Standard_Pack, + Buffoon_Pack, + Jumbo_Buffoon_Pack, + Mega_Buffoon_Pack, + Spectral_Pack, + Jumbo_Spectral_Pack, + Mega_Spectral_Pack, + PACK_END, + + // Tags + TAG_BEGIN, + Uncommon_Tag, + Rare_Tag, + Negative_Tag, + Foil_Tag, + Holographic_Tag, + Polychrome_Tag, + Investment_Tag, + Voucher_Tag, + Boss_Tag, + Standard_Tag, + Charm_Tag, + Meteor_Tag, + Buffoon_Tag, + Handy_Tag, + Garbage_Tag, + Ethereal_Tag, + Coupon_Tag, + Double_Tag, + Juggle_Tag, + D6_Tag, + Top_up_Tag, + Speed_Tag, + Orbital_Tag, + Economy_Tag, + TAG_END, + + // Blinds + B_BEGIN, + Small_Blind, + Big_Blind, + The_Hook, + The_Ox, + The_House, + The_Wall, + The_Wheel, + The_Arm, + The_Club, + The_Fish, + The_Psychic, + The_Goad, + The_Water, + The_Window, + The_Manacle, + The_Eye, + The_Mouth, + The_Plant, + The_Serpent, + The_Pillar, + The_Needle, + The_Head, + The_Tooth, + The_Flint, + The_Mark, + B_F_BEGIN, + Amber_Acorn, + Verdant_Leaf, + Violet_Vessel, + Crimson_Heart, + Cerulean_Bell, + B_F_END, + B_END, + + // Suits + SUIT_BEGIN, + Hearts, + Clubs, + Diamonds, + Spades, + SUIT_END, + + // Ranks + RANK_BEGIN, + _2, + _3, + _4, + _5, + _6, + _7, + _8, + _9, + _10, + Jack, + Queen, + King, + Ace, + RANK_END, + + // Cards + C_BEGIN, + C_2, + C_3, + C_4, + C_5, + C_6, + C_7, + C_8, + C_9, + C_A, + C_J, + C_K, + C_Q, + C_T, + D_2, + D_3, + D_4, + D_5, + D_6, + D_7, + D_8, + D_9, + D_A, + D_J, + D_K, + D_Q, + D_T, + H_2, + H_3, + H_4, + H_5, + H_6, + H_7, + H_8, + H_9, + H_A, + H_J, + H_K, + H_Q, + H_T, + S_2, + S_3, + S_4, + S_5, + S_6, + S_7, + S_8, + S_9, + S_A, + S_J, + S_K, + S_Q, + S_T, + C_END, + + // Decks + D_BEGIN, + Red_Deck, + Blue_Deck, + Yellow_Deck, + Green_Deck, + Black_Deck, + Magic_Deck, + Nebula_Deck, + Ghost_Deck, + Abandoned_Deck, + Checkered_Deck, + Zodiac_Deck, + Painted_Deck, + Anaglyph_Deck, + Plasma_Deck, + Erratic_Deck, + Challenge_Deck, + D_END, + + // Challenges + CHAL_BEGIN, + The_Omelette, + _15_Minute_City, + Rich_get_Richer, + On_a_Knifes_Edge, + X_ray_Vision, + Mad_World, + Luxury_Tax, + Non_Perishable, + Medusa, + Double_or_Nothing, + Typecast, + Inflation, + Bram_Poker, + Fragile, + Monolith, + Blast_Off, + Five_Card_Draw, + Golden_Needle, + Cruelty, + Jokerless, + CHAL_END, + + // Stakes + STAKE_BEGIN, + White_Stake, + Red_Stake, + Green_Stake, + Black_Stake, + Blue_Stake, + Purple_Stake, + Orange_Stake, + Gold_Stake, + STAKE_END, + + RARITY_BEGIN, + Common, + Uncommon, + Rare, + Legendary, + RARITY_END, + + TYPE_BEGIN, + T_Joker, + T_Tarot, + T_Planet, + T_Spectral, + T_Playing_Card, + TYPE_END, + + ITEMS_END +}; +inline std::string itemToString(Item i) { + switch (i) { + case Item::RETRY: + return "RETRY"; + case Item::J_BEGIN: + return "J BEGIN"; + case Item::J_C_BEGIN: + return "J C BEGIN"; + case Item::Joker: + return "Joker"; + case Item::Greedy_Joker: + return "Greedy Joker"; + case Item::Lusty_Joker: + return "Lusty Joker"; + case Item::Wrathful_Joker: + return "Wrathful Joker"; + case Item::Gluttonous_Joker: + return "Gluttonous Joker"; + case Item::Jolly_Joker: + return "Jolly Joker"; + case Item::Zany_Joker: + return "Zany Joker"; + case Item::Mad_Joker: + return "Mad Joker"; + case Item::Crazy_Joker: + return "Crazy Joker"; + case Item::Droll_Joker: + return "Droll Joker"; + case Item::Sly_Joker: + return "Sly Joker"; + case Item::Wily_Joker: + return "Wily Joker"; + case Item::Clever_Joker: + return "Clever Joker"; + case Item::Devious_Joker: + return "Devious Joker"; + case Item::Crafty_Joker: + return "Crafty Joker"; + case Item::Half_Joker: + return "Half Joker"; + case Item::Credit_Card: + return "Credit Card"; + case Item::Banner: + return "Banner"; + case Item::Mystic_Summit: + return "Mystic Summit"; + case Item::_8_Ball: + return "8 Ball"; + case Item::Misprint: + return "Misprint"; + case Item::Raised_Fist: + return "Raised Fist"; + case Item::Chaos_the_Clown: + return "Chaos the Clown"; + case Item::Scary_Face: + return "Scary Face"; + case Item::Abstract_Joker: + return "Abstract Joker"; + case Item::Delayed_Gratification: + return "Delayed Gratification"; + case Item::Gros_Michel: + return "Gros Michel"; + case Item::Even_Steven: + return "Even Steven"; + case Item::Odd_Todd: + return "Odd Todd"; + case Item::Scholar: + return "Scholar"; + case Item::Business_Card: + return "Business Card"; + case Item::Supernova: + return "Supernova"; + case Item::Ride_the_Bus: + return "Ride the Bus"; + case Item::Egg: + return "Egg"; + case Item::Runner: + return "Runner"; + case Item::Ice_Cream: + return "Ice Cream"; + case Item::Splash: + return "Splash"; + case Item::Blue_Joker: + return "Blue Joker"; + case Item::Faceless_Joker: + return "Faceless Joker"; + case Item::Green_Joker: + return "Green Joker"; + case Item::Superposition: + return "Superposition"; + case Item::To_Do_List: + return "To Do List"; + case Item::Cavendish: + return "Cavendish"; + case Item::Red_Card: + return "Red Card"; + case Item::Square_Joker: + return "Square Joker"; + case Item::Riff_raff: + return "Riff-raff"; + case Item::Photograph: + return "Photograph"; + case Item::Reserved_Parking: + return "Reserved Parking"; + case Item::Mail_In_Rebate: + return "Mail-In Rebate"; + case Item::Hallucination: + return "Hallucination"; + case Item::Fortune_Teller: + return "Fortune Teller"; + case Item::Juggler: + return "Juggler"; + case Item::Drunkard: + return "Drunkard"; + case Item::Golden_Joker: + return "Golden Joker"; + case Item::Popcorn: + return "Popcorn"; + case Item::Walkie_Talkie: + return "Walkie Talkie"; + case Item::Smiley_Face: + return "Smiley Face"; + case Item::Golden_Ticket: + return "Golden Ticket"; + case Item::Swashbuckler: + return "Swashbuckler"; + case Item::Hanging_Chad: + return "Hanging Chad"; + case Item::Shoot_the_Moon: + return "Shoot the Moon"; + case Item::J_C_END: + return "J C END"; + case Item::J_U_BEGIN: + return "J U BEGIN"; + case Item::Joker_Stencil: + return "Joker Stencil"; + case Item::Four_Fingers: + return "Four Fingers"; + case Item::Mime: + return "Mime"; + case Item::Ceremonial_Dagger: + return "Ceremonial Dagger"; + case Item::Marble_Joker: + return "Marble Joker"; + case Item::Loyalty_Card: + return "Loyalty Card"; + case Item::Dusk: + return "Dusk"; + case Item::Fibonacci: + return "Fibonacci"; + case Item::Steel_Joker: + return "Steel Joker"; + case Item::Hack: + return "Hack"; + case Item::Pareidolia: + return "Pareidolia"; + case Item::Space_Joker: + return "Space Joker"; + case Item::Burglar: + return "Burglar"; + case Item::Blackboard: + return "Blackboard"; + case Item::Sixth_Sense: + return "Sixth Sense"; + case Item::Constellation: + return "Constellation"; + case Item::Hiker: + return "Hiker"; + case Item::Card_Sharp: + return "Card Sharp"; + case Item::Madness: + return "Madness"; + case Item::Seance: + return "SΘance"; + case Item::Shortcut: + return "Shortcut"; + case Item::Hologram: + return "Hologram"; + case Item::Cloud_9: + return "Cloud 9"; + case Item::Rocket: + return "Rocket"; + case Item::Midas_Mask: + return "Midas Mask"; + case Item::Luchador: + return "Luchador"; + case Item::Gift_Card: + return "Gift Card"; + case Item::Turtle_Bean: + return "Turtle Bean"; + case Item::Erosion: + return "Erosion"; + case Item::To_the_Moon: + return "To the Moon"; + case Item::Stone_Joker: + return "Stone Joker"; + case Item::Lucky_Cat: + return "Lucky Cat"; + case Item::Bull: + return "Bull"; + case Item::Diet_Cola: + return "Diet Cola"; + case Item::Trading_Card: + return "Trading Card"; + case Item::Flash_Card: + return "Flash Card"; + case Item::Spare_Trousers: + return "Spare Trousers"; + case Item::Ramen: + return "Ramen"; + case Item::Seltzer: + return "Seltzer"; + case Item::Castle: + return "Castle"; + case Item::Mr_Bones: + return "Mr. Bones"; + case Item::Acrobat: + return "Acrobat"; + case Item::Sock_and_Buskin: + return "Sock and Buskin"; + case Item::Troubadour: + return "Troubadour"; + case Item::Certificate: + return "Certificate"; + case Item::Smeared_Joker: + return "Smeared Joker"; + case Item::Throwback: + return "Throwback"; + case Item::Rough_Gem: + return "Rough Gem"; + case Item::Bloodstone: + return "Bloodstone"; + case Item::Arrowhead: + return "Arrowhead"; + case Item::Onyx_Agate: + return "Onyx Agate"; + case Item::Glass_Joker: + return "Glass Joker"; + case Item::Showman: + return "Showman"; + case Item::Flower_Pot: + return "Flower Pot"; + case Item::Merry_Andy: + return "Merry Andy"; + case Item::Oops_All_6s: + return "Oops! All 6s"; + case Item::The_Idol: + return "The Idol"; + case Item::Seeing_Double: + return "Seeing Double"; + case Item::Matador: + return "Matador"; + case Item::Stuntman: + return "Stuntman"; + case Item::Satellite: + return "Satellite"; + case Item::Cartomancer: + return "Cartomancer"; + case Item::Astronomer: + return "Astronomer"; + case Item::Bootstraps: + return "Bootstraps"; + case Item::J_U_END: + return "J U END"; + case Item::J_R_BEGIN: + return "J R BEGIN"; + case Item::DNA: + return "DNA"; + case Item::Vampire: + return "Vampire"; + case Item::Vagabond: + return "Vagabond"; + case Item::Baron: + return "Baron"; + case Item::Obelisk: + return "Obelisk"; + case Item::Baseball_Card: + return "Baseball Card"; + case Item::Ancient_Joker: + return "Ancient Joker"; + case Item::Campfire: + return "Campfire"; + case Item::Blueprint: + return "Blueprint"; + case Item::Wee_Joker: + return "Wee Joker"; + case Item::Hit_the_Road: + return "Hit the Road"; + case Item::The_Duo: + return "The Duo"; + case Item::The_Trio: + return "The Trio"; + case Item::The_Family: + return "The Family"; + case Item::The_Order: + return "The Order"; + case Item::The_Tribe: + return "The Tribe"; + case Item::Invisible_Joker: + return "Invisible Joker"; + case Item::Brainstorm: + return "Brainstorm"; + case Item::Drivers_License: + return "Driver's License"; + case Item::Burnt_Joker: + return "Burnt Joker"; + case Item::J_R_END: + return "J R END"; + case Item::J_L_BEGIN: + return "J L BEGIN"; + case Item::Canio: + return "Canio"; + case Item::Triboulet: + return "Triboulet"; + case Item::Yorick: + return "Yorick"; + case Item::Chicot: + return "Chicot"; + case Item::Perkeo: + return "Perkeo"; + case Item::J_L_END: + return "J L END"; + case Item::J_END: + return "J END"; + case Item::V_BEGIN: + return "V BEGIN"; + case Item::Overstock: + return "Overstock"; + case Item::Overstock_Plus: + return "Overstock Plus"; + case Item::Clearance_Sale: + return "Clearance Sale"; + case Item::Liquidation: + return "Liquidation"; + case Item::Hone: + return "Hone"; + case Item::Glow_Up: + return "Glow Up"; + case Item::Reroll_Surplus: + return "Reroll Surplus"; + case Item::Reroll_Glut: + return "Reroll Glut"; + case Item::Crystal_Ball: + return "Crystal Ball"; + case Item::Omen_Globe: + return "Omen Globe"; + case Item::Telescope: + return "Telescope"; + case Item::Observatory: + return "Observatory"; + case Item::Grabber: + return "Grabber"; + case Item::Nacho_Tong: + return "Nacho Tong"; + case Item::Wasteful: + return "Wasteful"; + case Item::Recyclomancy: + return "Recyclomancy"; + case Item::Tarot_Merchant: + return "Tarot Merchant"; + case Item::Tarot_Tycoon: + return "Tarot Tycoon"; + case Item::Planet_Merchant: + return "Planet Merchant"; + case Item::Planet_Tycoon: + return "Planet Tycoon"; + case Item::Seed_Money: + return "Seed Money"; + case Item::Money_Tree: + return "Money Tree"; + case Item::Blank: + return "Blank"; + case Item::Antimatter: + return "Antimatter"; + case Item::Magic_Trick: + return "Magic Trick"; + case Item::Illusion: + return "Illusion"; + case Item::Hieroglyph: + return "Hieroglyph"; + case Item::Petroglyph: + return "Petroglyph"; + case Item::Directors_Cut: + return "Director's Cut"; + case Item::Retcon: + return "Retcon"; + case Item::Paint_Brush: + return "Paint Brush"; + case Item::Palette: + return "Palette"; + case Item::V_END: + return "V END"; + case Item::T_BEGIN: + return "T BEGIN"; + case Item::The_Fool: + return "The Fool"; + case Item::The_Magician: + return "The Magician"; + case Item::The_High_Priestess: + return "The High Priestess"; + case Item::The_Empress: + return "The Empress"; + case Item::The_Emperor: + return "The Emperor"; + case Item::The_Hierophant: + return "The Hierophant"; + case Item::The_Lovers: + return "The Lovers"; + case Item::The_Chariot: + return "The Chariot"; + case Item::Justice: + return "Justice"; + case Item::The_Hermit: + return "The Hermit"; + case Item::The_Wheel_of_Fortune: + return "The Wheel of Fortune"; + case Item::Strength: + return "Strength"; + case Item::The_Hanged_Man: + return "The Hanged Man"; + case Item::Death: + return "Death"; + case Item::Temperance: + return "Temperance"; + case Item::The_Devil: + return "The Devil"; + case Item::The_Tower: + return "The Tower"; + case Item::The_Star: + return "The Star"; + case Item::The_Moon: + return "The Moon"; + case Item::The_Sun: + return "The Sun"; + case Item::Judgement: + return "Judgement"; + case Item::The_World: + return "The World"; + case Item::T_END: + return "T END"; + case Item::P_BEGIN: + return "P BEGIN"; + case Item::Mercury: + return "Mercury"; + case Item::Venus: + return "Venus"; + case Item::Earth: + return "Earth"; + case Item::Mars: + return "Mars"; + case Item::Jupiter: + return "Jupiter"; + case Item::Saturn: + return "Saturn"; + case Item::Uranus: + return "Uranus"; + case Item::Neptune: + return "Neptune"; + case Item::Pluto: + return "Pluto"; + case Item::Planet_X: + return "Planet X"; + case Item::Ceres: + return "Ceres"; + case Item::Eris: + return "Eris"; + case Item::P_END: + return "P END"; + case Item::H_BEGIN: + return "H BEGIN"; + case Item::Pair: + return "Pair"; + case Item::Three_of_a_Kind: + return "Three of a Kind"; + case Item::Full_House: + return "Full House"; + case Item::Four_of_a_Kind: + return "Four of a Kind"; + case Item::Flush: + return "Flush"; + case Item::Straight: + return "Straight"; + case Item::Two_Pair: + return "Two Pair"; + case Item::Straight_Flush: + return "Straight Flush"; + case Item::High_Card: + return "High Card"; + case Item::Five_of_a_Kind: + return "Five of a Kind"; + case Item::Flush_House: + return "Flush House"; + case Item::Flush_Five: + return "Flush Five"; + case Item::H_END: + return "H END"; + case Item::S_BEGIN: + return "S BEGIN"; + case Item::Familiar: + return "Familiar"; + case Item::Grim: + return "Grim"; + case Item::Incantation: + return "Incantation"; + case Item::Talisman: + return "Talisman"; + case Item::Aura: + return "Aura"; + case Item::Wraith: + return "Wraith"; + case Item::Sigil: + return "Sigil"; + case Item::Ouija: + return "Ouija"; + case Item::Ectoplasm: + return "Ectoplasm"; + case Item::Immolate: + return "Immolate"; + case Item::Ankh: + return "Ankh"; + case Item::Deja_Vu: + return "Deja Vu"; + case Item::Hex: + return "Hex"; + case Item::Trance: + return "Trance"; + case Item::Medium: + return "Medium"; + case Item::Cryptid: + return "Cryptid"; + case Item::The_Soul: + return "The Soul"; + case Item::Black_Hole: + return "Black Hole"; + case Item::S_END: + return "S END"; + case Item::ENHANCEMENT_BEGIN: + return "ENHANCEMENT BEGIN"; + case Item::No_Enhancement: + return "No Enhancement"; + case Item::Bonus_Card: + return "Bonus Card"; + case Item::Mult_Card: + return "Mult Card"; + case Item::Wild_Card: + return "Wild Card"; + case Item::Glass_Card: + return "Glass Card"; + case Item::Steel_Card: + return "Steel Card"; + case Item::Stone_Card: + return "Stone Card"; + case Item::Gold_Card: + return "Gold Card"; + case Item::Lucky_Card: + return "Lucky Card"; + case Item::ENHANCEMENT_END: + return "ENHANCEMENT END"; + case Item::SEAL_BEGIN: + return "SEAL BEGIN"; + case Item::No_Seal: + return "No Seal"; + case Item::Gold_Seal: + return "Gold Seal"; + case Item::Red_Seal: + return "Red Seal"; + case Item::Blue_Seal: + return "Blue Seal"; + case Item::Purple_Seal: + return "Purple Seal"; + case Item::SEAL_END: + return "SEAL END"; + case Item::E_BEGIN: + return "E BEGIN"; + case Item::No_Edition: + return "No Edition"; + case Item::Foil: + return "Foil"; + case Item::Holographic: + return "Holographic"; + case Item::Polychrome: + return "Polychrome"; + case Item::Negative: + return "Negative"; + case Item::E_END: + return "E END"; + case Item::PACK_BEGIN: + return "PACK BEGIN"; + case Item::Arcana_Pack: + return "Arcana Pack"; + case Item::Jumbo_Arcana_Pack: + return "Jumbo Arcana Pack"; + case Item::Mega_Arcana_Pack: + return "Mega Arcana Pack"; + case Item::Celestial_Pack: + return "Celestial Pack"; + case Item::Jumbo_Celestial_Pack: + return "Jumbo Celestial Pack"; + case Item::Mega_Celestial_Pack: + return "Mega Celestial Pack"; + case Item::Standard_Pack: + return "Standard Pack"; + case Item::Jumbo_Standard_Pack: + return "Jumbo Standard Pack"; + case Item::Mega_Standard_Pack: + return "Mega Standard Pack"; + case Item::Buffoon_Pack: + return "Buffoon Pack"; + case Item::Jumbo_Buffoon_Pack: + return "Jumbo Buffoon Pack"; + case Item::Mega_Buffoon_Pack: + return "Mega Buffoon Pack"; + case Item::Spectral_Pack: + return "Spectral Pack"; + case Item::Jumbo_Spectral_Pack: + return "Jumbo Spectral Pack"; + case Item::Mega_Spectral_Pack: + return "Mega Spectral Pack"; + case Item::PACK_END: + return "PACK END"; + case Item::TAG_BEGIN: + return "TAG BEGIN"; + case Item::Uncommon_Tag: + return "Uncommon Tag"; + case Item::Rare_Tag: + return "Rare Tag"; + case Item::Negative_Tag: + return "Negative Tag"; + case Item::Foil_Tag: + return "Foil Tag"; + case Item::Holographic_Tag: + return "Holographic Tag"; + case Item::Polychrome_Tag: + return "Polychrome Tag"; + case Item::Investment_Tag: + return "Investment Tag"; + case Item::Voucher_Tag: + return "Voucher Tag"; + case Item::Boss_Tag: + return "Boss Tag"; + case Item::Standard_Tag: + return "Standard Tag"; + case Item::Charm_Tag: + return "Charm Tag"; + case Item::Meteor_Tag: + return "Meteor Tag"; + case Item::Buffoon_Tag: + return "Buffoon Tag"; + case Item::Handy_Tag: + return "Handy Tag"; + case Item::Garbage_Tag: + return "Garbage Tag"; + case Item::Ethereal_Tag: + return "Ethereal Tag"; + case Item::Coupon_Tag: + return "Coupon Tag"; + case Item::Double_Tag: + return "Double Tag"; + case Item::Juggle_Tag: + return "Juggle Tag"; + case Item::D6_Tag: + return "D6 Tag"; + case Item::Top_up_Tag: + return "Top-up Tag"; + case Item::Speed_Tag: + return "Speed Tag"; + case Item::Orbital_Tag: + return "Orbital Tag"; + case Item::Economy_Tag: + return "Economy Tag"; + case Item::TAG_END: + return "TAG END"; + case Item::B_BEGIN: + return "B BEGIN"; + case Item::Small_Blind: + return "Small Blind"; + case Item::Big_Blind: + return "Big Blind"; + case Item::The_Hook: + return "The Hook"; + case Item::The_Ox: + return "The Ox"; + case Item::The_House: + return "The House"; + case Item::The_Wall: + return "The Wall"; + case Item::The_Wheel: + return "The Wheel"; + case Item::The_Arm: + return "The Arm"; + case Item::The_Club: + return "The Club"; + case Item::The_Fish: + return "The Fish"; + case Item::The_Psychic: + return "The Psychic"; + case Item::The_Goad: + return "The Goad"; + case Item::The_Water: + return "The Water"; + case Item::The_Window: + return "The Window"; + case Item::The_Manacle: + return "The Manacle"; + case Item::The_Eye: + return "The Eye"; + case Item::The_Mouth: + return "The Mouth"; + case Item::The_Plant: + return "The Plant"; + case Item::The_Serpent: + return "The Serpent"; + case Item::The_Pillar: + return "The Pillar"; + case Item::The_Needle: + return "The Needle"; + case Item::The_Head: + return "The Head"; + case Item::The_Tooth: + return "The Tooth"; + case Item::The_Flint: + return "The Flint"; + case Item::The_Mark: + return "The Mark"; + case Item::B_F_BEGIN: + return "B F BEGIN"; + case Item::Amber_Acorn: + return "Amber Acorn"; + case Item::Verdant_Leaf: + return "Verdant Leaf"; + case Item::Violet_Vessel: + return "Violet Vessel"; + case Item::Crimson_Heart: + return "Crimson Heart"; + case Item::Cerulean_Bell: + return "Cerulean Bell"; + case Item::B_F_END: + return "B F END"; + case Item::B_END: + return "B END"; + case Item::SUIT_BEGIN: + return "SUIT BEGIN"; + case Item::Hearts: + return "Hearts"; + case Item::Clubs: + return "Clubs"; + case Item::Diamonds: + return "Diamonds"; + case Item::Spades: + return "Spades"; + case Item::SUIT_END: + return "SUIT END"; + case Item::RANK_BEGIN: + return "RANK BEGIN"; + case Item::_2: + return "2"; + case Item::_3: + return "3"; + case Item::_4: + return "4"; + case Item::_5: + return "5"; + case Item::_6: + return "6"; + case Item::_7: + return "7"; + case Item::_8: + return "8"; + case Item::_9: + return "9"; + case Item::_10: + return "10"; + case Item::Jack: + return "Jack"; + case Item::Queen: + return "Queen"; + case Item::King: + return "King"; + case Item::Ace: + return "Ace"; + case Item::RANK_END: + return "RANK END"; + case Item::C_BEGIN: + return "C BEGIN"; + case Item::C_2: + return "C 2"; + case Item::C_3: + return "C 3"; + case Item::C_4: + return "C 4"; + case Item::C_5: + return "C 5"; + case Item::C_6: + return "C 6"; + case Item::C_7: + return "C 7"; + case Item::C_8: + return "C 8"; + case Item::C_9: + return "C 9"; + case Item::C_A: + return "C A"; + case Item::C_J: + return "C J"; + case Item::C_K: + return "C K"; + case Item::C_Q: + return "C Q"; + case Item::C_T: + return "C T"; + case Item::D_2: + return "D 2"; + case Item::D_3: + return "D 3"; + case Item::D_4: + return "D 4"; + case Item::D_5: + return "D 5"; + case Item::D_6: + return "D 6"; + case Item::D_7: + return "D 7"; + case Item::D_8: + return "D 8"; + case Item::D_9: + return "D 9"; + case Item::D_A: + return "D A"; + case Item::D_J: + return "D J"; + case Item::D_K: + return "D K"; + case Item::D_Q: + return "D Q"; + case Item::D_T: + return "D T"; + case Item::H_2: + return "H 2"; + case Item::H_3: + return "H 3"; + case Item::H_4: + return "H 4"; + case Item::H_5: + return "H 5"; + case Item::H_6: + return "H 6"; + case Item::H_7: + return "H 7"; + case Item::H_8: + return "H 8"; + case Item::H_9: + return "H 9"; + case Item::H_A: + return "H A"; + case Item::H_J: + return "H J"; + case Item::H_K: + return "H K"; + case Item::H_Q: + return "H Q"; + case Item::H_T: + return "H T"; + case Item::S_2: + return "S 2"; + case Item::S_3: + return "S 3"; + case Item::S_4: + return "S 4"; + case Item::S_5: + return "S 5"; + case Item::S_6: + return "S 6"; + case Item::S_7: + return "S 7"; + case Item::S_8: + return "S 8"; + case Item::S_9: + return "S 9"; + case Item::S_A: + return "S A"; + case Item::S_J: + return "S J"; + case Item::S_K: + return "S K"; + case Item::S_Q: + return "S Q"; + case Item::S_T: + return "S T"; + case Item::C_END: + return "C END"; + case Item::D_BEGIN: + return "D BEGIN"; + case Item::Red_Deck: + return "Red Deck"; + case Item::Blue_Deck: + return "Blue Deck"; + case Item::Yellow_Deck: + return "Yellow Deck"; + case Item::Green_Deck: + return "Green Deck"; + case Item::Black_Deck: + return "Black Deck"; + case Item::Magic_Deck: + return "Magic Deck"; + case Item::Nebula_Deck: + return "Nebula Deck"; + case Item::Ghost_Deck: + return "Ghost Deck"; + case Item::Abandoned_Deck: + return "Abandoned Deck"; + case Item::Checkered_Deck: + return "Checkered Deck"; + case Item::Zodiac_Deck: + return "Zodiac Deck"; + case Item::Painted_Deck: + return "Painted Deck"; + case Item::Anaglyph_Deck: + return "Anaglyph Deck"; + case Item::Plasma_Deck: + return "Plasma Deck"; + case Item::Erratic_Deck: + return "Erratic Deck"; + case Item::Challenge_Deck: + return "Challenge Deck"; + case Item::D_END: + return "D END"; + case Item::CHAL_BEGIN: + return "CHAL BEGIN"; + case Item::The_Omelette: + return "The Omelette"; + case Item::_15_Minute_City: + return "15 Minute City"; + case Item::Rich_get_Richer: + return "Rich get Richer"; + case Item::On_a_Knifes_Edge: + return "On a Knife's Edge"; + case Item::X_ray_Vision: + return "X-ray Vision"; + case Item::Mad_World: + return "Mad World"; + case Item::Luxury_Tax: + return "Luxury Tax"; + case Item::Non_Perishable: + return "Non-Perishable"; + case Item::Medusa: + return "Medusa"; + case Item::Double_or_Nothing: + return "Double or Nothing"; + case Item::Typecast: + return "Typecast"; + case Item::Inflation: + return "Inflation"; + case Item::Bram_Poker: + return "Bram Poker"; + case Item::Fragile: + return "Fragile"; + case Item::Monolith: + return "Monolith"; + case Item::Blast_Off: + return "Blast Off"; + case Item::Five_Card_Draw: + return "Five-Card Draw"; + case Item::Golden_Needle: + return "Golden Needle"; + case Item::Cruelty: + return "Cruelty"; + case Item::Jokerless: + return "Jokerless"; + case Item::CHAL_END: + return "CHAL END"; + case Item::STAKE_BEGIN: + return "STAKE BEGIN"; + case Item::White_Stake: + return "White Stake"; + case Item::Red_Stake: + return "Red Stake"; + case Item::Green_Stake: + return "Green Stake"; + case Item::Black_Stake: + return "Black Stake"; + case Item::Blue_Stake: + return "Blue Stake"; + case Item::Purple_Stake: + return "Purple Stake"; + case Item::Orange_Stake: + return "Orange Stake"; + case Item::Gold_Stake: + return "Gold Stake"; + case Item::STAKE_END: + return "STAKE END"; + case Item::RARITY_BEGIN: + return "RARITY BEGIN"; + case Item::Common: + return "Common"; + case Item::Uncommon: + return "Uncommon"; + case Item::Rare: + return "Rare"; + case Item::Legendary: + return "Legendary"; + case Item::RARITY_END: + return "RARITY END"; + case Item::TYPE_BEGIN: + return "TYPE BEGIN"; + case Item::T_Joker: + return "T Joker"; + case Item::T_Tarot: + return "T Tarot"; + case Item::T_Planet: + return "T Planet"; + case Item::T_Spectral: + return "T Spectral"; + case Item::T_Playing_Card: + return "T Playing Card"; + case Item::TYPE_END: + return "TYPE END"; + default: + std::cout << "ERROR; stringToItem found no items... contact dev" + << std::endl; + EXIT_FAILURE; + } +} +inline Item stringToItem(std::string i) { + if (i == "RETRY") { + return Item::RETRY; + }; + if (i == "J BEGIN") { + return Item::J_BEGIN; + }; + if (i == "J C BEGIN") { + return Item::J_C_BEGIN; + }; + if (i == "Joker") { + return Item::Joker; + }; + if (i == "Greedy Joker") { + return Item::Greedy_Joker; + }; + if (i == "Lusty Joker") { + return Item::Lusty_Joker; + }; + if (i == "Wrathful Joker") { + return Item::Wrathful_Joker; + }; + if (i == "Gluttonous Joker") { + return Item::Gluttonous_Joker; + }; + if (i == "Jolly Joker") { + return Item::Jolly_Joker; + }; + if (i == "Zany Joker") { + return Item::Zany_Joker; + }; + if (i == "Mad Joker") { + return Item::Mad_Joker; + }; + if (i == "Crazy Joker") { + return Item::Crazy_Joker; + }; + if (i == "Droll Joker") { + return Item::Droll_Joker; + }; + if (i == "Sly Joker") { + return Item::Sly_Joker; + }; + if (i == "Wily Joker") { + return Item::Wily_Joker; + }; + if (i == "Clever Joker") { + return Item::Clever_Joker; + }; + if (i == "Devious Joker") { + return Item::Devious_Joker; + }; + if (i == "Crafty Joker") { + return Item::Crafty_Joker; + }; + if (i == "Half Joker") { + return Item::Half_Joker; + }; + if (i == "Credit Card") { + return Item::Credit_Card; + }; + if (i == "Banner") { + return Item::Banner; + }; + if (i == "Mystic Summit") { + return Item::Mystic_Summit; + }; + if (i == "8 Ball") { + return Item::_8_Ball; + }; + if (i == "Misprint") { + return Item::Misprint; + }; + if (i == "Raised Fist") { + return Item::Raised_Fist; + }; + if (i == "Chaos the Clown") { + return Item::Chaos_the_Clown; + }; + if (i == "Scary Face") { + return Item::Scary_Face; + }; + if (i == "Abstract Joker") { + return Item::Abstract_Joker; + }; + if (i == "Delayed Gratification") { + return Item::Delayed_Gratification; + }; + if (i == "Gros Michel") { + return Item::Gros_Michel; + }; + if (i == "Even Steven") { + return Item::Even_Steven; + }; + if (i == "Odd Todd") { + return Item::Odd_Todd; + }; + if (i == "Scholar") { + return Item::Scholar; + }; + if (i == "Business Card") { + return Item::Business_Card; + }; + if (i == "Supernova") { + return Item::Supernova; + }; + if (i == "Ride the Bus") { + return Item::Ride_the_Bus; + }; + if (i == "Egg") { + return Item::Egg; + }; + if (i == "Runner") { + return Item::Runner; + }; + if (i == "Ice Cream") { + return Item::Ice_Cream; + }; + if (i == "Splash") { + return Item::Splash; + }; + if (i == "Blue Joker") { + return Item::Blue_Joker; + }; + if (i == "Faceless Joker") { + return Item::Faceless_Joker; + }; + if (i == "Green Joker") { + return Item::Green_Joker; + }; + if (i == "Superposition") { + return Item::Superposition; + }; + if (i == "To Do List") { + return Item::To_Do_List; + }; + if (i == "Cavendish") { + return Item::Cavendish; + }; + if (i == "Red Card") { + return Item::Red_Card; + }; + if (i == "Square Joker") { + return Item::Square_Joker; + }; + if (i == "Riff-raff") { + return Item::Riff_raff; + }; + if (i == "Photograph") { + return Item::Photograph; + }; + if (i == "Reserved Parking") { + return Item::Reserved_Parking; + }; + if (i == "Mail-In Rebate") { + return Item::Mail_In_Rebate; + }; + if (i == "Hallucination") { + return Item::Hallucination; + }; + if (i == "Fortune Teller") { + return Item::Fortune_Teller; + }; + if (i == "Juggler") { + return Item::Juggler; + }; + if (i == "Drunkard") { + return Item::Drunkard; + }; + if (i == "Golden Joker") { + return Item::Golden_Joker; + }; + if (i == "Popcorn") { + return Item::Popcorn; + }; + if (i == "Walkie Talkie") { + return Item::Walkie_Talkie; + }; + if (i == "Smiley Face") { + return Item::Smiley_Face; + }; + if (i == "Golden Ticket") { + return Item::Golden_Ticket; + }; + if (i == "Swashbuckler") { + return Item::Swashbuckler; + }; + if (i == "Hanging Chad") { + return Item::Hanging_Chad; + }; + if (i == "Shoot the Moon") { + return Item::Shoot_the_Moon; + }; + if (i == "J C END") { + return Item::J_C_END; + }; + if (i == "J U BEGIN") { + return Item::J_U_BEGIN; + }; + if (i == "Joker Stencil") { + return Item::Joker_Stencil; + }; + if (i == "Four Fingers") { + return Item::Four_Fingers; + }; + if (i == "Mime") { + return Item::Mime; + }; + if (i == "Ceremonial Dagger") { + return Item::Ceremonial_Dagger; + }; + if (i == "Marble Joker") { + return Item::Marble_Joker; + }; + if (i == "Loyalty Card") { + return Item::Loyalty_Card; + }; + if (i == "Dusk") { + return Item::Dusk; + }; + if (i == "Fibonacci") { + return Item::Fibonacci; + }; + if (i == "Steel Joker") { + return Item::Steel_Joker; + }; + if (i == "Hack") { + return Item::Hack; + }; + if (i == "Pareidolia") { + return Item::Pareidolia; + }; + if (i == "Space Joker") { + return Item::Space_Joker; + }; + if (i == "Burglar") { + return Item::Burglar; + }; + if (i == "Blackboard") { + return Item::Blackboard; + }; + if (i == "Sixth Sense") { + return Item::Sixth_Sense; + }; + if (i == "Constellation") { + return Item::Constellation; + }; + if (i == "Hiker") { + return Item::Hiker; + }; + if (i == "Card Sharp") { + return Item::Card_Sharp; + }; + if (i == "Madness") { + return Item::Madness; + }; + if (i == "SΘance") { + return Item::Seance; + }; + if (i == "Shortcut") { + return Item::Shortcut; + }; + if (i == "Hologram") { + return Item::Hologram; + }; + if (i == "Cloud 9") { + return Item::Cloud_9; + }; + if (i == "Rocket") { + return Item::Rocket; + }; + if (i == "Midas Mask") { + return Item::Midas_Mask; + }; + if (i == "Luchador") { + return Item::Luchador; + }; + if (i == "Gift Card") { + return Item::Gift_Card; + }; + if (i == "Turtle Bean") { + return Item::Turtle_Bean; + }; + if (i == "Erosion") { + return Item::Erosion; + }; + if (i == "To the Moon") { + return Item::To_the_Moon; + }; + if (i == "Stone Joker") { + return Item::Stone_Joker; + }; + if (i == "Lucky Cat") { + return Item::Lucky_Cat; + }; + if (i == "Bull") { + return Item::Bull; + }; + if (i == "Diet Cola") { + return Item::Diet_Cola; + }; + if (i == "Trading Card") { + return Item::Trading_Card; + }; + if (i == "Flash Card") { + return Item::Flash_Card; + }; + if (i == "Spare Trousers") { + return Item::Spare_Trousers; + }; + if (i == "Ramen") { + return Item::Ramen; + }; + if (i == "Seltzer") { + return Item::Seltzer; + }; + if (i == "Castle") { + return Item::Castle; + }; + if (i == "Mr. Bones") { + return Item::Mr_Bones; + }; + if (i == "Acrobat") { + return Item::Acrobat; + }; + if (i == "Sock and Buskin") { + return Item::Sock_and_Buskin; + }; + if (i == "Troubadour") { + return Item::Troubadour; + }; + if (i == "Certificate") { + return Item::Certificate; + }; + if (i == "Smeared Joker") { + return Item::Smeared_Joker; + }; + if (i == "Throwback") { + return Item::Throwback; + }; + if (i == "Rough Gem") { + return Item::Rough_Gem; + }; + if (i == "Bloodstone") { + return Item::Bloodstone; + }; + if (i == "Arrowhead") { + return Item::Arrowhead; + }; + if (i == "Onyx Agate") { + return Item::Onyx_Agate; + }; + if (i == "Glass Joker") { + return Item::Glass_Joker; + }; + if (i == "Showman") { + return Item::Showman; + }; + if (i == "Flower Pot") { + return Item::Flower_Pot; + }; + if (i == "Merry Andy") { + return Item::Merry_Andy; + }; + if (i == "Oops! All 6s") { + return Item::Oops_All_6s; + }; + if (i == "The Idol") { + return Item::The_Idol; + }; + if (i == "Seeing Double") { + return Item::Seeing_Double; + }; + if (i == "Matador") { + return Item::Matador; + }; + if (i == "Stuntman") { + return Item::Stuntman; + }; + if (i == "Satellite") { + return Item::Satellite; + }; + if (i == "Cartomancer") { + return Item::Cartomancer; + }; + if (i == "Astronomer") { + return Item::Astronomer; + }; + if (i == "Bootstraps") { + return Item::Bootstraps; + }; + if (i == "J U END") { + return Item::J_U_END; + }; + if (i == "J R BEGIN") { + return Item::J_R_BEGIN; + }; + if (i == "DNA") { + return Item::DNA; + }; + if (i == "Vampire") { + return Item::Vampire; + }; + if (i == "Vagabond") { + return Item::Vagabond; + }; + if (i == "Baron") { + return Item::Baron; + }; + if (i == "Obelisk") { + return Item::Obelisk; + }; + if (i == "Baseball Card") { + return Item::Baseball_Card; + }; + if (i == "Ancient Joker") { + return Item::Ancient_Joker; + }; + if (i == "Campfire") { + return Item::Campfire; + }; + if (i == "Blueprint") { + return Item::Blueprint; + }; + if (i == "Wee Joker") { + return Item::Wee_Joker; + }; + if (i == "Hit the Road") { + return Item::Hit_the_Road; + }; + if (i == "The Duo") { + return Item::The_Duo; + }; + if (i == "The Trio") { + return Item::The_Trio; + }; + if (i == "The Family") { + return Item::The_Family; + }; + if (i == "The Order") { + return Item::The_Order; + }; + if (i == "The Tribe") { + return Item::The_Tribe; + }; + if (i == "Invisible Joker") { + return Item::Invisible_Joker; + }; + if (i == "Brainstorm") { + return Item::Brainstorm; + }; + if (i == "Driver's License") { + return Item::Drivers_License; + }; + if (i == "Burnt Joker") { + return Item::Burnt_Joker; + }; + if (i == "J R END") { + return Item::J_R_END; + }; + if (i == "J L BEGIN") { + return Item::J_L_BEGIN; + }; + if (i == "Canio") { + return Item::Canio; + }; + if (i == "Triboulet") { + return Item::Triboulet; + }; + if (i == "Yorick") { + return Item::Yorick; + }; + if (i == "Chicot") { + return Item::Chicot; + }; + if (i == "Perkeo") { + return Item::Perkeo; + }; + if (i == "J L END") { + return Item::J_L_END; + }; + if (i == "J END") { + return Item::J_END; + }; + if (i == "V BEGIN") { + return Item::V_BEGIN; + }; + if (i == "Overstock") { + return Item::Overstock; + }; + if (i == "Overstock Plus") { + return Item::Overstock_Plus; + }; + if (i == "Clearance Sale") { + return Item::Clearance_Sale; + }; + if (i == "Liquidation") { + return Item::Liquidation; + }; + if (i == "Hone") { + return Item::Hone; + }; + if (i == "Glow Up") { + return Item::Glow_Up; + }; + if (i == "Reroll Surplus") { + return Item::Reroll_Surplus; + }; + if (i == "Reroll Glut") { + return Item::Reroll_Glut; + }; + if (i == "Crystal Ball") { + return Item::Crystal_Ball; + }; + if (i == "Omen Globe") { + return Item::Omen_Globe; + }; + if (i == "Telescope") { + return Item::Telescope; + }; + if (i == "Observatory") { + return Item::Observatory; + }; + if (i == "Grabber") { + return Item::Grabber; + }; + if (i == "Nacho Tong") { + return Item::Nacho_Tong; + }; + if (i == "Wasteful") { + return Item::Wasteful; + }; + if (i == "Recyclomancy") { + return Item::Recyclomancy; + }; + if (i == "Tarot Merchant") { + return Item::Tarot_Merchant; + }; + if (i == "Tarot Tycoon") { + return Item::Tarot_Tycoon; + }; + if (i == "Planet Merchant") { + return Item::Planet_Merchant; + }; + if (i == "Planet Tycoon") { + return Item::Planet_Tycoon; + }; + if (i == "Seed Money") { + return Item::Seed_Money; + }; + if (i == "Money Tree") { + return Item::Money_Tree; + }; + if (i == "Blank") { + return Item::Blank; + }; + if (i == "Antimatter") { + return Item::Antimatter; + }; + if (i == "Magic Trick") { + return Item::Magic_Trick; + }; + if (i == "Illusion") { + return Item::Illusion; + }; + if (i == "Hieroglyph") { + return Item::Hieroglyph; + }; + if (i == "Petroglyph") { + return Item::Petroglyph; + }; + if (i == "Director's Cut") { + return Item::Directors_Cut; + }; + if (i == "Retcon") { + return Item::Retcon; + }; + if (i == "Paint Brush") { + return Item::Paint_Brush; + }; + if (i == "Palette") { + return Item::Palette; + }; + if (i == "V END") { + return Item::V_END; + }; + if (i == "T BEGIN") { + return Item::T_BEGIN; + }; + if (i == "The Fool") { + return Item::The_Fool; + }; + if (i == "The Magician") { + return Item::The_Magician; + }; + if (i == "The High Priestess") { + return Item::The_High_Priestess; + }; + if (i == "The Empress") { + return Item::The_Empress; + }; + if (i == "The Emperor") { + return Item::The_Emperor; + }; + if (i == "The Hierophant") { + return Item::The_Hierophant; + }; + if (i == "The Lovers") { + return Item::The_Lovers; + }; + if (i == "The Chariot") { + return Item::The_Chariot; + }; + if (i == "Justice") { + return Item::Justice; + }; + if (i == "The Hermit") { + return Item::The_Hermit; + }; + if (i == "The Wheel of Fortune") { + return Item::The_Wheel_of_Fortune; + }; + if (i == "Strength") { + return Item::Strength; + }; + if (i == "The Hanged Man") { + return Item::The_Hanged_Man; + }; + if (i == "Death") { + return Item::Death; + }; + if (i == "Temperance") { + return Item::Temperance; + }; + if (i == "The Devil") { + return Item::The_Devil; + }; + if (i == "The Tower") { + return Item::The_Tower; + }; + if (i == "The Star") { + return Item::The_Star; + }; + if (i == "The Moon") { + return Item::The_Moon; + }; + if (i == "The Sun") { + return Item::The_Sun; + }; + if (i == "Judgement") { + return Item::Judgement; + }; + if (i == "The World") { + return Item::The_World; + }; + if (i == "T END") { + return Item::T_END; + }; + if (i == "P BEGIN") { + return Item::P_BEGIN; + }; + if (i == "Mercury") { + return Item::Mercury; + }; + if (i == "Venus") { + return Item::Venus; + }; + if (i == "Earth") { + return Item::Earth; + }; + if (i == "Mars") { + return Item::Mars; + }; + if (i == "Jupiter") { + return Item::Jupiter; + }; + if (i == "Saturn") { + return Item::Saturn; + }; + if (i == "Uranus") { + return Item::Uranus; + }; + if (i == "Neptune") { + return Item::Neptune; + }; + if (i == "Pluto") { + return Item::Pluto; + }; + if (i == "Planet X") { + return Item::Planet_X; + }; + if (i == "Ceres") { + return Item::Ceres; + }; + if (i == "Eris") { + return Item::Eris; + }; + if (i == "P END") { + return Item::P_END; + }; + if (i == "H BEGIN") { + return Item::H_BEGIN; + }; + if (i == "Pair") { + return Item::Pair; + }; + if (i == "Three of a Kind") { + return Item::Three_of_a_Kind; + }; + if (i == "Full House") { + return Item::Full_House; + }; + if (i == "Four of a Kind") { + return Item::Four_of_a_Kind; + }; + if (i == "Flush") { + return Item::Flush; + }; + if (i == "Straight") { + return Item::Straight; + }; + if (i == "Two Pair") { + return Item::Two_Pair; + }; + if (i == "Straight Flush") { + return Item::Straight_Flush; + }; + if (i == "High Card") { + return Item::High_Card; + }; + if (i == "Five of a Kind") { + return Item::Five_of_a_Kind; + }; + if (i == "Flush House") { + return Item::Flush_House; + }; + if (i == "Flush Five") { + return Item::Flush_Five; + }; + if (i == "H END") { + return Item::H_END; + }; + if (i == "S BEGIN") { + return Item::S_BEGIN; + }; + if (i == "Familiar") { + return Item::Familiar; + }; + if (i == "Grim") { + return Item::Grim; + }; + if (i == "Incantation") { + return Item::Incantation; + }; + if (i == "Talisman") { + return Item::Talisman; + }; + if (i == "Aura") { + return Item::Aura; + }; + if (i == "Wraith") { + return Item::Wraith; + }; + if (i == "Sigil") { + return Item::Sigil; + }; + if (i == "Ouija") { + return Item::Ouija; + }; + if (i == "Ectoplasm") { + return Item::Ectoplasm; + }; + if (i == "Immolate") { + return Item::Immolate; + }; + if (i == "Ankh") { + return Item::Ankh; + }; + if (i == "Deja Vu") { + return Item::Deja_Vu; + }; + if (i == "Hex") { + return Item::Hex; + }; + if (i == "Trance") { + return Item::Trance; + }; + if (i == "Medium") { + return Item::Medium; + }; + if (i == "Cryptid") { + return Item::Cryptid; + }; + if (i == "The Soul") { + return Item::The_Soul; + }; + if (i == "Black Hole") { + return Item::Black_Hole; + }; + if (i == "S END") { + return Item::S_END; + }; + if (i == "ENHANCEMENT BEGIN") { + return Item::ENHANCEMENT_BEGIN; + }; + if (i == "No Enhancement") { + return Item::No_Enhancement; + }; + if (i == "Bonus Card") { + return Item::Bonus_Card; + }; + if (i == "Mult Card") { + return Item::Mult_Card; + }; + if (i == "Wild Card") { + return Item::Wild_Card; + }; + if (i == "Glass Card") { + return Item::Glass_Card; + }; + if (i == "Steel Card") { + return Item::Steel_Card; + }; + if (i == "Stone Card") { + return Item::Stone_Card; + }; + if (i == "Gold Card") { + return Item::Gold_Card; + }; + if (i == "Lucky Card") { + return Item::Lucky_Card; + }; + if (i == "ENHANCEMENT END") { + return Item::ENHANCEMENT_END; + }; + if (i == "SEAL BEGIN") { + return Item::SEAL_BEGIN; + }; + if (i == "No Seal") { + return Item::No_Seal; + }; + if (i == "Gold Seal") { + return Item::Gold_Seal; + }; + if (i == "Red Seal") { + return Item::Red_Seal; + }; + if (i == "Blue Seal") { + return Item::Blue_Seal; + }; + if (i == "Purple Seal") { + return Item::Purple_Seal; + }; + if (i == "SEAL END") { + return Item::SEAL_END; + }; + if (i == "E BEGIN") { + return Item::E_BEGIN; + }; + if (i == "No Edition") { + return Item::No_Edition; + }; + if (i == "Foil") { + return Item::Foil; + }; + if (i == "Holographic") { + return Item::Holographic; + }; + if (i == "Polychrome") { + return Item::Polychrome; + }; + if (i == "Negative") { + return Item::Negative; + }; + if (i == "E END") { + return Item::E_END; + }; + if (i == "PACK BEGIN") { + return Item::PACK_BEGIN; + }; + if (i == "Arcana Pack") { + return Item::Arcana_Pack; + }; + if (i == "Jumbo Arcana Pack") { + return Item::Jumbo_Arcana_Pack; + }; + if (i == "Mega Arcana Pack") { + return Item::Mega_Arcana_Pack; + }; + if (i == "Celestial Pack") { + return Item::Celestial_Pack; + }; + if (i == "Jumbo Celestial Pack") { + return Item::Jumbo_Celestial_Pack; + }; + if (i == "Mega Celestial Pack") { + return Item::Mega_Celestial_Pack; + }; + if (i == "Standard Pack") { + return Item::Standard_Pack; + }; + if (i == "Jumbo Standard Pack") { + return Item::Jumbo_Standard_Pack; + }; + if (i == "Mega Standard Pack") { + return Item::Mega_Standard_Pack; + }; + if (i == "Buffoon Pack") { + return Item::Buffoon_Pack; + }; + if (i == "Jumbo Buffoon Pack") { + return Item::Jumbo_Buffoon_Pack; + }; + if (i == "Mega Buffoon Pack") { + return Item::Mega_Buffoon_Pack; + }; + if (i == "Spectral Pack") { + return Item::Spectral_Pack; + }; + if (i == "Jumbo Spectral Pack") { + return Item::Jumbo_Spectral_Pack; + }; + if (i == "Mega Spectral Pack") { + return Item::Mega_Spectral_Pack; + }; + if (i == "PACK END") { + return Item::PACK_END; + }; + if (i == "TAG BEGIN") { + return Item::TAG_BEGIN; + }; + if (i == "Uncommon Tag") { + return Item::Uncommon_Tag; + }; + if (i == "Rare Tag") { + return Item::Rare_Tag; + }; + if (i == "Negative Tag") { + return Item::Negative_Tag; + }; + if (i == "Foil Tag") { + return Item::Foil_Tag; + }; + if (i == "Holographic Tag") { + return Item::Holographic_Tag; + }; + if (i == "Polychrome Tag") { + return Item::Polychrome_Tag; + }; + if (i == "Investment Tag") { + return Item::Investment_Tag; + }; + if (i == "Voucher Tag") { + return Item::Voucher_Tag; + }; + if (i == "Boss Tag") { + return Item::Boss_Tag; + }; + if (i == "Standard Tag") { + return Item::Standard_Tag; + }; + if (i == "Charm Tag") { + return Item::Charm_Tag; + }; + if (i == "Meteor Tag") { + return Item::Meteor_Tag; + }; + if (i == "Buffoon Tag") { + return Item::Buffoon_Tag; + }; + if (i == "Handy Tag") { + return Item::Handy_Tag; + }; + if (i == "Garbage Tag") { + return Item::Garbage_Tag; + }; + if (i == "Ethereal Tag") { + return Item::Ethereal_Tag; + }; + if (i == "Coupon Tag") { + return Item::Coupon_Tag; + }; + if (i == "Double Tag") { + return Item::Double_Tag; + }; + if (i == "Juggle Tag") { + return Item::Juggle_Tag; + }; + if (i == "D6 Tag") { + return Item::D6_Tag; + }; + if (i == "Top-up Tag") { + return Item::Top_up_Tag; + }; + if (i == "Speed Tag") { + return Item::Speed_Tag; + }; + if (i == "Orbital Tag") { + return Item::Orbital_Tag; + }; + if (i == "Economy Tag") { + return Item::Economy_Tag; + }; + if (i == "TAG END") { + return Item::TAG_END; + }; + if (i == "B BEGIN") { + return Item::B_BEGIN; + }; + if (i == "Small Blind") { + return Item::Small_Blind; + }; + if (i == "Big Blind") { + return Item::Big_Blind; + }; + if (i == "The Hook") { + return Item::The_Hook; + }; + if (i == "The Ox") { + return Item::The_Ox; + }; + if (i == "The House") { + return Item::The_House; + }; + if (i == "The Wall") { + return Item::The_Wall; + }; + if (i == "The Wheel") { + return Item::The_Wheel; + }; + if (i == "The Arm") { + return Item::The_Arm; + }; + if (i == "The Club") { + return Item::The_Club; + }; + if (i == "The Fish") { + return Item::The_Fish; + }; + if (i == "The Psychic") { + return Item::The_Psychic; + }; + if (i == "The Goad") { + return Item::The_Goad; + }; + if (i == "The Water") { + return Item::The_Water; + }; + if (i == "The Window") { + return Item::The_Window; + }; + if (i == "The Manacle") { + return Item::The_Manacle; + }; + if (i == "The Eye") { + return Item::The_Eye; + }; + if (i == "The Mouth") { + return Item::The_Mouth; + }; + if (i == "The Plant") { + return Item::The_Plant; + }; + if (i == "The Serpent") { + return Item::The_Serpent; + }; + if (i == "The Pillar") { + return Item::The_Pillar; + }; + if (i == "The Needle") { + return Item::The_Needle; + }; + if (i == "The Head") { + return Item::The_Head; + }; + if (i == "The Tooth") { + return Item::The_Tooth; + }; + if (i == "The Flint") { + return Item::The_Flint; + }; + if (i == "The Mark") { + return Item::The_Mark; + }; + if (i == "B F BEGIN") { + return Item::B_F_BEGIN; + }; + if (i == "Amber Acorn") { + return Item::Amber_Acorn; + }; + if (i == "Verdant Leaf") { + return Item::Verdant_Leaf; + }; + if (i == "Violet Vessel") { + return Item::Violet_Vessel; + }; + if (i == "Crimson Heart") { + return Item::Crimson_Heart; + }; + if (i == "Cerulean Bell") { + return Item::Cerulean_Bell; + }; + if (i == "B F END") { + return Item::B_F_END; + }; + if (i == "B END") { + return Item::B_END; + }; + if (i == "SUIT BEGIN") { + return Item::SUIT_BEGIN; + }; + if (i == "Hearts") { + return Item::Hearts; + }; + if (i == "Clubs") { + return Item::Clubs; + }; + if (i == "Diamonds") { + return Item::Diamonds; + }; + if (i == "Spades") { + return Item::Spades; + }; + if (i == "SUIT END") { + return Item::SUIT_END; + }; + if (i == "RANK BEGIN") { + return Item::RANK_BEGIN; + }; + if (i == "2") { + return Item::_2; + }; + if (i == "3") { + return Item::_3; + }; + if (i == "4") { + return Item::_4; + }; + if (i == "5") { + return Item::_5; + }; + if (i == "6") { + return Item::_6; + }; + if (i == "7") { + return Item::_7; + }; + if (i == "8") { + return Item::_8; + }; + if (i == "9") { + return Item::_9; + }; + if (i == "10") { + return Item::_10; + }; + if (i == "Jack") { + return Item::Jack; + }; + if (i == "Queen") { + return Item::Queen; + }; + if (i == "King") { + return Item::King; + }; + if (i == "Ace") { + return Item::Ace; + }; + if (i == "RANK END") { + return Item::RANK_END; + }; + if (i == "C BEGIN") { + return Item::C_BEGIN; + }; + if (i == "C 2") { + return Item::C_2; + }; + if (i == "C 3") { + return Item::C_3; + }; + if (i == "C 4") { + return Item::C_4; + }; + if (i == "C 5") { + return Item::C_5; + }; + if (i == "C 6") { + return Item::C_6; + }; + if (i == "C 7") { + return Item::C_7; + }; + if (i == "C 8") { + return Item::C_8; + }; + if (i == "C 9") { + return Item::C_9; + }; + if (i == "C A") { + return Item::C_A; + }; + if (i == "C J") { + return Item::C_J; + }; + if (i == "C K") { + return Item::C_K; + }; + if (i == "C Q") { + return Item::C_Q; + }; + if (i == "C T") { + return Item::C_T; + }; + if (i == "D 2") { + return Item::D_2; + }; + if (i == "D 3") { + return Item::D_3; + }; + if (i == "D 4") { + return Item::D_4; + }; + if (i == "D 5") { + return Item::D_5; + }; + if (i == "D 6") { + return Item::D_6; + }; + if (i == "D 7") { + return Item::D_7; + }; + if (i == "D 8") { + return Item::D_8; + }; + if (i == "D 9") { + return Item::D_9; + }; + if (i == "D A") { + return Item::D_A; + }; + if (i == "D J") { + return Item::D_J; + }; + if (i == "D K") { + return Item::D_K; + }; + if (i == "D Q") { + return Item::D_Q; + }; + if (i == "D T") { + return Item::D_T; + }; + if (i == "H 2") { + return Item::H_2; + }; + if (i == "H 3") { + return Item::H_3; + }; + if (i == "H 4") { + return Item::H_4; + }; + if (i == "H 5") { + return Item::H_5; + }; + if (i == "H 6") { + return Item::H_6; + }; + if (i == "H 7") { + return Item::H_7; + }; + if (i == "H 8") { + return Item::H_8; + }; + if (i == "H 9") { + return Item::H_9; + }; + if (i == "H A") { + return Item::H_A; + }; + if (i == "H J") { + return Item::H_J; + }; + if (i == "H K") { + return Item::H_K; + }; + if (i == "H Q") { + return Item::H_Q; + }; + if (i == "H T") { + return Item::H_T; + }; + if (i == "S 2") { + return Item::S_2; + }; + if (i == "S 3") { + return Item::S_3; + }; + if (i == "S 4") { + return Item::S_4; + }; + if (i == "S 5") { + return Item::S_5; + }; + if (i == "S 6") { + return Item::S_6; + }; + if (i == "S 7") { + return Item::S_7; + }; + if (i == "S 8") { + return Item::S_8; + }; + if (i == "S 9") { + return Item::S_9; + }; + if (i == "S A") { + return Item::S_A; + }; + if (i == "S J") { + return Item::S_J; + }; + if (i == "S K") { + return Item::S_K; + }; + if (i == "S Q") { + return Item::S_Q; + }; + if (i == "S T") { + return Item::S_T; + }; + if (i == "C END") { + return Item::C_END; + }; + if (i == "D BEGIN") { + return Item::D_BEGIN; + }; + if (i == "Red Deck") { + return Item::Red_Deck; + }; + if (i == "Blue Deck") { + return Item::Blue_Deck; + }; + if (i == "Yellow Deck") { + return Item::Yellow_Deck; + }; + if (i == "Green Deck") { + return Item::Green_Deck; + }; + if (i == "Black Deck") { + return Item::Black_Deck; + }; + if (i == "Magic Deck") { + return Item::Magic_Deck; + }; + if (i == "Nebula Deck") { + return Item::Nebula_Deck; + }; + if (i == "Ghost Deck") { + return Item::Ghost_Deck; + }; + if (i == "Abandoned Deck") { + return Item::Abandoned_Deck; + }; + if (i == "Checkered Deck") { + return Item::Checkered_Deck; + }; + if (i == "Zodiac Deck") { + return Item::Zodiac_Deck; + }; + if (i == "Painted Deck") { + return Item::Painted_Deck; + }; + if (i == "Anaglyph Deck") { + return Item::Anaglyph_Deck; + }; + if (i == "Plasma Deck") { + return Item::Plasma_Deck; + }; + if (i == "Erratic Deck") { + return Item::Erratic_Deck; + }; + if (i == "Challenge Deck") { + return Item::Challenge_Deck; + }; + if (i == "D END") { + return Item::D_END; + }; + if (i == "CHAL BEGIN") { + return Item::CHAL_BEGIN; + }; + if (i == "The Omelette") { + return Item::The_Omelette; + }; + if (i == "15 Minute City") { + return Item::_15_Minute_City; + }; + if (i == "Rich get Richer") { + return Item::Rich_get_Richer; + }; + if (i == "On a Knife's Edge") { + return Item::On_a_Knifes_Edge; + }; + if (i == "X-ray Vision") { + return Item::X_ray_Vision; + }; + if (i == "Mad World") { + return Item::Mad_World; + }; + if (i == "Luxury Tax") { + return Item::Luxury_Tax; + }; + if (i == "Non-Perishable") { + return Item::Non_Perishable; + }; + if (i == "Medusa") { + return Item::Medusa; + }; + if (i == "Double or Nothing") { + return Item::Double_or_Nothing; + }; + if (i == "Typecast") { + return Item::Typecast; + }; + if (i == "Inflation") { + return Item::Inflation; + }; + if (i == "Bram Poker") { + return Item::Bram_Poker; + }; + if (i == "Fragile") { + return Item::Fragile; + }; + if (i == "Monolith") { + return Item::Monolith; + }; + if (i == "Blast Off") { + return Item::Blast_Off; + }; + if (i == "Five-Card Draw") { + return Item::Five_Card_Draw; + }; + if (i == "Golden Needle") { + return Item::Golden_Needle; + }; + if (i == "Cruelty") { + return Item::Cruelty; + }; + if (i == "Jokerless") { + return Item::Jokerless; + }; + if (i == "CHAL END") { + return Item::CHAL_END; + }; + if (i == "STAKE BEGIN") { + return Item::STAKE_BEGIN; + }; + if (i == "White Stake") { + return Item::White_Stake; + }; + if (i == "Red Stake") { + return Item::Red_Stake; + }; + if (i == "Green Stake") { + return Item::Green_Stake; + }; + if (i == "Black Stake") { + return Item::Black_Stake; + }; + if (i == "Blue Stake") { + return Item::Blue_Stake; + }; + if (i == "Purple Stake") { + return Item::Purple_Stake; + }; + if (i == "Orange Stake") { + return Item::Orange_Stake; + }; + if (i == "Gold Stake") { + return Item::Gold_Stake; + }; + if (i == "STAKE END") { + return Item::STAKE_END; + }; + if (i == "RARITY BEGIN") { + return Item::RARITY_BEGIN; + }; + if (i == "Common") { + return Item::Common; + }; + if (i == "Uncommon") { + return Item::Uncommon; + }; + if (i == "Rare") { + return Item::Rare; + }; + if (i == "Legendary") { + return Item::Legendary; + }; + if (i == "RARITY END") { + return Item::RARITY_END; + }; + if (i == "TYPE BEGIN") { + return Item::TYPE_BEGIN; + }; + if (i == "T Joker") { + return Item::T_Joker; + }; + if (i == "T Tarot") { + return Item::T_Tarot; + }; + if (i == "T Planet") { + return Item::T_Planet; + }; + if (i == "T Spectral") { + return Item::T_Spectral; + }; + if (i == "T Playing Card") { + return Item::T_Playing_Card; + }; + if (i == "TYPE END") { + return Item::TYPE_END; + }; + return Item::RETRY; +} + +// Structs for storing information +struct ShopInstance { + double jokerRate; + double tarotRate; + double planetRate; + double playingCardRate; + double spectralRate; + ShopInstance() { + jokerRate = 20; + tarotRate = 4; + planetRate = 4; + playingCardRate = 0; + spectralRate = 0; + }; + ShopInstance(double j, double t, double p, double c, double s) { + jokerRate = j; + tarotRate = t; + planetRate = p; + playingCardRate = c; + spectralRate = s; + } + double getTotalRate() { + return jokerRate + tarotRate + planetRate + playingCardRate + spectralRate; + } +}; + +struct JokerStickers { + bool eternal; + bool perishable; + bool rental; + JokerStickers() { + eternal = false; + perishable = false; + rental = false; + }; + JokerStickers(bool e, bool p, bool r) { + eternal = e; + perishable = p; + rental = r; + } +}; + +struct JokerData { + Item joker; + Item rarity; + Item edition; + JokerStickers stickers; + JokerData() { + joker = Item::Joker; + rarity = Item::Common; + edition = Item::No_Edition; + stickers = JokerStickers(); + }; + JokerData(Item j, Item r, Item e, JokerStickers s) { + joker = j; + rarity = r; + edition = e; + stickers = s; + }; +}; + +struct ShopItem { + Item type; + Item item; + JokerData jokerData; + ShopItem() { + type = Item::T_Tarot; + item = Item::The_Fool; + }; + ShopItem(Item t, Item i) { + type = t; + item = i; + }; + ShopItem(Item t, Item i, JokerData j) { + type = t; + item = i; + jokerData = j; + }; +}; + +struct WeightedItem { + Item item; + double weight; + WeightedItem(Item i, double w) { + item = i; + weight = w; + }; +}; + +struct Pack { + Item type; + int size; + int choices; + Pack(Item t, int s, int c) { + type = t; + size = s; + choices = c; + } +}; + +struct Card { + Item base; + Item enhancement; + Item edition; + Item seal; + Card(Item b, Item n, Item e, Item s) { + base = b; + enhancement = n; + edition = e; + seal = s; + } +}; + +constexpr inline std::array ENHANCEMENTS = { + Item::Bonus_Card, Item::Mult_Card, Item::Wild_Card, Item::Glass_Card, + Item::Steel_Card, Item::Stone_Card, Item::Gold_Card, Item::Lucky_Card}; + +constexpr inline std::array CARDS = { + Item::C_2, Item::C_3, Item::C_4, Item::C_5, Item::C_6, Item::C_7, Item::C_8, + Item::C_9, Item::C_A, Item::C_J, Item::C_K, Item::C_Q, Item::C_T, Item::D_2, + Item::D_3, Item::D_4, Item::D_5, Item::D_6, Item::D_7, Item::D_8, Item::D_9, + Item::D_A, Item::D_J, Item::D_K, Item::D_Q, Item::D_T, Item::H_2, Item::H_3, + Item::H_4, Item::H_5, Item::H_6, Item::H_7, Item::H_8, Item::H_9, Item::H_A, + Item::H_J, Item::H_K, Item::H_Q, Item::H_T, Item::S_2, Item::S_3, Item::S_4, + Item::S_5, Item::S_6, Item::S_7, Item::S_8, Item::S_9, Item::S_A, Item::S_J, + Item::S_K, Item::S_Q, Item::S_T}; + +constexpr inline std::array SUITS = {Item::Spades, Item::Hearts, + Item::Clubs, Item::Diamonds}; + +constexpr inline std::array RANKS = { + Item::_2, Item::_3, Item::_4, Item::_5, Item::_6, + Item::_7, Item::_8, Item::_9, Item::_10, Item::Jack, + Item::Queen, Item::King, Item::Ace}; + +inline std::array PACKS = { + WeightedItem(Item::RETRY, 22.42), // total + WeightedItem(Item::Arcana_Pack, 4), + WeightedItem(Item::Jumbo_Arcana_Pack, 2), + WeightedItem(Item::Mega_Arcana_Pack, 0.5), + WeightedItem(Item::Celestial_Pack, 4), + WeightedItem(Item::Jumbo_Celestial_Pack, 2), + WeightedItem(Item::Mega_Celestial_Pack, 0.5), + WeightedItem(Item::Standard_Pack, 4), + WeightedItem(Item::Jumbo_Standard_Pack, 2), + WeightedItem(Item::Mega_Standard_Pack, 0.5), + WeightedItem(Item::Buffoon_Pack, 1.2), + WeightedItem(Item::Jumbo_Buffoon_Pack, 0.6), + WeightedItem(Item::Mega_Buffoon_Pack, 0.15), + WeightedItem(Item::Spectral_Pack, 0.6), + WeightedItem(Item::Jumbo_Spectral_Pack, 0.3), + WeightedItem(Item::Mega_Spectral_Pack, 0.07)}; + +constexpr inline std::array TAROTS = {Item::The_Fool, + Item::The_Magician, + Item::The_High_Priestess, + Item::The_Empress, + Item::The_Emperor, + Item::The_Hierophant, + Item::The_Lovers, + Item::The_Chariot, + Item::Justice, + Item::The_Hermit, + Item::The_Wheel_of_Fortune, + Item::Strength, + Item::The_Hanged_Man, + Item::Death, + Item::Temperance, + Item::The_Devil, + Item::The_Tower, + Item::The_Star, + Item::The_Moon, + Item::The_Sun, + Item::Judgement, + Item::The_World}; + +constexpr inline std::array PLANETS = { + Item::Mercury, Item::Venus, Item::Earth, Item::Mars, + Item::Jupiter, Item::Saturn, Item::Uranus, Item::Neptune, + Item::Pluto, Item::Planet_X, Item::Ceres, Item::Eris}; + +constexpr inline std::array COMMON_JOKERS_100 = { + Item::Joker, + Item::Greedy_Joker, + Item::Lusty_Joker, + Item::Wrathful_Joker, + Item::Gluttonous_Joker, + Item::Jolly_Joker, + Item::Zany_Joker, + Item::Mad_Joker, + Item::Crazy_Joker, + Item::Droll_Joker, + Item::Sly_Joker, + Item::Wily_Joker, + Item::Clever_Joker, + Item::Devious_Joker, + Item::Crafty_Joker, + Item::Half_Joker, + Item::Credit_Card, + Item::Banner, + Item::Mystic_Summit, + Item::_8_Ball, + Item::Misprint, + Item::Raised_Fist, + Item::Chaos_the_Clown, + Item::Scary_Face, + Item::Abstract_Joker, + Item::Delayed_Gratification, + Item::Gros_Michel, + Item::Even_Steven, + Item::Odd_Todd, + Item::Scholar, + Item::Business_Card, + Item::Supernova, + Item::Ride_the_Bus, + Item::Egg, + Item::Runner, + Item::Ice_Cream, + Item::Splash, + Item::Blue_Joker, + Item::Faceless_Joker, + Item::Green_Joker, + Item::Superposition, + Item::To_Do_List, + Item::Cavendish, + Item::Red_Card, + Item::Square_Joker, + Item::Riff_raff, + Item::Photograph, + Item::Mail_In_Rebate, + Item::Hallucination, + Item::Fortune_Teller, + Item::Juggler, + Item::Drunkard, + Item::Golden_Joker, + Item::Popcorn, + Item::Walkie_Talkie, + Item::Smiley_Face, + Item::Golden_Ticket, + Item::Swashbuckler, + Item::Hanging_Chad, + Item::Shoot_the_Moon}; + +constexpr inline std::array COMMON_JOKERS = { + Item::Joker, + Item::Greedy_Joker, + Item::Lusty_Joker, + Item::Wrathful_Joker, + Item::Gluttonous_Joker, + Item::Jolly_Joker, + Item::Zany_Joker, + Item::Mad_Joker, + Item::Crazy_Joker, + Item::Droll_Joker, + Item::Sly_Joker, + Item::Wily_Joker, + Item::Clever_Joker, + Item::Devious_Joker, + Item::Crafty_Joker, + Item::Half_Joker, + Item::Credit_Card, + Item::Banner, + Item::Mystic_Summit, + Item::_8_Ball, + Item::Misprint, + Item::Raised_Fist, + Item::Chaos_the_Clown, + Item::Scary_Face, + Item::Abstract_Joker, + Item::Delayed_Gratification, + Item::Gros_Michel, + Item::Even_Steven, + Item::Odd_Todd, + Item::Scholar, + Item::Business_Card, + Item::Supernova, + Item::Ride_the_Bus, + Item::Egg, + Item::Runner, + Item::Ice_Cream, + Item::Splash, + Item::Blue_Joker, + Item::Faceless_Joker, + Item::Green_Joker, + Item::Superposition, + Item::To_Do_List, + Item::Cavendish, + Item::Red_Card, + Item::Square_Joker, + Item::Riff_raff, + Item::Photograph, + Item::Reserved_Parking, + Item::Mail_In_Rebate, + Item::Hallucination, + Item::Fortune_Teller, + Item::Juggler, + Item::Drunkard, + Item::Golden_Joker, + Item::Popcorn, + Item::Walkie_Talkie, + Item::Smiley_Face, + Item::Golden_Ticket, + Item::Swashbuckler, + Item::Hanging_Chad, + Item::Shoot_the_Moon, +}; + +constexpr inline std::array UNCOMMON_JOKERS_100 = { + Item::Joker_Stencil, Item::Four_Fingers, + Item::Mime, Item::Ceremonial_Dagger, + Item::Marble_Joker, Item::Loyalty_Card, + Item::Dusk, Item::Fibonacci, + Item::Steel_Joker, Item::Hack, + Item::Pareidolia, Item::Space_Joker, + Item::Burglar, Item::Blackboard, + Item::Constellation, Item::Hiker, + Item::Card_Sharp, Item::Madness, + Item::Vampire, Item::Shortcut, + Item::Hologram, Item::Vagabond, + Item::Cloud_9, Item::Rocket, + Item::Midas_Mask, Item::Luchador, + Item::Gift_Card, Item::Turtle_Bean, + Item::Erosion, Item::Reserved_Parking, + Item::To_the_Moon, Item::Stone_Joker, + Item::Lucky_Cat, Item::Bull, + Item::Diet_Cola, Item::Trading_Card, + Item::Flash_Card, Item::Spare_Trousers, + Item::Ramen, Item::Seltzer, + Item::Castle, Item::Mr_Bones, + Item::Acrobat, Item::Sock_and_Buskin, + Item::Troubadour, Item::Certificate, + Item::Smeared_Joker, Item::Throwback, + Item::Rough_Gem, Item::Bloodstone, + Item::Arrowhead, Item::Onyx_Agate, + Item::Glass_Joker, Item::Showman, + Item::Flower_Pot, Item::Merry_Andy, + Item::Oops_All_6s, Item::The_Idol, + Item::Seeing_Double, Item::Matador, + Item::Stuntman, Item::Satellite, + Item::Cartomancer, Item::Astronomer, + Item::Burnt_Joker, Item::Bootstraps}; + +constexpr inline std::array UNCOMMON_JOKERS = { + Item::Joker_Stencil, Item::Four_Fingers, + Item::Mime, Item::Ceremonial_Dagger, + Item::Marble_Joker, Item::Loyalty_Card, + Item::Dusk, Item::Fibonacci, + Item::Steel_Joker, Item::Hack, + Item::Pareidolia, Item::Space_Joker, + Item::Burglar, Item::Blackboard, + Item::Sixth_Sense, Item::Constellation, + Item::Hiker, Item::Card_Sharp, + Item::Madness, Item::Seance, + Item::Vampire, Item::Shortcut, + Item::Hologram, Item::Cloud_9, + Item::Rocket, Item::Midas_Mask, + Item::Luchador, Item::Gift_Card, + Item::Turtle_Bean, Item::Erosion, + Item::To_the_Moon, Item::Stone_Joker, + Item::Lucky_Cat, Item::Bull, + Item::Diet_Cola, Item::Trading_Card, + Item::Flash_Card, Item::Spare_Trousers, + Item::Ramen, Item::Seltzer, + Item::Castle, Item::Mr_Bones, + Item::Acrobat, Item::Sock_and_Buskin, + Item::Troubadour, Item::Certificate, + Item::Smeared_Joker, Item::Throwback, + Item::Rough_Gem, Item::Bloodstone, + Item::Arrowhead, Item::Onyx_Agate, + Item::Glass_Joker, Item::Showman, + Item::Flower_Pot, Item::Merry_Andy, + Item::Oops_All_6s, Item::The_Idol, + Item::Seeing_Double, Item::Matador, + Item::Satellite, Item::Cartomancer, + Item::Astronomer, Item::Bootstraps, +}; + +constexpr inline std::array RARE_JOKERS_100 = {Item::DNA, + Item::Sixth_Sense, + Item::Seance, + Item::Baron, + Item::Obelisk, + Item::Baseball_Card, + Item::Ancient_Joker, + Item::Campfire, + Item::Blueprint, + Item::Wee_Joker, + Item::Hit_the_Road, + Item::The_Duo, + Item::The_Trio, + Item::The_Family, + Item::The_Order, + Item::The_Tribe, + Item::Invisible_Joker, + Item::Brainstorm, + Item::Drivers_License}; + +constexpr inline std::array RARE_JOKERS = { + Item::DNA, + Item::Vagabond, + Item::Baron, + Item::Obelisk, + Item::Baseball_Card, + Item::Ancient_Joker, + Item::Campfire, + Item::Blueprint, + Item::Wee_Joker, + Item::Hit_the_Road, + Item::The_Duo, + Item::The_Trio, + Item::The_Family, + Item::The_Order, + Item::The_Tribe, + Item::Stuntman, + Item::Invisible_Joker, + Item::Brainstorm, + Item::Drivers_License, + Item::Burnt_Joker, +}; + +constexpr inline std::array LEGENDARY_JOKERS = { + Item::Canio, Item::Triboulet, Item::Yorick, Item::Chicot, Item::Perkeo}; + +constexpr inline std::array VOUCHERS = { + Item::Overstock, Item::Overstock_Plus, Item::Clearance_Sale, + Item::Liquidation, Item::Hone, Item::Glow_Up, + Item::Reroll_Surplus, Item::Reroll_Glut, Item::Crystal_Ball, + Item::Omen_Globe, Item::Telescope, Item::Observatory, + Item::Grabber, Item::Nacho_Tong, Item::Wasteful, + Item::Recyclomancy, Item::Tarot_Merchant, Item::Tarot_Tycoon, + Item::Planet_Merchant, Item::Planet_Tycoon, Item::Seed_Money, + Item::Money_Tree, Item::Blank, Item::Antimatter, + Item::Magic_Trick, Item::Illusion, Item::Hieroglyph, + Item::Petroglyph, Item::Directors_Cut, Item::Retcon, + Item::Paint_Brush, Item::Palette}; + +constexpr inline std::array SPECTRALS = { + Item::Familiar, Item::Grim, Item::Incantation, Item::Talisman, + Item::Aura, Item::Wraith, Item::Sigil, Item::Ouija, + Item::Ectoplasm, Item::Immolate, Item::Ankh, Item::Deja_Vu, + Item::Hex, Item::Trance, Item::Medium, Item::Cryptid, + Item::RETRY, // Soul + Item::RETRY // Black_Hole +}; + +constexpr inline std::array TAGS = { + Item::Uncommon_Tag, Item::Rare_Tag, Item::Negative_Tag, + Item::Foil_Tag, Item::Holographic_Tag, Item::Polychrome_Tag, + Item::Investment_Tag, Item::Voucher_Tag, Item::Boss_Tag, + Item::Standard_Tag, Item::Charm_Tag, Item::Meteor_Tag, + Item::Buffoon_Tag, Item::Handy_Tag, Item::Garbage_Tag, + Item::Ethereal_Tag, Item::Coupon_Tag, Item::Double_Tag, + Item::Juggle_Tag, Item::D6_Tag, Item::Top_up_Tag, + Item::Speed_Tag, Item::Orbital_Tag, Item::Economy_Tag}; + +constexpr inline std::array BOSSES = { + Item::The_Arm, Item::The_Club, Item::The_Eye, + Item::Amber_Acorn, Item::Cerulean_Bell, Item::Crimson_Heart, + Item::Verdant_Leaf, Item::Violet_Vessel, Item::The_Fish, + Item::The_Flint, Item::The_Goad, Item::The_Head, + Item::The_Hook, Item::The_House, Item::The_Manacle, + Item::The_Mark, Item::The_Mouth, Item::The_Needle, + Item::The_Ox, Item::The_Pillar, Item::The_Plant, + Item::The_Psychic, Item::The_Serpent, Item::The_Tooth, + Item::The_Wall, Item::The_Water, Item::The_Wheel, + Item::The_Window}; + +#endif \ No newline at end of file diff --git a/immolate/main.cpp b/immolate/main.cpp new file mode 100644 index 0000000..5467468 --- /dev/null +++ b/immolate/main.cpp @@ -0,0 +1,282 @@ +#include "functions.hpp" +#include "search.hpp" +#include +#include +#include + +long filter(Instance inst) { + long legendaries = 0; + inst.nextPack(1); + for (int p = 1; p <= 3; p++) { + Pack pack = packInfo(inst.nextPack(1)); + if (pack.type == Item::Arcana_Pack) { + auto packContents = inst.nextArcanaPack(pack.size, 1); + for (int x = 0; x < pack.size; x++) { + if (packContents[x] == Item::The_Soul) + legendaries++; + } + } + if (pack.type == Item::Spectral_Pack) { + auto packContents = inst.nextSpectralPack(pack.size, 1); + for (int x = 0; x < pack.size; x++) { + if (packContents[x] == Item::The_Soul) + legendaries++; + } + } + } + return legendaries; +}; + +long filter_perkeo_observatory(Instance inst) { + if (inst.nextVoucher(1) == Item::Telescope) { + inst.activateVoucher(Item::Telescope); + if (inst.nextVoucher(2) != Item::Observatory) + return 0; + } else + return 0; + int antes[5] = {1, 1, 2, 2, 2}; + for (int i = 0; i < 5; i++) { + Pack pack = packInfo(inst.nextPack(antes[i])); + std::vector packContents; + if (pack.type == Item::Arcana_Pack) { + packContents = inst.nextArcanaPack(pack.size, antes[i]); + } else if (pack.type == Item::Spectral_Pack) { + packContents = inst.nextSpectralPack(pack.size, antes[i]); + } else + continue; + for (int x = 0; x < pack.size; x++) { + if (packContents[x] == Item::The_Soul && + inst.nextJoker(ItemSource::Soul, antes[i], true).joker == + Item::Perkeo) + return 1; + } + } + return 0; +} + +long filter_negative_tag(Instance inst) { + // Note: If the score cutoff was passed as a variable, this code could be + // significantly optimized + int maxAnte = 20; + int score = 0; + for (int i = 2; i <= maxAnte; i++) { + if (inst.nextTag(i) == Item::Negative_Tag) + score++; + } + return score; +} + +long filter_lucky(Instance inst) { + for (int i = 0; i < 7; i++) { + if (inst.random(RandomType::Lucky_Money) >= 1.0/15) { + return 0; + } + } + return 1; +} + +long filter_suas_speedrun(Instance inst) { + // First four cards in shop must include Mr. Bones, Merry Andy, and Luchador + bool bones = false, andy = false, luchador = false; + for (int i = 0; i < 4; i++) { + ShopItem item = inst.nextShopItem(2); + if (item.item == Item::Mr_Bones) + bones = true; + if (item.item == Item::Merry_Andy) + andy = true; + if (item.item == Item::Luchador) + luchador = true; + } + if (!bones || !andy || !luchador) + return 0; + // Ante 1 must have a Coupon Tag + inst.initLocks(1, false, true); + bool coupon = false; + for (int i = 0; i < 2; i++) { + if (inst.nextTag(1) == Item::Coupon_Tag) + coupon = true; + } + if (!coupon) + return 1; + // Ante 2 Boss must be The Wall + inst.nextBoss(1); + inst.initUnlocks(2, false); + if (inst.nextBoss(2) != Item::The_Wall) + return 2; + return 3; +} + +long filter_cavendish(Instance inst) { + inst.initLocks(1, false, false); + // Check for a Charm Tag (Arcana Pack) + if (inst.nextTag(1) != Item::Charm_Tag) + return 0; + // Check for a Judgement within that pack + std::vector packContents = inst.nextArcanaPack(5, 1); + bool hasJudgement = false; + for (int i = 0; i < 5; i++) { + if (packContents[i] == Item::Judgement) + hasJudgement = true; + } + if (!hasJudgement) + return 1; + // Check for Gros Michel + if (inst.nextJoker(ItemSource::Judgement, 1, false).joker != Item::Gros_Michel) + return 2; + // Check for Gros Michel break + if (inst.random(RandomType::Gros_Michel) >= 1.0/6) + return 3; + // Check for Cavendish in first shop + if (inst.nextShopItem(1).item != Item::Cavendish || inst.nextShopItem(1).item != Item::Cavendish) + return 4; + // Check for Cavendish break + if (inst.random(RandomType::Cavendish) < 1.0/1000) + return 9999; + return 5; +} + +long filter_blank(Instance inst) { return 0; } + +// These won't be permanent filters, just ones I sub in and out while JSON +// filters aren't ready yet +long filter_test(Instance inst) { + // Four Fingers, Shortcut, and Smeared Joker in first two antes + // (https://discord.com/channels/1325151824638120007/1326284714125955183) + bool fingers = false; + bool shortcut = false; + bool smeared = false; + // 4 chances in Ante 1, 6 chances in Ante 2, so no rerolling + for (int i = 0; i < 4; i++) { + ShopItem item = inst.nextShopItem(1); + if (item.item == Item::Four_Fingers) { + fingers = true; + }; + if (item.item == Item::Shortcut) { + shortcut = true; + }; + if (item.item == Item::Smeared_Joker) { + smeared = true; + }; + } + for (int i = 0; i < 6; i++) { + ShopItem item = inst.nextShopItem(2); + if (item.item == Item::Four_Fingers) { + fingers = true; + }; + if (item.item == Item::Shortcut) { + shortcut = true; + }; + if (item.item == Item::Smeared_Joker) { + smeared = true; + }; + } + if (fingers && shortcut && smeared) { + return 1; + } + return 0; +} + +// Benchmark function +// Runs 1 billion seeds of perkeo observatory +// And prints total time and seeds per second +void benchmark() { + long total = 0; + long start = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + Search search(filter_perkeo_observatory, "IMMOLATE", 12, 1000000000); + search.highScore = 10; // No output + search.printDelay = 100000000000; + search.search(); + long end = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + std::cout << "------LONGER TESTING------\n"; + std::cout << "Total time: " << end - start << "ms\n"; + std::cout << "Seeds per second: " << std::fixed << std::setprecision(0) + << 1000000000 / ((end - start) / 1000.0) << "\n"; +} + +void benchmark_quick() { + long total = 0; + long start = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + Search search(filter_perkeo_observatory, "IMMOLATE", 12, 100000000); + search.highScore = 10; // No output + search.printDelay = 100000000000; + search.search(); + long end = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + std::cout << "----PERKEO OBSERVATORY----\n"; + std::cout << "Total time: " << end - start << "ms\n"; + std::cout << "Seeds per second: " << std::fixed << std::setprecision(0) + << 100000000 / ((end - start) / 1000.0) << "\n"; +} + +void benchmark_quick_lucky() { + long total = 0; + long start = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + Search search(filter_lucky, "IMMOLATE", 12, 100000000); + search.highScore = 10; // No output + search.printDelay = 100000000000; + search.search(); + long end = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + std::cout << "-------LUCKY CARDS-------\n"; + std::cout << "Total time: " << end - start << "ms\n"; + std::cout << "Seeds per second: " << std::fixed << std::setprecision(0) + << 100000000 / ((end - start) / 1000.0) << "\n"; +} + +void benchmark_single() { + long total = 0; + long start = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + Search search(filter_perkeo_observatory, "IMMOLATE", 1, 10000000); + search.highScore = 10; // No output + search.printDelay = 100000000000; + search.search(); + long end = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + std::cout << "----SINGLE THREADED PO----\n"; + std::cout << "Total time: " << end - start << "ms\n"; + std::cout << "Seeds per second: " << std::fixed << std::setprecision(0) + << 10000000 / ((end - start) / 1000.0) << "\n"; +} + +void benchmark_blank() { + long total = 0; + long start = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + Search search(filter_blank, "IMMOLATE", 12, 100000000); + search.printDelay = 100000000000; // No output + search.search(); + long end = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + std::cout << "-------BLANK FILTER-------\n"; + std::cout << "Total time: " << end - start << "ms\n"; + std::cout << "Seeds per second: " << std::fixed << std::setprecision(0) + << 100000000 / ((end - start) / 1000.0) << "\n"; +} + +int main() { + /*benchmark_single(); + benchmark_quick(); + benchmark_quick_lucky(); + benchmark_blank(); + benchmark();*/ + Search search(filter_cavendish, "11111J31", 8, 2318107019761); + search.highScore = 5; + search.printDelay = 2318107019761; + search.search(); + return 1; +} \ No newline at end of file diff --git a/immolate/rng.cpp b/immolate/rng.cpp new file mode 100644 index 0000000..6facfb1 --- /dev/null +++ b/immolate/rng.cpp @@ -0,0 +1,67 @@ +#include "rng.hpp" + +const std::string ItemSource::Shop = "sho"; +const std::string ItemSource::Emperor = "emp"; +const std::string ItemSource::High_Priestess = "pri"; +const std::string ItemSource::Judgement = "jud"; +const std::string ItemSource::Wraith = "wra"; +const std::string ItemSource::Arcana_Pack = "ar1"; +const std::string ItemSource::Omen_Globe = "ar2"; +const std::string ItemSource::Celestial_Pack = "pl1"; +const std::string ItemSource::Spectral_Pack = "spe"; +const std::string ItemSource::Standard_Pack = "sta"; +const std::string ItemSource::Buffoon_Pack = "buf"; +const std::string ItemSource::Vagabond = "vag"; +const std::string ItemSource::Superposition = "sup"; +const std::string ItemSource::_8_Ball = "8ba"; +const std::string ItemSource::Seance = "sea"; +const std::string ItemSource::Sixth_Sense = "sixth"; +const std::string ItemSource::Top_Up = "top"; +const std::string ItemSource::Rare_Tag = "rta"; +const std::string ItemSource::Uncommon_Tag = "uta"; +const std::string ItemSource::Purple_Seal = "8ba"; +const std::string ItemSource::Soul = "sou"; +const std::string ItemSource::Riff_Raff = "rif"; +const std::string ItemSource::Cartomancer = "car"; + +const std::string RandomType::Joker_Common = "Joker1"; +const std::string RandomType::Joker_Uncommon = "Joker2"; +const std::string RandomType::Joker_Rare = "Joker3"; +const std::string RandomType::Joker_Legendary = "Joker4"; +const std::string RandomType::Joker_Rarity = "rarity"; +const std::string RandomType::Joker_Edition = "edi"; +const std::string RandomType::Misprint = "misprint"; +const std::string RandomType::Standard_Has_Enhancement = "stdset"; +const std::string RandomType::Enhancement = "Enhanced"; +const std::string RandomType::Card = "front"; +const std::string RandomType::Standard_Edition = "standard_edition"; +const std::string RandomType::Standard_Has_Seal = "stdseal"; +const std::string RandomType::Standard_Seal = "stdsealtype"; +const std::string RandomType::Shop_Pack = "shop_pack"; +const std::string RandomType::Tarot = "Tarot"; +const std::string RandomType::Spectral = "Spectral"; +const std::string RandomType::Tags = "Tag"; +const std::string RandomType::Shuffle_New_Round = "nr"; +const std::string RandomType::Card_Type = "cdt"; +const std::string RandomType::Planet = "Planet"; +const std::string RandomType::Lucky_Mult = "lucky_mult"; +const std::string RandomType::Lucky_Money = "lucky_money"; +const std::string RandomType::Sigil = "sigil"; +const std::string RandomType::Ouija = "ouija"; +const std::string RandomType::Wheel_of_Fortune = "wheel_of_fortune"; +const std::string RandomType::Gros_Michel = "gros_michel"; +const std::string RandomType::Cavendish = "cavendish"; +const std::string RandomType::Voucher = "Voucher"; +const std::string RandomType::Voucher_Tag = "Voucher_fromtag"; +const std::string RandomType::Orbital_Tag = "orbital"; +const std::string RandomType::Soul = "soul_"; +const std::string RandomType::Erratic = "erratic"; +const std::string RandomType::Eternal = + "stake_shop_joker_eternal"; // Eternal jokers pre 1.0.1 +const std::string RandomType::Perishable = "ssjp"; +const std::string RandomType::Rental = "ssjr"; +const std::string RandomType::Eternal_Perishable = "etperpoll"; +const std::string RandomType::Rental_Pack = "packssjr"; +const std::string RandomType::Eternal_Perishable_Pack = "packetper"; +const std::string RandomType::Boss = "boss"; +const std::string RandomType::Omen_Globe = "omen_globe"; \ No newline at end of file diff --git a/immolate/rng.hpp b/immolate/rng.hpp new file mode 100644 index 0000000..df4085b --- /dev/null +++ b/immolate/rng.hpp @@ -0,0 +1,75 @@ +#ifndef RNG_HPP +#define RNG_HPP + +#include + +struct ItemSource { + static const std::string Shop; + static const std::string Emperor; + static const std::string High_Priestess; + static const std::string Judgement; + static const std::string Wraith; + static const std::string Arcana_Pack; + static const std::string Omen_Globe; + static const std::string Celestial_Pack; + static const std::string Spectral_Pack; + static const std::string Standard_Pack; + static const std::string Buffoon_Pack; + static const std::string Vagabond; + static const std::string Superposition; + static const std::string _8_Ball; + static const std::string Seance; + static const std::string Sixth_Sense; + static const std::string Top_Up; + static const std::string Rare_Tag; + static const std::string Uncommon_Tag; + static const std::string Purple_Seal; + static const std::string Soul; + static const std::string Riff_Raff; + static const std::string Cartomancer; +}; + +struct RandomType { + static const std::string Joker_Common; + static const std::string Joker_Uncommon; + static const std::string Joker_Rare; + static const std::string Joker_Legendary; + static const std::string Joker_Rarity; + static const std::string Joker_Edition; + static const std::string Misprint; + static const std::string Standard_Has_Enhancement; + static const std::string Enhancement; + static const std::string Card; + static const std::string Standard_Edition; + static const std::string Standard_Has_Seal; + static const std::string Standard_Seal; + static const std::string Shop_Pack; + static const std::string Tarot; + static const std::string Spectral; + static const std::string Tags; + static const std::string Shuffle_New_Round; + static const std::string Card_Type; + static const std::string Planet; + static const std::string Lucky_Mult; + static const std::string Lucky_Money; + static const std::string Sigil; + static const std::string Ouija; + static const std::string Wheel_of_Fortune; + static const std::string Gros_Michel; + static const std::string Cavendish; + static const std::string Voucher; + static const std::string Voucher_Tag; + static const std::string Orbital_Tag; + static const std::string Soul; + static const std::string Erratic; + static const std::string Eternal; // Eternal jokers pre 1.0.1 + static const std::string Perishable; + static const std::string Rental; + static const std::string Eternal_Perishable; + static const std::string Rental_Pack; + static const std::string Eternal_Perishable_Pack; + static const std::string Boss; + static const std::string Omen_Globe; +}; + +#endif // RNG_HPP \ No newline at end of file diff --git a/immolate/search.cpp b/immolate/search.cpp new file mode 100644 index 0000000..e69de29 diff --git a/immolate/search.hpp b/immolate/search.hpp new file mode 100644 index 0000000..4c96329 --- /dev/null +++ b/immolate/search.hpp @@ -0,0 +1,111 @@ +#ifndef SEARCH_HPP +#define SEARCH_HPP + +#include "instance.hpp" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +const long long BLOCK_SIZE = 1000000; + +class Search { +public: + std::atomic seedsProcessed{0}; + std::atomic highScore{1}; + long long printDelay = 10000000; + std::function filter; + std::atomic found{false}; // Atomic flag to signal when a solution is found + Seed foundSeed; // Store the found seed + bool exitOnFind = false; + long long startSeed; + int numThreads; + long long numSeeds; + std::mutex mtx; + std::atomic nextBlock{0}; // Shared index for the next block to be processed + + Search(std::function f) { + filter = f; + startSeed = 0; + numThreads = 1; + numSeeds = 2318107019761; + } + + Search(std::function f, int t) { + filter = f; + startSeed = 0; + numThreads = t; + numSeeds = 2318107019761; + } + + Search(std::function f, int t, long long n) { + filter = f; + startSeed = 0; + numThreads = t; + numSeeds = n; + }; + Search(std::function f, std::string seed, int t, long long n) { + filter = f; + startSeed = Seed(seed).getID(); + numThreads = t; + numSeeds = n; + }; + + void searchBlock(long long start, long long end) { + Seed s = Seed(start); + Instance inst(s); + for (long long i = start; i < end; ++i) { + if (found) return; // Exit if a solution is found + // Perform the search on the seed + int result = filter(inst); + if (result >= highScore) { + std::lock_guard lock(mtx); + highScore = result; + foundSeed = s; + std::cout << "Found seed: " << s.tostring() << " (" << result << ")" + << std::endl; + if (exitOnFind) { + found = true; + return; + } + } + seedsProcessed++; + if (seedsProcessed % printDelay == 0) { + std::cout << "Seeds processed: " << seedsProcessed << std::endl; + } + inst.next(); + } + } + + std::string search() { + std::vector threads; + long long totalBlocks = (numSeeds + BLOCK_SIZE - 1) / BLOCK_SIZE; + for (int t = 0; t < numThreads; t++) { + threads.emplace_back([this, totalBlocks]() { + while (true) { + long long block = nextBlock.fetch_add(1); + if (block >= totalBlocks) break; + long long start = block * BLOCK_SIZE + startSeed; + long long end = std::min(start + BLOCK_SIZE, numSeeds + startSeed); + searchBlock(start, end); + } + }); + } + + for (auto& thread : threads) { + thread.join(); + } + + return foundSeed.tostring(); + } +}; + +#endif \ No newline at end of file diff --git a/immolate/seed.cpp b/immolate/seed.cpp new file mode 100644 index 0000000..0595dba --- /dev/null +++ b/immolate/seed.cpp @@ -0,0 +1,112 @@ +#include "seed.hpp" +#include "util.hpp" +#include + +Seed::Seed() { + seed.fill(-1); + length = 0; + for (int i = 0; i < 8; i++) { + cache[i].fill(-1); + } +} + +Seed::Seed(std::string strSeed) { + seed.fill(-1); + length = strSeed.size(); + for (int i = 0; i < 8; i++) { + cache[i].fill(-1); + } + // Note: Assumes this is safe + for (long unsigned int i = 0; i < strSeed.size(); i++) { + seed[strSeed.size() - 1 - i] = charSeeds[strSeed[i]]; + } +} + +Seed::Seed(long long id) { + length = 0; + for (int i = 0; i < 8; i++) { + cache[i].fill(-1); + } + for (int i = 0; i < 8; i++) { + if (id > 0) { + length++; + seed[i] = (id - 1) / idCoeff[i]; + id -= 1 + seed[i] * idCoeff[i]; + } else { + seed[i] = -1; + } + } +} + +std::string Seed::tostring() { + std::string strSeed; + for (int i = 7; i >= 0; i--) { + if (seed[i] != -1) { + strSeed.push_back(seedChars[seed[i]]); + } + } + return strSeed; +} + +void Seed::debugprint() { + for (int i = 0; i < 8; i++) { + std::cout << seed[i] << " "; + } + std::cout << std::endl; +} + +long long Seed::getID() { + long long id = 0; + for (int i = 0; i <= 7; i++) { + if (seed[i] >= 0) { + id += idCoeff[i] * seed[i] + 1; + } + } + return id; +} + +void Seed::next() { + if (length < 8) { + seed[length] = 0; + length++; + } else { + int i = 7; + while (i >= 0) { + cache[i].fill(-1); + if (seed[i] == 34) { + seed[i] = -1; + length--; + } else { + seed[i]++; + break; + } + i--; + } + } +} + +// Not optimized for performance +// I don't think this will need to be implemented in searching +void Seed::next(int x) { + long long newID = (getID() + x) % 2318107019761; + *this = Seed(newID); +} + +double Seed::pseudohash(int prefixLength) { + if (length == 0) return 1; //Empty seed edge case + + if (cache[length-1][prefixLength+length-1] == -1) { + int i = length - 2; + while (i >= 0 && cache[i][prefixLength+length-1] == -1) { + i--; + } + if (i == -1) { + cache[0][prefixLength+length-1] = pseudostep(seedChars[seed[0]], prefixLength+length, 1); + i = 0; + } + for (int j = i+1; j < length; j++) { + cache[j][prefixLength+length-1] = pseudostep(seedChars[seed[j]], prefixLength+length-j, cache[j-1][prefixLength+length-1]); + } + } + return cache[length-1][prefixLength+length-1]; +} \ No newline at end of file diff --git a/immolate/seed.hpp b/immolate/seed.hpp new file mode 100644 index 0000000..8e184be --- /dev/null +++ b/immolate/seed.hpp @@ -0,0 +1,49 @@ +#ifndef SEED_HPP +#define SEED_HPP + +#include +#include + +// Seed helper class +// Caches hashing info recursively to save speed +// Because of that, also has an interesting order for seeds: +// , 1, 11, 111, ..., 11111111, 21111111, 31111111, ..., Z1111111, +// 2111111, 12111111, ..., ZZ111111, 211111, ..., ZZZZZZZZ +const std::string seedChars = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const std::array charSeeds = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, + 8, -1, -1, -1, -1, -1, -1, -1, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; +const std::array idCoeff = { + 66231629136, 1892332261, 54066636, 1544761, 44136, 1261, 36, 1}; + +struct Seed { + // -1 is blank, 0 to 34 represent valid characters + // To aid in hashing, stored right to left + std::array seed; + + int length; + + // The cache. Stored as [position in seed][length of string] + std::array, 8> cache; + + Seed(); + Seed(std::string strSeed); + Seed(long long id); + + std::string tostring(); + void debugprint(); + long long getID(); + + void next(); + void next(int x); + + double pseudohash(int prefixLength); +}; + +#endif // SEED_HPP \ No newline at end of file diff --git a/immolate/util.cpp b/immolate/util.cpp new file mode 100644 index 0000000..82df8f6 --- /dev/null +++ b/immolate/util.cpp @@ -0,0 +1,178 @@ +#include "util.hpp" +#include +#include + +LuaRandom::LuaRandom(double seed) { + double d = seed; + uint64_t r = 0x11090601; + for (int i = 0; i < 4; i++) { + uint64_t m = 1ull << (r & 255); + r >>= 8; + d = d * 3.14159265358979323846 + 2.7182818284590452354; + dbllong u; + u.dbl = d; + if (u.ulong < m) + u.ulong += m; + state[i] = u.ulong; + } + for (int i = 0; i < 10; i++) { + _randint(); + } +} + +LuaRandom::LuaRandom() { LuaRandom(0); } + +uint64_t LuaRandom::_randint() { + uint64_t z = 0; + uint64_t r = 0; + z = state[0]; + z = (((z << 31ull) ^ z) >> 45ull) ^ ((z & (MAX_UINT64 << 1ull)) << 18ull); + r ^= z; + state[0] = z; + z = state[1]; + z = (((z << 19ull) ^ z) >> 30ull) ^ ((z & (MAX_UINT64 << 6ull)) << 28ull); + r ^= z; + state[1] = z; + z = state[2]; + z = (((z << 24ull) ^ z) >> 48ull) ^ ((z & (MAX_UINT64 << 9ull)) << 7ull); + r ^= z; + state[2] = z; + z = state[3]; + z = (((z << 21ull) ^ z) >> 39ull) ^ ((z & (MAX_UINT64 << 17ull)) << 8ull); + r ^= z; + state[3] = z; + return r; +} + +uint64_t LuaRandom::randdblmem() { + return (_randint() & 4503599627370495ull) | 4607182418800017408ull; +} + +double LuaRandom::random() { + dbllong u; + u.ulong = randdblmem(); + return u.dbl - 1.0; +} + +int LuaRandom::randint(int min, int max) { + return (int)(random() * (max - min + 1)) + min; +} + +int portable_clzll(uint64_t x) { + if (x == 0) + return 64; // Undefined for 0, by convention we return 64 + +#if defined(__GNUC__) || defined(__clang__) + return __builtin_clzll(x); +#elif defined(_MSC_VER) + unsigned long index; + if (_BitScanReverse64(&index, x)) { + return 63 - index; + } + return 64; +#else + // Fallback for other compilers (manual bit manipulation) + int n = 0; + if (x <= 0x00000000FFFFFFFF) { + n += 32; + x <<= 32; + } + if (x <= 0x0000FFFFFFFFFFFF) { + n += 16; + x <<= 16; + } + if (x <= 0x00FFFFFFFFFFFFFF) { + n += 8; + x <<= 8; + } + if (x <= 0x0FFFFFFFFFFFFFFF) { + n += 4; + x <<= 4; + } + if (x <= 0x3FFFFFFFFFFFFFFF) { + n += 2; + x <<= 2; + } + if (x <= 0x7FFFFFFFFFFFFFFF) { + n += 1; + } + return n; +#endif +} + +double fract(double x) { + uint64_t x_int; + + std::memcpy(&x_int, &x, sizeof(x_int)); + + uint64_t expo = (x_int & DBL_EXPO) >> DBL_MANT_SZ; + if (expo < DBL_EXPO_BIAS) { + return x; + } + if (expo == ((1 << DBL_EXPO_SZ) - 1)) { + return std::numeric_limits::quiet_NaN(); + } + uint64_t expo_biased = expo - DBL_EXPO_BIAS; + if (expo_biased >= DBL_MANT_SZ) { + return 0; + } + uint64_t mant = x_int & DBL_MANT; + uint64_t frac_mant = mant & ((1ull << (DBL_MANT_SZ - expo_biased)) - 1); + if (frac_mant == 0) { + return 0; + } + uint64_t frac_lzcnt = portable_clzll(frac_mant) - (64 - DBL_MANT_SZ); + uint64_t res_expo = (expo - frac_lzcnt - 1) << DBL_MANT_SZ; + uint64_t res_mant = (frac_mant << (frac_lzcnt + 1)) & DBL_MANT; + uint64_t res = res_expo | res_mant; + + double result; + std::memcpy(&result, &res, sizeof(result)); + return result; +} + +double pseudohash(std::string s) { + double num = 1; + for (size_t i = s.length(); i > 0; i--) { + num = fract(1.1239285023 / num * s[i - 1] * 3.141592653589793116 + + 3.141592653589793116 * i); + } + return num; +} + +double pseudohash_from(std::string s, double num) { + for (size_t i = s.length(); i > 0; i--) { + num = fract(1.1239285023 / num * s[i - 1] * 3.141592653589793116 + + 3.141592653589793116 * i); + } + return num; +} + +double pseudostep(char s, int pos, double num) { + return fract(1.1239285023 / num * s * 3.141592653589793116 + + 3.141592653589793116 * pos); +} + +std::string anteToString(int a) { + if (a < 10) + return {(char)(0x30 + a)}; + else + return {(char)(0x30 + a / 10), (char)(0x30 + a % 10)}; +} + +const double inv_prec = std::pow(10.0, 13); +const double two_inv_prec = std::pow(2.0, 13); +const double five_inv_prec = std::pow(5.0, 13); + +double round13(double x) { + double normal_case = std::round(x * inv_prec) / inv_prec; + if (normal_case == + (std::round(std::nextafter(x, -1) * inv_prec) / inv_prec)) { + return normal_case; + } + double truncated = fract(x * two_inv_prec) * five_inv_prec; + if (fract(truncated) >= 0.5) { + return (std::floor(x * inv_prec) + 1) / inv_prec; + } + return std::floor(x * inv_prec) / inv_prec; +} \ No newline at end of file diff --git a/immolate/util.hpp b/immolate/util.hpp new file mode 100644 index 0000000..4fbbe4e --- /dev/null +++ b/immolate/util.hpp @@ -0,0 +1,46 @@ +#ifndef UTIL_HPP +#define UTIL_HPP + +#include +#include +#include + +const uint64_t MAX_UINT64 = 18446744073709551615ull; + +typedef union DoubleLong { + double dbl; + uint64_t ulong; +} dbllong; + +struct LuaRandom { + uint64_t state[4]; + LuaRandom(double seed); + LuaRandom(); + uint64_t _randint(); + uint64_t randdblmem(); + double random(); + int randint(int min, int max); +}; + +#define DBL_EXPO 0x7FF0000000000000 +#define DBL_MANT 0x000FFFFFFFFFFFFF + +#define DBL_EXPO_SZ 11 +#define DBL_MANT_SZ 52 + +#define DBL_EXPO_BIAS 1023 + +#if defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanReverse64) +#endif + +int portable_clzll(uint64_t x); +double fract(double x); +double pseudohash(std::string s); +double pseudohash_from(std::string s, double num); +double pseudostep(char s, int pos, double num); +std::string anteToString(int a); +double round13(double x); + +#endif // UTIL_HPP \ No newline at end of file From 1e1e5d9334db906a3cdd59ab5573dc874a20ea15 Mon Sep 17 00:00:00 2001 From: OceanRamen Date: Sun, 14 Jun 2026 02:38:47 +0100 Subject: [PATCH 4/5] Add immolate/ and .github/ to .gitignore Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 08656cb..395c103 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ style_guide.md Debug/ config.lua .vscode +immolate/ +.github/ From 0bdfe116100b25b2b3833c0497207bf2a73ac689 Mon Sep 17 00:00:00 2001 From: OceanRamen Date: Mon, 15 Jun 2026 16:59:38 +0100 Subject: [PATCH 5/5] Update Brainstorm to use new Immolate2 API The Immolate DLL was rebuilt with a simplified brainstorm() C API: old: brainstorm(seed, voucher, pack, tag, souls, observatory, perkeo) new: brainstorm(seed, pack, tag, souls) Voucher, observatory, and perkeo parameters were removed from the legacy simple filter path; complex filtering is handled via the existing brainstorm_query() JSON filter system. Changes: - Update ffi.cdef to match new brainstorm() signature - Remove voucher/observatory/perkeo logic from autoReroll() - Drop voucher_name, voucher_id, inst_observatory, inst_perkeo, legendary_choice, legendary_id from DEFAULT_CONFIG.ar_filters - Remove VOUCHER SEARCH and LEGENDARY UI cycle controls - Remove now-dead callback functions and lookup tables from ui.lua - Replace Immolate.dll with the new build - Update immolate/immolate.cpp with the new implementation Co-Authored-By: Claude Sonnet 4.6 --- Core/Brainstorm.lua | 50 ++---- Immolate.dll | Bin 108032 -> 158208 bytes UI/ui.lua | 75 --------- immolate/immolate.cpp | 380 +++++++++++++++++++++++++++++++++++------- 4 files changed, 336 insertions(+), 169 deletions(-) diff --git a/Core/Brainstorm.lua b/Core/Brainstorm.lua index f307cb3..c612a9f 100644 --- a/Core/Brainstorm.lua +++ b/Core/Brainstorm.lua @@ -19,15 +19,9 @@ Brainstorm.DEFAULT_CONFIG = { ar_filters = { pack = {}, pack_id = 1, - voucher_name = "", - voucher_id = 1, tag_name = "tag_charm", tag_id = 2, soul_skip = 1, - inst_observatory = false, - inst_perkeo = false, - legendary_choice = "None", - legendary_id = 1, }, ar_prefs = { spf_id = 3, @@ -78,9 +72,7 @@ local function ensureImmolateLoaded() end if not Brainstorm.immolate_cdef then - ffi.cdef( - [[const char* brainstorm(const char* seed, const char* voucher, const char* pack, const char* tag, double souls, bool observatory, bool perkeo);]] - ) + ffi.cdef([[const char* brainstorm(const char* seed, const char* pack, const char* tag, double souls);]]) ffi.cdef([[const char* brainstorm_query(const char* seed, const char* query_json);]]) ffi.cdef([[void free_result(const char* result);]]) Brainstorm.immolate_cdef = true @@ -330,29 +322,16 @@ function Brainstorm.autoReroll() local seed_found = random_string(8, entropy) local filters = Brainstorm.config.ar_filters - local perkeo_flag = filters.inst_perkeo or false - if filters.legendary_choice then - if filters.legendary_choice == "Perkeo" or filters.legendary_choice == "Perkeo + Observatory" then - perkeo_flag = true - else - perkeo_flag = false - end - end - local pack_key = "" - if #filters.pack > 0 then - pack_key = filters.pack[1]:match("^(.*)_") or "" - end - local pack_name = localize({ type = "name_text", set = "Other", key = pack_key }) or "" - local tag_name = localize({ - type = "name_text", - set = "Tag", - key = filters.tag_name, - }) or "" - local voucher_name = localize({ - type = "name_text", - set = "Voucher", - key = filters.voucher_name, - }) or "" + local pack_key = "" + if #filters.pack > 0 then + pack_key = filters.pack[1]:match("^(.*)_") or "" + end + local pack_name = localize({ type = "name_text", set = "Other", key = pack_key }) or "" + local tag_name = localize({ + type = "name_text", + set = "Tag", + key = filters.tag_name, + }) or "" if not ensureImmolateLoaded() or not Brainstorm.immolate.brainstorm then Brainstorm.ar_active = false @@ -367,12 +346,9 @@ function Brainstorm.autoReroll() else seed_ptr = Brainstorm.immolate.brainstorm( seed_found, - voucher_name, pack_name, tag_name, - filters.soul_skip, - filters.inst_observatory, - perkeo_flag + filters.soul_skip ) end if not seed_ptr then @@ -400,8 +376,6 @@ function Brainstorm.autoReroll() pack_name, tag_name, filters.soul_skip, - filters.inst_observatory, - filters.legendary_choice, }, } G.GAME.seeded = false diff --git a/Immolate.dll b/Immolate.dll index cafd09b915c541c20939cba80015f8146f811a9e..3f450a3ce3b461fd9650ebfff7b9be85df0aeafc 100644 GIT binary patch literal 158208 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Ξ*_GdgBR{%ba1-`1u!Zqd^ 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 diff --git a/UI/ui.lua b/UI/ui.lua index 5061d87..03dc641 100644 --- a/UI/ui.lua +++ b/UI/ui.lua @@ -20,25 +20,6 @@ local tag_list = { ["D6 Tag"] = "tag_d_six", } -local voucher_list = { - ["None"] = "", - ["Overstock"] = "v_overstock_norm", - ["Clearance Sale"] = "v_clearance_sale", - ["Hone"] = "v_hone", - ["Reroll Surplus"] = "v_reroll_surplus", - ["Crystal Ball"] = "v_crystal_ball", - ["Telescope"] = "v_telescope", - ["Grabber"] = "v_grabber", - ["Wasteful"] = "v_wasteful", - ["Tarot Merchant"] = "v_tarot_merchant", - ["Planet Merchant"] = "v_planet_merchant", - ["Seed Money"] = "v_seed_money", - ["Blank"] = "v_blank", - ["Magic Trick"] = "v_magic_trick", - ["Hieroglyph"] = "v_hieroglyph", - ["Director's Cut"] = "v_directors_cut", - ["Paint Brush"] = "v_paint_brush", -} local pack_list = { ["None"] = {}, ["Normal Arcana"] = { @@ -80,34 +61,6 @@ local spf_list = { local spf_keys = { "500", "750", "1000" } -local legendary_list = { - ["None"] = "", - ["Perkeo"] = "perkeo", - ["Observatory"] = "observatory", - ["Perkeo + Observatory"] = "perkeo_observatory", -} -local legendary_keys = { "None", "Perkeo", "Observatory", "Perkeo + Observatory" } - -local voucher_keys = { - "None", - "Overstock", - "Clearance Sale", - "Hone", - "Reroll Surplus", - "Crystal Ball", - "Telescope", - "Grabber", - "Wasteful", - "Tarot Merchant", - "Planet Merchant", - "Seed Money", - "Blank", - "Magic Trick", - "Hieroglyph", - "Director's Cut", - "Paint Brush", -} - local tag_keys = { "None", "Charm Tag", @@ -177,12 +130,6 @@ G.FUNCS.change_active_filter = function(x) Brainstorm.writeConfig() end -G.FUNCS.change_target_voucher = function(x) - Brainstorm.config.ar_filters.voucher_id = x.to_key - Brainstorm.config.ar_filters.voucher_name = voucher_list[x.to_val] - Brainstorm.writeConfig() -end - G.FUNCS.change_target_pack = function(x) Brainstorm.config.ar_filters.pack_id = x.to_key Brainstorm.config.ar_filters.pack = pack_list[x.to_val] @@ -195,12 +142,6 @@ G.FUNCS.change_target_tag = function(x) Brainstorm.writeConfig() end -G.FUNCS.change_target_legendary = function(x) - Brainstorm.config.ar_filters.legendary_id = x.to_key - Brainstorm.config.ar_filters.legendary_choice = legendary_keys[x.to_key] or "None" - Brainstorm.writeConfig() -end - G.FUNCS.change_soul_count = function(x) Brainstorm.config.ar_filters.soul_skip = x.to_val Brainstorm.writeConfig() @@ -256,14 +197,6 @@ function create_tabs(args) opt_callback = "change_target_tag", current_option = Brainstorm.config.ar_filters.tag_id or 1, }), - create_option_cycle({ - label = "VOUCHER SEARCH", - scale = 0.8, - w = 4, - options = voucher_keys, - opt_callback = "change_target_voucher", - current_option = Brainstorm.config.ar_filters.voucher_id or 1, - }), create_option_cycle({ label = "PACK SEARCH", scale = 0.8, @@ -272,14 +205,6 @@ function create_tabs(args) opt_callback = "change_target_pack", current_option = Brainstorm.config.ar_filters.pack_id or 1, }), - create_option_cycle({ - label = "LEGENDARY", - scale = 0.8, - w = 4, - options = legendary_keys, - opt_callback = "change_target_legendary", - current_option = Brainstorm.config.ar_filters.legendary_id or 1, - }), }, }, { diff --git a/immolate/immolate.cpp b/immolate/immolate.cpp index 82719d6..18244b4 100644 --- a/immolate/immolate.cpp +++ b/immolate/immolate.cpp @@ -4,6 +4,16 @@ #include #include +#ifdef _WIN32 + #ifdef BUILDING_DLL + #define IMMOLATE_API __declspec(dllexport) + #else + #define IMMOLATE_API __declspec(dllimport) + #endif +#else + #define IMMOLATE_API +#endif + Item BRAINSTORM_PACK = Item::RETRY; Item BRAINSTORM_TAG = Item::Charm_Tag; long BRAINSTORM_SOULS = 1; @@ -47,89 +57,241 @@ std::string tag, double souls) { BRAINSTORM_PACK = stringToItem(pack); return search.search(); } +// --------------------------------------------------------------------------- +// Step / filter tree +// --------------------------------------------------------------------------- + struct Step { std::string op; mini_json::Value args; + std::vector subSteps; // used by "all" and "any" combinator ops }; +// --------------------------------------------------------------------------- +// Matching helpers +// --------------------------------------------------------------------------- + static Item parseItemSafe(const mini_json::Value &v) { if (!v.isString()) return Item::RETRY; return stringToItem(v.getString()); } -static bool matchesItem(const Item actual, const mini_json::Value &args) { - const auto &eq = args["equals"]; +// Match a single Item against { "equals": "...", "in": [...] } +static bool matchesItem(Item actual, const mini_json::Value &cond) { + const auto &eq = cond["equals"]; if (eq.isString() && actual != parseItemSafe(eq)) return false; - const auto &inArr = args["in"]; + const auto &inArr = cond["in"]; if (inArr.isArray()) { bool ok = false; - for (const auto &el : inArr.array) { - if (el.isString() && actual == parseItemSafe(el)) { - ok = true; - break; - } - } + for (const auto &el : inArr.array) + if (el.isString() && actual == parseItemSafe(el)) { ok = true; break; } if (!ok) return false; } return true; } -static bool matchesJoker(const JokerData &jd, const mini_json::Value &match) { - if (!match.isObject()) return true; - if (match["joker"].isString() && jd.joker != parseItemSafe(match["joker"])) return false; - if (match["rarity"].isString() && jd.rarity != parseItemSafe(match["rarity"])) return false; - if (match["edition"].isString() && jd.edition != parseItemSafe(match["edition"])) return false; - const auto &stickers = match["stickers"]; +// Match a JokerData against { "joker": "...", "rarity": "...", "edition": "...", "stickers": {...} } +static bool matchesJoker(const JokerData &jd, const mini_json::Value &cond) { + if (!cond.isObject()) return true; + if (cond["joker"].isString() && jd.joker != parseItemSafe(cond["joker"])) return false; + if (cond["rarity"].isString() && jd.rarity != parseItemSafe(cond["rarity"])) return false; + if (cond["edition"].isString() && jd.edition != parseItemSafe(cond["edition"])) return false; + const auto &stickers = cond["stickers"]; if (stickers.isObject()) { - if (stickers["eternal"].isBool() && jd.stickers.eternal != stickers["eternal"].getBool()) return false; - if (stickers["perishable"].isBool() && jd.stickers.perishable != stickers["perishable"].getBool()) return false; - if (stickers["rental"].isBool() && jd.stickers.rental != stickers["rental"].getBool()) return false; + if (stickers["eternal"].isBool() && jd.stickers.eternal != stickers["eternal"].getBool()) return false; + if (stickers["perishable"].isBool() && jd.stickers.perishable != stickers["perishable"].getBool()) return false; + if (stickers["rental"].isBool() && jd.stickers.rental != stickers["rental"].getBool()) return false; } return true; } +// Match a Card against { "base": "...", "enhancement": "...", "edition": "...", "seal": "..." } +static bool matchesCard(const Card &card, const mini_json::Value &cond) { + if (!cond.isObject()) return true; + if (cond["base"].isString() && card.base != parseItemSafe(cond["base"])) return false; + if (cond["enhancement"].isString() && card.enhancement != parseItemSafe(cond["enhancement"])) return false; + if (cond["edition"].isString() && card.edition != parseItemSafe(cond["edition"])) return false; + if (cond["seal"].isString() && card.seal != parseItemSafe(cond["seal"])) return false; + return true; +} + +// Match a ShopItem against { "type": "...", "item": "...", "match": {joker cond} } +static bool matchesShopItem(const ShopItem &si, const mini_json::Value &cond) { + if (!cond.isObject()) return true; + if (cond["type"].isString() && si.type != parseItemSafe(cond["type"])) return false; + if (cond["item"].isString() && si.item != parseItemSafe(cond["item"])) return false; + if (cond["match"].isObject() && !matchesJoker(si.jokerData, cond["match"])) return false; + return true; +} + static long long getNumber(const mini_json::Value &v, long long def) { return v.isNumber() ? static_cast(v.number) : def; } +// Apply any/all/none/count sub-conditions over a vector pack. +// Sub-conditions use the same { "equals"/"in" } format as matchesItem. +static bool matchesItemPack(const std::vector &items, const mini_json::Value &args) { + const auto &anyCond = args["any"]; + if (anyCond.isObject()) { + bool found = false; + for (const auto &item : items) + if (matchesItem(item, anyCond)) { found = true; break; } + if (!found) return false; + } + const auto &allCond = args["all"]; + if (allCond.isObject()) { + for (const auto &item : items) + if (!matchesItem(item, allCond)) return false; + } + const auto &noneCond = args["none"]; + if (noneCond.isObject()) { + for (const auto &item : items) + if (matchesItem(item, noneCond)) return false; + } + const auto &countCond = args["count"]; + if (countCond.isObject()) { + const auto &matchCond = countCond["match"]; + int cnt = 0; + for (const auto &item : items) + if (!matchCond.isObject() || matchesItem(item, matchCond)) cnt++; + long long minV = getNumber(countCond["min"], -1); + long long maxV = getNumber(countCond["max"], -1); + long long eqV = getNumber(countCond["equals"], -1); + if (minV >= 0 && cnt < (int)minV) return false; + if (maxV >= 0 && cnt > (int)maxV) return false; + if (eqV >= 0 && cnt != (int)eqV) return false; + } + return true; +} + +// Apply any/all/none/count sub-conditions over a vector pack. +static bool matchesJokerPack(const std::vector &jokers, const mini_json::Value &args) { + const auto &anyCond = args["any"]; + if (anyCond.isObject()) { + bool found = false; + for (const auto &jd : jokers) + if (matchesJoker(jd, anyCond)) { found = true; break; } + if (!found) return false; + } + const auto &allCond = args["all"]; + if (allCond.isObject()) { + for (const auto &jd : jokers) + if (!matchesJoker(jd, allCond)) return false; + } + const auto &noneCond = args["none"]; + if (noneCond.isObject()) { + for (const auto &jd : jokers) + if (matchesJoker(jd, noneCond)) return false; + } + const auto &countCond = args["count"]; + if (countCond.isObject()) { + const auto &matchCond = countCond["match"]; + int cnt = 0; + for (const auto &jd : jokers) + if (!matchCond.isObject() || matchesJoker(jd, matchCond)) cnt++; + long long minV = getNumber(countCond["min"], -1); + long long maxV = getNumber(countCond["max"], -1); + long long eqV = getNumber(countCond["equals"], -1); + if (minV >= 0 && cnt < (int)minV) return false; + if (maxV >= 0 && cnt > (int)maxV) return false; + if (eqV >= 0 && cnt != (int)eqV) return false; + } + return true; +} + +// Apply any/all/none/count sub-conditions over a vector pack. +static bool matchesCardPack(const std::vector &cards, const mini_json::Value &args) { + const auto &anyCond = args["any"]; + if (anyCond.isObject()) { + bool found = false; + for (const auto &card : cards) + if (matchesCard(card, anyCond)) { found = true; break; } + if (!found) return false; + } + const auto &allCond = args["all"]; + if (allCond.isObject()) { + for (const auto &card : cards) + if (!matchesCard(card, allCond)) return false; + } + const auto &noneCond = args["none"]; + if (noneCond.isObject()) { + for (const auto &card : cards) + if (matchesCard(card, noneCond)) return false; + } + const auto &countCond = args["count"]; + if (countCond.isObject()) { + const auto &matchCond = countCond["match"]; + int cnt = 0; + for (const auto &card : cards) + if (!matchCond.isObject() || matchesCard(card, matchCond)) cnt++; + long long minV = getNumber(countCond["min"], -1); + long long maxV = getNumber(countCond["max"], -1); + long long eqV = getNumber(countCond["equals"], -1); + if (minV >= 0 && cnt < (int)minV) return false; + if (maxV >= 0 && cnt > (int)maxV) return false; + if (eqV >= 0 && cnt != (int)eqV) return false; + } + return true; +} + +// --------------------------------------------------------------------------- +// Step execution +// --------------------------------------------------------------------------- + static bool applyStep(const Step &step, Instance &inst) { const auto &args = step.args; + + // --- Combinators --- + + // "all": every sub-step must pass (AND) + if (step.op == "all") { + for (const auto &sub : step.subSteps) + if (!applyStep(sub, inst)) return false; + return true; + } + // "any": at least one sub-step must pass (OR) + if (step.op == "any") { + for (const auto &sub : step.subSteps) + if (applyStep(sub, inst)) return true; + return false; + } + + // --- Simple game-state ops --- + if (step.op == "tag") { int idx = static_cast(getNumber(args["index"], 1)); - Item val = inst.nextTag(idx); - return matchesItem(val, args); + return matchesItem(inst.nextTag(idx), args); } if (step.op == "pack") { int idx = static_cast(getNumber(args["index"], 1)); - Item val = inst.nextPack(idx); - return matchesItem(val, args); + return matchesItem(inst.nextPack(idx), args); } if (step.op == "voucher") { int idx = static_cast(getNumber(args["index"], 1)); Item val = inst.nextVoucher(idx); if (!matchesItem(val, args)) return false; - if (args["activate"].getBool(false)) { - inst.activateVoucher(val); - } + if (args["activate"].getBool(false)) inst.activateVoucher(val); return true; } if (step.op == "boss") { int idx = static_cast(getNumber(args["index"], 1)); - Item val = inst.nextBoss(idx); - return matchesItem(val, args); + return matchesItem(inst.nextBoss(idx), args); } + + // --- Individual card draws --- + + // "joker": draw (and optionally advance) joker draws. + // draw=N checks the 1st joker and advances through N draws total (original behaviour). if (step.op == "joker") { int draw = static_cast(getNumber(args["draw"], 1)); int ante = static_cast(getNumber(args["ante"], 1)); bool stickers = args["has_stickers"].getBool(true); std::string source = args["source"].getString("Brainstorm_Joker"); JokerData jd = inst.nextJoker(source, ante, stickers); - // Advance draws if draw > 1 - for (int i = 1; i < draw; i++) { - inst.nextJoker(source, ante, stickers); - } + for (int i = 1; i < draw; i++) inst.nextJoker(source, ante, stickers); return matchesJoker(jd, args["match"]); } + // "joker_window": search up to limit draws for any matching joker. if (step.op == "joker_window") { int limit = static_cast(getNumber(args["limit"], 1)); int ante = static_cast(getNumber(args["ante"], 1)); @@ -142,6 +304,105 @@ static bool applyStep(const Step &step, Instance &inst) { } return false; } + // "tarot": draw the Nth tarot card (draw=N skips N-1 then checks the Nth). + if (step.op == "tarot") { + int draw = static_cast(getNumber(args["draw"], 1)); + int ante = static_cast(getNumber(args["ante"], 1)); + bool soulable = args["soulable"].getBool(true); + std::string source = args["source"].getString("Brainstorm_Tarot"); + for (int i = 1; i < draw; i++) inst.nextTarot(source, ante, soulable); + return matchesItem(inst.nextTarot(source, ante, soulable), args); + } + // "planet": draw the Nth planet card. + if (step.op == "planet") { + int draw = static_cast(getNumber(args["draw"], 1)); + int ante = static_cast(getNumber(args["ante"], 1)); + bool soulable = args["soulable"].getBool(true); + std::string source = args["source"].getString("Brainstorm_Planet"); + for (int i = 1; i < draw; i++) inst.nextPlanet(source, ante, soulable); + return matchesItem(inst.nextPlanet(source, ante, soulable), args); + } + // "spectral": draw the Nth spectral card. + if (step.op == "spectral") { + int draw = static_cast(getNumber(args["draw"], 1)); + int ante = static_cast(getNumber(args["ante"], 1)); + bool soulable = args["soulable"].getBool(true); + std::string source = args["source"].getString("Brainstorm_Spectral"); + for (int i = 1; i < draw; i++) inst.nextSpectral(source, ante, soulable); + return matchesItem(inst.nextSpectral(source, ante, soulable), args); + } + // "shop_item": draw the Nth shop item. + if (step.op == "shop_item") { + int draw = static_cast(getNumber(args["draw"], 1)); + int ante = static_cast(getNumber(args["ante"], 1)); + for (int i = 1; i < draw; i++) inst.nextShopItem(ante); + return matchesShopItem(inst.nextShopItem(ante), args); + } + // "standard_card": draw the Nth standard playing card. + if (step.op == "standard_card") { + int draw = static_cast(getNumber(args["draw"], 1)); + int ante = static_cast(getNumber(args["ante"], 1)); + for (int i = 1; i < draw; i++) inst.nextStandardCard(ante); + return matchesCard(inst.nextStandardCard(ante), args); + } + + // --- Pack contents ops --- + // Each uses any/all/none/count sub-conditions on the pack's items. + + // "arcana_pack": generate a tarot/soul pack and inspect its contents. + if (step.op == "arcana_pack") { + int size = static_cast(getNumber(args["size"], 3)); + int ante = static_cast(getNumber(args["ante"], 1)); + return matchesItemPack(inst.nextArcanaPack(size, ante), args); + } + // "celestial_pack": generate a planet pack and inspect its contents. + if (step.op == "celestial_pack") { + int size = static_cast(getNumber(args["size"], 3)); + int ante = static_cast(getNumber(args["ante"], 1)); + return matchesItemPack(inst.nextCelestialPack(size, ante), args); + } + // "spectral_pack": generate a spectral pack and inspect its contents. + if (step.op == "spectral_pack") { + int size = static_cast(getNumber(args["size"], 2)); + int ante = static_cast(getNumber(args["ante"], 1)); + return matchesItemPack(inst.nextSpectralPack(size, ante), args); + } + // "buffoon_pack": generate a joker pack and inspect its contents. + if (step.op == "buffoon_pack") { + int size = static_cast(getNumber(args["size"], 2)); + int ante = static_cast(getNumber(args["ante"], 1)); + return matchesJokerPack(inst.nextBuffoonPack(size, ante), args); + } + // "standard_pack": generate a playing-card pack and inspect its contents. + if (step.op == "standard_pack") { + int size = static_cast(getNumber(args["size"], 3)); + int ante = static_cast(getNumber(args["ante"], 1)); + return matchesCardPack(inst.nextStandardPack(size, ante), args); + } + + // --- State mutation ops (always return true) --- + + // "init_locks": initialise lock/unlock state for a given ante/profile. + if (step.op == "init_locks") { + int ante = static_cast(getNumber(args["ante"], 1)); + bool freshProfile = args["fresh_profile"].getBool(false); + bool freshRun = args["fresh_run"].getBool(false); + inst.initLocks(ante, freshProfile, freshRun); + if (args["unlock"].getBool(false)) inst.initUnlocks(ante, freshProfile); + return true; + } + // "set": change deck or stake for subsequent draws. + if (step.op == "set") { + const auto &deck = args["deck"]; + const auto &stake = args["stake"]; + if (deck.isString()) inst.setDeck(stringToItem(deck.getString())); + if (stake.isString()) inst.setStake(stringToItem(stake.getString())); + else if (stake.isNumber()) inst.setStake(static_cast(static_cast(stake.number))); + return true; + } + + // --- Seed state inspection --- + if (step.op == "state") { std::string field = args["field"].getString(""); if (field == "id") { @@ -157,38 +418,46 @@ static bool applyStep(const Step &step, Instance &inst) { } return true; } - if (step.op == "set") { - const auto &deck = args["deck"]; - const auto &stake = args["stake"]; - if (deck.isString()) inst.setDeck(stringToItem(deck.getString())); - if (stake.isString() || stake.isNumber()) { - if (stake.isString()) { - inst.setStake(stringToItem(stake.getString())); - } else { - inst.setStake(static_cast(static_cast(stake.number))); - } - } - return true; - } - // Unknown op -> fail safely + + // Unknown op → fail safely so malformed queries don't silently pass. return false; } +// --------------------------------------------------------------------------- +// Filter tree construction from JSON +// --------------------------------------------------------------------------- + static void collectSteps(const mini_json::Value &node, std::vector &out) { - if (node.isObject() && node["all"].isArray()) { - for (const auto &child : node["all"].array) { + if (!node.isObject()) return; + + // { "all": [...] } → flatten children into sequential AND (same as before) + if (node["all"].isArray()) { + for (const auto &child : node["all"].array) collectSteps(child, out); - } return; } - if (node.isObject() && node["op"].isString()) { + // { "any": [...] } → single OR combinator step with children + if (node["any"].isArray()) { + Step anyStep; + anyStep.op = "any"; + for (const auto &child : node["any"].array) + collectSteps(child, anyStep.subSteps); + out.push_back(anyStep); + return; + } + // { "op": "...", "args": {...} } → leaf step + if (node["op"].isString()) { Step s; - s.op = node["op"].getString(); + s.op = node["op"].getString(); s.args = node["args"]; out.push_back(s); } } +// --------------------------------------------------------------------------- +// Public API +// --------------------------------------------------------------------------- + static const char *dupCString(const std::string &str) { char *c_result = (char *)malloc(str.length() + 1); if (!c_result) return nullptr; @@ -198,7 +467,7 @@ static const char *dupCString(const std::string &str) { IMMOLATE_API const char *brainstorm_query(const char *seed, const char *query_json) { - std::string seed_str = seed ? seed : ""; + std::string seed_str = seed ? seed : ""; std::string query_str = query_json ? query_json : ""; mini_json::Value root; @@ -213,14 +482,13 @@ IMMOLATE_API const char *brainstorm_query(const char *seed, } const auto &search = root["search"]; - int threads = static_cast(getNumber(search["threads"], 1)); - long long max_seeds = getNumber(search["max_seeds"], 100000000); - bool exit_on_find = search["exit_on_find"].getBool(true); + int threads = static_cast(getNumber(search["threads"], 1)); + long long max_seeds = getNumber(search["max_seeds"], 100000000); + bool exit_on_find = search["exit_on_find"].getBool(true); Search s([steps](Instance inst) { - for (const auto &step : steps) { + for (const auto &step : steps) if (!applyStep(step, inst)) return 0; - } return 1; }, seed_str, threads, max_seeds > 0 ? max_seeds : 100000000); s.exitOnFind = exit_on_find;