diff options
Diffstat (limited to 'Source')
| -rw-r--r-- | Source/ChallengeRandomizer.cpp | 108 | ||||
| -rw-r--r-- | Source/ChallengeRandomizer.h | 13 | ||||
| -rw-r--r-- | Source/Memory.cpp | 3 | ||||
| -rw-r--r-- | Source/Memory.h | 38 | ||||
| -rw-r--r-- | Source/Panels.h | 203 | ||||
| -rw-r--r-- | Source/Randomizer.cpp | 405 | ||||
| -rw-r--r-- | Source/Randomizer.h | 35 | ||||
| -rw-r--r-- | Source/Randomizer2.cpp | 644 | ||||
| -rw-r--r-- | Source/Randomizer2.h | 19 | ||||
| -rw-r--r-- | Source/Randomizer2Core.cpp | 203 | ||||
| -rw-r--r-- | Source/Randomizer2Core.h | 17 | ||||
| -rw-r--r-- | Source/Solver.cpp | 76 | ||||
| -rw-r--r-- | Source/Solver.h | 13 | ||||
| -rw-r--r-- | Source/Source.vcxproj | 10 | ||||
| -rw-r--r-- | Source/Validator.cpp | 97 | ||||
| -rw-r--r-- | Source/Validator.h | 27 |
16 files changed, 326 insertions, 1585 deletions
| diff --git a/Source/ChallengeRandomizer.cpp b/Source/ChallengeRandomizer.cpp deleted file mode 100644 index 976374e..0000000 --- a/Source/ChallengeRandomizer.cpp +++ /dev/null | |||
| @@ -1,108 +0,0 @@ | |||
| 1 | #include "pch.h" | ||
| 2 | #include "ChallengeRandomizer.h" | ||
| 3 | |||
| 4 | // Modify an opcode to use RNG2 instead of main RNG | ||
| 5 | void ChallengeRandomizer::AdjustRng(int offset) { | ||
| 6 | int currentRng = _memory->ReadData<int>({offset}, 0x1)[0]; | ||
| 7 | _memory->WriteData<int>({offset}, {currentRng + 0x20}); | ||
| 8 | } | ||
| 9 | |||
| 10 | // Overwrite the pointer for the lightmap_generator (which is unused, afaict) to point to a secondary RNG. | ||
| 11 | // Then, adjust all the RNG functions in challenge/doors to use this RNG. | ||
| 12 | ChallengeRandomizer::ChallengeRandomizer(const std::shared_ptr<Memory>& memory, int seed) : _memory(memory) | ||
| 13 | { | ||
| 14 | RNG_ADDR = _memory->ReadData<int>({GLOBALS + 0x10}, 1)[0]; | ||
| 15 | RNG2_ADDR = _memory->ReadData<int>({GLOBALS + 0x30}, 1)[0]; | ||
| 16 | bool alreadyInjected = (RNG2_ADDR == RNG_ADDR + 4); | ||
| 17 | |||
| 18 | if (!alreadyInjected) _memory->WriteData<int>({GLOBALS + 0x30}, {RNG_ADDR + 4}); | ||
| 19 | _memory->WriteData<int>({GLOBALS + 0x30, 0}, {seed}); | ||
| 20 | |||
| 21 | // do_success_side_effects | ||
| 22 | _memory->AddSigScan({0xFF, 0xC8, 0x99, 0x2B, 0xC2, 0xD1, 0xF8, 0x8B, 0xD0}, [&](int index) { | ||
| 23 | if (GLOBALS == 0x5B28C0) { // Version differences. | ||
| 24 | index += 0x3E; | ||
| 25 | } else if (GLOBALS == 0x62D0A0) { | ||
| 26 | index += 0x42; | ||
| 27 | } | ||
| 28 | // Overwritten bytes start just after the movsxd rax, dword ptr ds:[rdi + 0x230] | ||
| 29 | // aka test eax, eax; jle 2C; imul rcx, rax, 34 | ||
| 30 | _memory->WriteData<byte>({index}, { | ||
| 31 | 0x8B, 0x0D, 0x00, 0x00, 0x00, 0x00, // mov ecx, [0x00000000] ;This is going to be the address of the custom RNG | ||
| 32 | 0x67, 0xC7, 0x01, 0x00, 0x00, 0x00, 0x00, // mov dword ptr ds:[ecx], 0x00000000 ;This is going to be the seed value | ||
| 33 | 0x48, 0x83, 0xF8, 0x02, // cmp rax, 0x2 ;This is the short solve on the record player (which turns it off) | ||
| 34 | 0x90, 0x90, 0x90 // nop nop nop | ||
| 35 | }); | ||
| 36 | int target = (GLOBALS + 0x30) - (index + 0x6); // +6 is for the length of the line | ||
| 37 | _memory->WriteData<int>({index + 0x2}, {target}); | ||
| 38 | _memory->WriteData<int>({index + 0x9}, {seed}); // Because we're resetting seed every challenge, we need to run this injection every time. | ||
| 39 | }); | ||
| 40 | |||
| 41 | if (!alreadyInjected) { | ||
| 42 | // shuffle_integers | ||
| 43 | _memory->AddSigScan({0x48, 0x89, 0x5C, 0x24, 0x10, 0x56, 0x48, 0x83, 0xEC, 0x20, 0x48, 0x63, 0xDA, 0x48, 0x8B, 0xF1, 0x83, 0xFB, 0x01}, [&](int index) { | ||
| 44 | AdjustRng(index + 0x23); | ||
| 45 | }); | ||
| 46 | // shuffle<int> | ||
| 47 | _memory->AddSigScan({0x33, 0xF6, 0x48, 0x8B, 0xD9, 0x39, 0x31, 0x7E, 0x51}, [&](int index) { | ||
| 48 | AdjustRng(index - 0x4); | ||
| 49 | }); | ||
| 50 | // cut_random_edges | ||
| 51 | _memory->AddSigScan({0x89, 0x44, 0x24, 0x3C, 0x33, 0xC0, 0x85, 0xC0, 0x75, 0xFA}, [&](int index) { | ||
| 52 | AdjustRng(index + 0x3B); | ||
| 53 | }); | ||
| 54 | // get_empty_decoration_slot | ||
| 55 | _memory->AddSigScan({0x42, 0x83, 0x3C, 0x80, 0x00, 0x75, 0xDF}, [&](int index) { | ||
| 56 | AdjustRng(index - 0x17); | ||
| 57 | }); | ||
| 58 | // get_empty_dot_spot | ||
| 59 | _memory->AddSigScan({0xF7, 0xF3, 0x85, 0xD2, 0x74, 0xEC}, [&](int index) { | ||
| 60 | AdjustRng(index - 0xB); | ||
| 61 | }); | ||
| 62 | // add_exactly_this_many_bisection_dots | ||
| 63 | _memory->AddSigScan({0x48, 0x8B, 0xB4, 0x24, 0xB8, 0x00, 0x00, 0x00, 0x48, 0x8B, 0xBC, 0x24, 0xB0, 0x00, 0x00, 0x00}, [&](int index) { | ||
| 64 | AdjustRng(index - 0x4); | ||
| 65 | }); | ||
| 66 | // make_a_shaper | ||
| 67 | _memory->AddSigScan({0xF7, 0xE3, 0xD1, 0xEA, 0x8D, 0x0C, 0x52}, [&](int index) { | ||
| 68 | AdjustRng(index - 0x10); | ||
| 69 | AdjustRng(index + 0x1C); | ||
| 70 | AdjustRng(index + 0x49); | ||
| 71 | }); | ||
| 72 | // Entity_Machine_Panel::init_pattern_data_lotus | ||
| 73 | _memory->AddSigScan({0x40, 0x55, 0x56, 0x48, 0x8D, 0x6C, 0x24, 0xB1}, [&](int index) { | ||
| 74 | AdjustRng(index + 0x433); | ||
| 75 | AdjustRng(index + 0x45B); | ||
| 76 | AdjustRng(index + 0x5A7); | ||
| 77 | AdjustRng(index + 0x5D6); | ||
| 78 | AdjustRng(index + 0x6F6); | ||
| 79 | AdjustRng(index + 0xD17); | ||
| 80 | AdjustRng(index + 0xFDA); | ||
| 81 | }); | ||
| 82 | // Entity_Record_Player::reroll_lotus_eater_stuff | ||
| 83 | _memory->AddSigScan({0xB8, 0xAB, 0xAA, 0xAA, 0xAA, 0x41, 0xC1, 0xE8}, [&](int index) { | ||
| 84 | AdjustRng(index - 0x13); | ||
| 85 | AdjustRng(index + 0x34); | ||
| 86 | }); | ||
| 87 | |||
| 88 | // These disable the random locations on timer panels, which would otherwise increment the RNG. | ||
| 89 | // I'm writing 31 C0 (xor eax, eax), then 3 NOPs, which pretends the RNG returns 0. | ||
| 90 | // do_lotus_minutes | ||
| 91 | _memory->AddSigScan({0x0F, 0xBE, 0x6C, 0x08, 0xFF, 0x45}, [&](int index) { | ||
| 92 | _memory->WriteData<byte>({index + 0x410}, {0x31, 0xC0, 0x90, 0x90, 0x90}); | ||
| 93 | }); | ||
| 94 | // do_lotus_tenths | ||
| 95 | _memory->AddSigScan({0x00, 0x04, 0x00, 0x00, 0x41, 0x8D, 0x50, 0x09}, [&](int index) { | ||
| 96 | _memory->WriteData<byte>({index + 0xA2}, {0x31, 0xC0, 0x90, 0x90, 0x90}); | ||
| 97 | }); | ||
| 98 | // do_lotus_eighths | ||
| 99 | _memory->AddSigScan({0x75, 0xF5, 0x0F, 0xBE, 0x44, 0x08, 0xFF}, [&](int index) { | ||
| 100 | _memory->WriteData<byte>({index + 0x1AE}, {0x31, 0xC0, 0x90, 0x90, 0x90}); | ||
| 101 | }); | ||
| 102 | } | ||
| 103 | |||
| 104 | int failed = _memory->ExecuteSigScans(); | ||
| 105 | if (failed != 0) { | ||
| 106 | std::cout << "Failed " << failed << " sigscans"; | ||
| 107 | } | ||
| 108 | } | ||
| diff --git a/Source/ChallengeRandomizer.h b/Source/ChallengeRandomizer.h deleted file mode 100644 index b06be81..0000000 --- a/Source/ChallengeRandomizer.h +++ /dev/null | |||
| @@ -1,13 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | class ChallengeRandomizer { | ||
| 4 | public: | ||
| 5 | ChallengeRandomizer(const std::shared_ptr<Memory>& memory, int seed); | ||
| 6 | |||
| 7 | private: | ||
| 8 | void AdjustRng(int offset); | ||
| 9 | std::shared_ptr<Memory> _memory; | ||
| 10 | |||
| 11 | int RNG_ADDR; | ||
| 12 | int RNG2_ADDR; | ||
| 13 | }; | ||
| diff --git a/Source/Memory.cpp b/Source/Memory.cpp index 4a4cb63..8802add 100644 --- a/Source/Memory.cpp +++ b/Source/Memory.cpp | |||
| @@ -187,7 +187,7 @@ void* Memory::ComputeOffset(std::vector<int> offsets) { | |||
| 187 | return reinterpret_cast<void*>(cumulativeAddress + final_offset); | 187 | return reinterpret_cast<void*>(cumulativeAddress + final_offset); |
| 188 | } | 188 | } |
| 189 | 189 | ||
| 190 | int GLOBALS, POSITION, ORIENTATION, PATH_COLOR, REFLECTION_PATH_COLOR, DOT_COLOR, ACTIVE_COLOR, BACKGROUND_REGION_COLOR, SUCCESS_COLOR_A, SUCCESS_COLOR_B, STROBE_COLOR_A, STROBE_COLOR_B, ERROR_COLOR, PATTERN_POINT_COLOR, PATTERN_POINT_COLOR_A, PATTERN_POINT_COLOR_B, SYMBOL_A, SYMBOL_B, SYMBOL_C, SYMBOL_D, SYMBOL_E, PUSH_SYMBOL_COLORS, OUTER_BACKGROUND, OUTER_BACKGROUND_MODE, TRACED_EDGES, AUDIO_PREFIX, POWER, TARGET, POWER_OFF_ON_FAIL, IS_CYLINDER, CYLINDER_Z0, CYLINDER_Z1, CYLINDER_RADIUS, CURSOR_SPEED_SCALE, NEEDS_REDRAW, SPECULAR_ADD, SPECULAR_POWER, PATH_WIDTH_SCALE, STARTPOINT_SCALE, NUM_DOTS, NUM_CONNECTIONS, MAX_BROADCAST_DISTANCE, DOT_POSITIONS, DOT_FLAGS, DOT_CONNECTION_A, DOT_CONNECTION_B, DECORATIONS, DECORATION_FLAGS, DECORATION_COLORS, NUM_DECORATIONS, REFLECTION_DATA, GRID_SIZE_X, GRID_SIZE_Y, STYLE_FLAGS, SEQUENCE_LEN, SEQUENCE, DOT_SEQUENCE_LEN, DOT_SEQUENCE, DOT_SEQUENCE_LEN_REFLECTION, DOT_SEQUENCE_REFLECTION, NUM_COLORED_REGIONS, COLORED_REGIONS, PANEL_TARGET, SPECULAR_TEXTURE, CABLE_TARGET_2, AUDIO_LOG_NAME, OPEN_RATE, METADATA, HOTEL_EP_NAME; | 190 | int GLOBALS, POSITION, ORIENTATION, PATH_COLOR, REFLECTION_PATH_COLOR, DOT_COLOR, ACTIVE_COLOR, BACKGROUND_REGION_COLOR, SUCCESS_COLOR_A, SUCCESS_COLOR_B, STROBE_COLOR_A, STROBE_COLOR_B, ERROR_COLOR, PATTERN_POINT_COLOR, PATTERN_POINT_COLOR_A, PATTERN_POINT_COLOR_B, SYMBOL_A, SYMBOL_B, SYMBOL_C, SYMBOL_D, SYMBOL_E, PUSH_SYMBOL_COLORS, OUTER_BACKGROUND, OUTER_BACKGROUND_MODE, TRACED_EDGES, AUDIO_PREFIX, POWER, TARGET, POWER_OFF_ON_FAIL, IS_CYLINDER, CYLINDER_Z0, CYLINDER_Z1, CYLINDER_RADIUS, CURSOR_SPEED_SCALE, NEEDS_REDRAW, SPECULAR_ADD, SPECULAR_POWER, PATH_WIDTH_SCALE, STARTPOINT_SCALE, NUM_DOTS, NUM_CONNECTIONS, MAX_BROADCAST_DISTANCE, DOT_POSITIONS, DOT_FLAGS, DOT_CONNECTION_A, DOT_CONNECTION_B, DECORATIONS, DECORATION_FLAGS, DECORATION_COLORS, NUM_DECORATIONS, REFLECTION_DATA, GRID_SIZE_X, GRID_SIZE_Y, STYLE_FLAGS, SEQUENCE_LEN, SEQUENCE, DOT_SEQUENCE_LEN, DOT_SEQUENCE, DOT_SEQUENCE_LEN_REFLECTION, DOT_SEQUENCE_REFLECTION, NUM_COLORED_REGIONS, COLORED_REGIONS, PANEL_TARGET, SPECULAR_TEXTURE, CABLE_TARGET_2, AUDIO_LOG_NAME, OPEN_RATE, METADATA, HOTEL_EP_NAME, RANDOMISE_ON_POWER_ON; |
| 191 | 191 | ||
| 192 | void Memory::LoadPanelOffsets() { | 192 | void Memory::LoadPanelOffsets() { |
| 193 | AddSigScan({0x74, 0x41, 0x48, 0x85, 0xC0, 0x74, 0x04, 0x48, 0x8B, 0x48, 0x10}, [sharedThis = shared_from_this()](int index){ | 193 | AddSigScan({0x74, 0x41, 0x48, 0x85, 0xC0, 0x74, 0x04, 0x48, 0x8B, 0x48, 0x10}, [sharedThis = shared_from_this()](int index){ |
| @@ -313,6 +313,7 @@ void Memory::LoadPanelOffsets() { | |||
| 313 | DOT_FLAGS = 0x3C8; | 313 | DOT_FLAGS = 0x3C8; |
| 314 | DOT_CONNECTION_A = 0x3D0; | 314 | DOT_CONNECTION_A = 0x3D0; |
| 315 | DOT_CONNECTION_B = 0x3D8; | 315 | DOT_CONNECTION_B = 0x3D8; |
| 316 | RANDOMISE_ON_POWER_ON = 0x3E0; | ||
| 316 | DECORATIONS = 0x418; | 317 | DECORATIONS = 0x418; |
| 317 | DECORATION_FLAGS = 0x420; | 318 | DECORATION_FLAGS = 0x420; |
| 318 | DECORATION_COLORS = 0x428; | 319 | DECORATION_COLORS = 0x428; |
| diff --git a/Source/Memory.h b/Source/Memory.h index 1724251..e093960 100644 --- a/Source/Memory.h +++ b/Source/Memory.h | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | extern int GLOBALS, POSITION, ORIENTATION, PATH_COLOR, REFLECTION_PATH_COLOR, DOT_COLOR, ACTIVE_COLOR, BACKGROUND_REGION_COLOR, SUCCESS_COLOR_A, SUCCESS_COLOR_B, STROBE_COLOR_A, STROBE_COLOR_B, ERROR_COLOR, PATTERN_POINT_COLOR, PATTERN_POINT_COLOR_A, PATTERN_POINT_COLOR_B, SYMBOL_A, SYMBOL_B, SYMBOL_C, SYMBOL_D, SYMBOL_E, PUSH_SYMBOL_COLORS, OUTER_BACKGROUND, OUTER_BACKGROUND_MODE, TRACED_EDGES, AUDIO_PREFIX, POWER, TARGET, POWER_OFF_ON_FAIL, IS_CYLINDER, CYLINDER_Z0, CYLINDER_Z1, CYLINDER_RADIUS, CURSOR_SPEED_SCALE, NEEDS_REDRAW, SPECULAR_ADD, SPECULAR_POWER, PATH_WIDTH_SCALE, STARTPOINT_SCALE, NUM_DOTS, NUM_CONNECTIONS, MAX_BROADCAST_DISTANCE, DOT_POSITIONS, DOT_FLAGS, DOT_CONNECTION_A, DOT_CONNECTION_B, DECORATIONS, DECORATION_FLAGS, DECORATION_COLORS, NUM_DECORATIONS, REFLECTION_DATA, GRID_SIZE_X, GRID_SIZE_Y, STYLE_FLAGS, SEQUENCE_LEN, SEQUENCE, DOT_SEQUENCE_LEN, DOT_SEQUENCE, DOT_SEQUENCE_LEN_REFLECTION, DOT_SEQUENCE_REFLECTION, NUM_COLORED_REGIONS, COLORED_REGIONS, PANEL_TARGET, SPECULAR_TEXTURE, CABLE_TARGET_2, AUDIO_LOG_NAME, OPEN_RATE, METADATA, HOTEL_EP_NAME; | 3 | extern int GLOBALS, POSITION, ORIENTATION, PATH_COLOR, REFLECTION_PATH_COLOR, DOT_COLOR, ACTIVE_COLOR, BACKGROUND_REGION_COLOR, SUCCESS_COLOR_A, SUCCESS_COLOR_B, STROBE_COLOR_A, STROBE_COLOR_B, ERROR_COLOR, PATTERN_POINT_COLOR, PATTERN_POINT_COLOR_A, PATTERN_POINT_COLOR_B, SYMBOL_A, SYMBOL_B, SYMBOL_C, SYMBOL_D, SYMBOL_E, PUSH_SYMBOL_COLORS, OUTER_BACKGROUND, OUTER_BACKGROUND_MODE, TRACED_EDGES, AUDIO_PREFIX, POWER, TARGET, POWER_OFF_ON_FAIL, IS_CYLINDER, CYLINDER_Z0, CYLINDER_Z1, CYLINDER_RADIUS, CURSOR_SPEED_SCALE, NEEDS_REDRAW, SPECULAR_ADD, SPECULAR_POWER, PATH_WIDTH_SCALE, STARTPOINT_SCALE, NUM_DOTS, NUM_CONNECTIONS, MAX_BROADCAST_DISTANCE, DOT_POSITIONS, DOT_FLAGS, DOT_CONNECTION_A, DOT_CONNECTION_B, DECORATIONS, DECORATION_FLAGS, DECORATION_COLORS, NUM_DECORATIONS, REFLECTION_DATA, GRID_SIZE_X, GRID_SIZE_Y, STYLE_FLAGS, SEQUENCE_LEN, SEQUENCE, DOT_SEQUENCE_LEN, DOT_SEQUENCE, DOT_SEQUENCE_LEN_REFLECTION, DOT_SEQUENCE_REFLECTION, NUM_COLORED_REGIONS, COLORED_REGIONS, PANEL_TARGET, SPECULAR_TEXTURE, CABLE_TARGET_2, AUDIO_LOG_NAME, OPEN_RATE, METADATA, HOTEL_EP_NAME, RANDOMISE_ON_POWER_ON; |
| 4 | 4 | ||
| 5 | enum class ProcStatus { | 5 | enum class ProcStatus { |
| 6 | NotRunning, | 6 | NotRunning, |
| @@ -55,6 +55,42 @@ public: | |||
| 55 | void AddSigScan(const std::vector<byte>& scanBytes, const std::function<void(int index)>& scanFunc); | 55 | void AddSigScan(const std::vector<byte>& scanBytes, const std::function<void(int index)>& scanFunc); |
| 56 | int ExecuteSigScans(); | 56 | int ExecuteSigScans(); |
| 57 | 57 | ||
| 58 | template <typename T> | ||
| 59 | void CopyEntityData(int fromId, int toId, int offset, int size) { | ||
| 60 | WriteEntityData<T>(toId, offset, ReadEntityData<T>(fromId, offset, size)); | ||
| 61 | } | ||
| 62 | |||
| 63 | template <typename T> | ||
| 64 | void CopyArray(int fromId, int toId, int offset, int size) { | ||
| 65 | if (size == 0) { | ||
| 66 | WriteEntityData<uintptr_t>(toId, offset, { static_cast<uintptr_t>(0) }); | ||
| 67 | } | ||
| 68 | else { | ||
| 69 | WriteNewArray<T>(toId, offset, ReadArray<T>(fromId, offset, size)); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | template <typename T> | ||
| 74 | void CopyArrayDynamicSize(int fromId, int toId, int arrayOffset, int sizeOffset) { | ||
| 75 | CopyArray<T>(fromId, toId, arrayOffset, ReadEntityData<int>(fromId, sizeOffset, sizeof(int))[0]); | ||
| 76 | } | ||
| 77 | |||
| 78 | template <class T> | ||
| 79 | void WritePanelData(int panel, int offset, const std::vector<T>& data) { | ||
| 80 | WriteData<T>({ GLOBALS, 0x18, panel * 8, offset }, data); | ||
| 81 | } | ||
| 82 | |||
| 83 | template <class T> | ||
| 84 | T ReadPanelData(int panel, int offset) { | ||
| 85 | return ReadData<T>({ GLOBALS, 0x18, panel * 8, offset }, 1)[0]; | ||
| 86 | } | ||
| 87 | |||
| 88 | template <class T> | ||
| 89 | std::vector<T> ReadPanelData(int panel, int offset, size_t size) { | ||
| 90 | if (size == 0) return std::vector<T>(); | ||
| 91 | return ReadData<T>({ GLOBALS, 0x18, panel * 8, offset }, size); | ||
| 92 | } | ||
| 93 | |||
| 58 | private: | 94 | private: |
| 59 | template<class T> | 95 | template<class T> |
| 60 | std::vector<T> ReadData(const std::vector<int>& offsets, size_t numItems) { | 96 | std::vector<T> ReadData(const std::vector<int>& offsets, size_t numItems) { |
| diff --git a/Source/Panels.h b/Source/Panels.h index 4a850ee..b8bb248 100644 --- a/Source/Panels.h +++ b/Source/Panels.h | |||
| @@ -67,6 +67,20 @@ std::vector<int> leftForwardRightPanels = { | |||
| 67 | 0x17E52, // Treehouse Green 4 | 67 | 0x17E52, // Treehouse Green 4 |
| 68 | }; | 68 | }; |
| 69 | 69 | ||
| 70 | std::vector<int> treehousePivots = { | ||
| 71 | 0x17DD1, // Treehouse Left Orange 9 | ||
| 72 | 0x17CE3, // Treehouse Right Orange 4 | ||
| 73 | 0x17DB7, // Treehouse Right Orange 10 | ||
| 74 | 0x17E52, // Treehouse Green 4 | ||
| 75 | }; | ||
| 76 | |||
| 77 | std::vector<int> utmPerspective = { | ||
| 78 | 0x288EA, // UTM Perspective 1 | ||
| 79 | 0x288FC, // UTM Perspective 2 | ||
| 80 | 0x289E7, // UTM Perspective 3 | ||
| 81 | 0x288AA, // UTM Perspective 4 | ||
| 82 | }; | ||
| 83 | |||
| 70 | std::vector<int> pillars = { | 84 | std::vector<int> pillars = { |
| 71 | 0x0383D, // Mountain 3 Left Pillar 1 | 85 | 0x0383D, // Mountain 3 Left Pillar 1 |
| 72 | 0x0383F, // Mountain 3 Left Pillar 2 | 86 | 0x0383F, // Mountain 3 Left Pillar 2 |
| @@ -117,8 +131,8 @@ std::vector<int> mountainMultipanel = { | |||
| 117 | }; | 131 | }; |
| 118 | 132 | ||
| 119 | std::vector<int> squarePanels = { | 133 | std::vector<int> squarePanels = { |
| 120 | 0x00064, // Tutorial Straight | 134 | //0x00064, // Tutorial Straight |
| 121 | 0x00182, // Tutorial Bend | 135 | //0x00182, // Tutorial Bend |
| 122 | 0x0A3B2, // Tutorial Back Right | 136 | 0x0A3B2, // Tutorial Back Right |
| 123 | 0x00295, // Tutorial Center Left | 137 | 0x00295, // Tutorial Center Left |
| 124 | 0x00293, // Tutorial Front Center | 138 | 0x00293, // Tutorial Front Center |
| @@ -433,6 +447,191 @@ std::vector<int> squarePanels = { | |||
| 433 | 0x0A16E, // UTM Challenge Entrance | 447 | 0x0A16E, // UTM Challenge Entrance |
| 434 | 0x039B4, // Tunnels Theater Catwalk | 448 | 0x039B4, // Tunnels Theater Catwalk |
| 435 | 0x09E85, // Tunnels Town Shortcut | 449 | 0x09E85, // Tunnels Town Shortcut |
| 450 | |||
| 451 | 0x00698, // Desert Surface 1 | ||
| 452 | 0x0048F, // Desert Surface 2 | ||
| 453 | 0x09F92, // Desert Surface 3 | ||
| 454 | 0x0A036, // Desert Surface 4 | ||
| 455 | 0x09DA6, // Desert Surface 5 | ||
| 456 | 0x0A049, // Desert Surface 6 | ||
| 457 | 0x0A053, // Desert Surface 7 | ||
| 458 | 0x09F94, // Desert Surface 8 | ||
| 459 | 0x00422, // Desert Light 1 | ||
| 460 | 0x006E3, // Desert Light 2 | ||
| 461 | 0x0A02D, // Desert Light 3 | ||
| 462 | 0x00C72, // Desert Pond 1 | ||
| 463 | 0x0129D, // Desert Pond 2 | ||
| 464 | 0x008BB, // Desert Pond 3 | ||
| 465 | 0x0078D, // Desert Pond 4 | ||
| 466 | 0x18313, // Desert Pond 5 | ||
| 467 | 0x04D18, // Desert Flood 1 | ||
| 468 | 0x01205, // Desert Flood 2 | ||
| 469 | 0x181AB, // Desert Flood 3 | ||
| 470 | 0x0117A, // Desert Flood 4 | ||
| 471 | 0x17ECA, // Desert Flood 5 | ||
| 472 | 0x18076, // Desert Flood Exit | ||
| 473 | 0x0A15C, // Desert Final Left Convex | ||
| 474 | 0x09FFF, // Desert Final Left Concave | ||
| 475 | 0x0A15F, // Desert Final Near | ||
| 476 | 0x012D7, // Desert Final Far | ||
| 477 | 0x09F7D, // Bunker Tutorial 1 | ||
| 478 | 0x09FDC, // Bunker Tutorial 2 | ||
| 479 | 0x09FF7, // Bunker Tutorial 3 | ||
| 480 | 0x09F82, // Bunker Tutorial 4 | ||
| 481 | 0x09FF8, // Bunker Tutorial 5 | ||
| 482 | 0x09D9F, // Bunker Advanced 1 | ||
| 483 | 0x09DA1, // Bunker Advanced 2 | ||
| 484 | 0x09DA2, // Bunker Advanced 3 | ||
| 485 | 0x09DAF, // Bunker Advanced 4 | ||
| 486 | // 0x0A099, // Bunker Glass Door | ||
| 487 | 0x0A010, // Bunker Glass 1 | ||
| 488 | 0x0A01B, // Bunker Glass 2 | ||
| 489 | 0x0A01F, // Bunker Glass 3 | ||
| 490 | 0x17E63, // Bunker Ultraviolet 1 | ||
| 491 | 0x17E67, // Bunker Ultraviolet 2 | ||
| 492 | 0x002C4, // Jungle Waves 1 | ||
| 493 | 0x00767, // Jungle Waves 2 | ||
| 494 | 0x002C6, // Jungle Waves 3 | ||
| 495 | 0x0070E, // Jungle Waves 4 | ||
| 496 | 0x0070F, // Jungle Waves 5 | ||
| 497 | 0x0087D, // Jungle Waves 6 | ||
| 498 | 0x002C7, // Jungle Waves 7 | ||
| 499 | // 0x17CAB, // Jungle Pop-up Wall | ||
| 500 | 0x0026D, // Jungle Dots 1 | ||
| 501 | 0x0026E, // Jungle Dots 2 | ||
| 502 | 0x0026F, // Jungle Dots 3 | ||
| 503 | 0x00C3F, // Jungle Dots 4 | ||
| 504 | 0x00C41, // Jungle Dots 5 | ||
| 505 | 0x014B2, // Jungle Dots 6 | ||
| 506 | 0x198B5, // Shadows Tutorial 1 | ||
| 507 | 0x198BD, // Shadows Tutorial 2 | ||
| 508 | 0x198BF, // Shadows Tutorial 3 | ||
| 509 | 0x19771, // Shadows Tutorial 4 | ||
| 510 | 0x0A8DC, // Shadows Tutorial 5 | ||
| 511 | 0x0AC74, // Shadows Tutorial 6 | ||
| 512 | 0x0AC7A, // Shadows Tutorial 7 | ||
| 513 | 0x0A8E0, // Shadows Tutorial 8 | ||
| 514 | 0x386FA, // Shadows Avoid 1 | ||
| 515 | 0x1C33F, // Shadows Avoid 2 | ||
| 516 | 0x196E2, // Shadows Avoid 3 | ||
| 517 | 0x1972A, // Shadows Avoid 4 | ||
| 518 | 0x19809, // Shadows Avoid 5 | ||
| 519 | 0x19806, // Shadows Avoid 6 | ||
| 520 | 0x196F8, // Shadows Avoid 7 | ||
| 521 | 0x1972F, // Shadows Avoid 8 | ||
| 522 | 0x19797, // Shadows Follow 1 | ||
| 523 | 0x1979A, // Shadows Follow 2 | ||
| 524 | 0x197E0, // Shadows Follow 3 | ||
| 525 | 0x197E8, // Shadows Follow 4 | ||
| 526 | 0x197E5, // Shadows Follow 5 | ||
| 527 | 0x19650, // Shadows Laser | ||
| 528 | |||
| 529 | 0x09FCC, // Mountain 2 Multipanel 1 | ||
| 530 | 0x09FCE, // Mountain 2 Multipanel 2 | ||
| 531 | 0x09FCF, // Mountain 2 Multipanel 3 | ||
| 532 | 0x09FD0, // Mountain 2 Multipanel 4 | ||
| 533 | 0x09FD1, // Mountain 2 Multipanel 5 | ||
| 534 | 0x09FD2, // Mountain 2 Multipanel 6 | ||
| 535 | |||
| 536 | 0x00290, // Monastery Exterior 1 | ||
| 537 | 0x00038, // Monastery Exterior 2 | ||
| 538 | 0x00037, // Monastery Exterior 3 | ||
| 539 | // 0x09D9B, // Monastery Bonsai | ||
| 540 | 0x193A7, // Monastery Interior 1 | ||
| 541 | 0x193AA, // Monastery Interior 2 | ||
| 542 | 0x193AB, // Monastery Interior 3 | ||
| 543 | 0x193A6, // Monastery Interior 4 | ||
| 544 | |||
| 545 | |||
| 546 | |||
| 547 | 0x03629, // Tutorial Gate Open | ||
| 548 | 0x03505, // Tutorial Gate Close | ||
| 549 | |||
| 550 | 0x0C373, // Tutorial Patio floor | ||
| 551 | 0x1C349, // Symmetry Island Door 2 - Collision fails here, sadly | ||
| 552 | |||
| 553 | 0x28A69, // Town Lattice | ||
| 554 | 0x28A79, // Town Maze | ||
| 555 | 0x28B39, // Town Red Hexagonal | ||
| 556 | |||
| 557 | 0x18590, // Town Transparent | ||
| 558 | 0x28AE3, // Town Wire | ||
| 559 | |||
| 560 | |||
| 561 | 0x0383D, // Mountain 3 Left Pillar 1 | ||
| 562 | 0x0383F, // Mountain 3 Left Pillar 2 | ||
| 563 | 0x03859, // Mountain 3 Left Pillar 3 | ||
| 564 | 0x339BB, // Mountain 3 Left Pillar 4 | ||
| 565 | |||
| 566 | 0x0383A, // Mountain 3 Right Pillar 1 | ||
| 567 | 0x09E56, // Mountain 3 Right Pillar 2 | ||
| 568 | 0x09E5A, // Mountain 3 Right Pillar 3 | ||
| 569 | 0x33961, // Mountain 3 Right Pillar 4 | ||
| 570 | |||
| 571 | 0x09DD5, // UTM Challenge Pillar | ||
| 572 | |||
| 573 | |||
| 574 | 0x09FD8, // Mountain 2 Rainbow 5 | ||
| 575 | 0x17CAA, // Jungle Courtyard Gate | ||
| 576 | 0x0005C, // Glass Factory Vertical Symmetry 5 | ||
| 577 | 0x17C31, // Desert Final Transparent | ||
| 578 | |||
| 579 | 0x00143, // Orchard Apple Tree 1 | ||
| 580 | 0x0003B, // Orchard Apple Tree 2 | ||
| 581 | 0x00055, // Orchard Apple Tree 3 | ||
| 582 | 0x032F7, // Orchard Apple Tree 4 | ||
| 583 | 0x032FF, // Orchard Apple Tree 5 | ||
| 584 | 0x009B8, // Symmetry Island Transparent 1 | ||
| 585 | 0x003E8, // Symmetry Island Transparent 2 | ||
| 586 | 0x00A15, // Symmetry Island Transparent 3 | ||
| 587 | 0x00B53, // Symmetry Island Transparent 4 | ||
| 588 | 0x00B8D, // Symmetry Island Transparent 5 | ||
| 589 | 0x0C335, // Tutorial Pillar | ||
| 590 | |||
| 591 | 0x09F8E, //Bottom Right | ||
| 592 | 0x09FC1, //Bottom Left | ||
| 593 | 0x09F01, //Top Right | ||
| 594 | 0x09EFF, //Top Left | ||
| 595 | 0x09FDA, // metapuzzle | ||
| 596 | |||
| 597 | 0x03678, // Mill Lower Ramp Contol | ||
| 598 | 0x03679, // Mill Lower Lift Control | ||
| 599 | 0x03675, // Mill Upper Lift Control | ||
| 600 | 0x03676, // Mill Upper Ramp Control | ||
| 601 | 0x03852, // Boathouse Ramp Angle Control | ||
| 602 | 0x03858, // Boathouse Ramp Position Control | ||
| 603 | 0x275FA, // Boathouse Hook Control | ||
| 604 | 0x17CC4, // Railroad Elevator | ||
| 605 | 0x17E2B, // Swamp Flood Gate Control | ||
| 606 | 0x00609, // Swamp Surface Sliding Bridge Control | ||
| 607 | 0x18488, // Swamp Underwater Sliding Bridge Control | ||
| 608 | 0x17C0A, // Swamp Island Control 1 | ||
| 609 | 0x17E07, // Swamp Island Control 2 | ||
| 610 | |||
| 611 | 0x01983, // Mountain 3 Left Peekaboo | ||
| 612 | 0x01987, // Mountain 3 Right Peekaboo | ||
| 613 | |||
| 614 | 0x0088E, // Challenge Easy Maze | ||
| 615 | 0x00BAF, // Challenge Hard Maze | ||
| 616 | 0x00BF3, // Challenge Stones Maze | ||
| 617 | /*0x0051F, // Challenge Column Bottom Left | ||
| 618 | 0x00524, // Challenge Column Top Right | ||
| 619 | 0x00CDB, // Challenge Column Top Left | ||
| 620 | 0x00CD4, // Challenge Column Far Panel*/ | ||
| 621 | /*0x00C80, // Challenge Triple 1 Left | ||
| 622 | 0x00CA1, // Challenge Triple 1 Center | ||
| 623 | 0x00CB9, // Challenge Triple 1 Right | ||
| 624 | 0x00C22, // Challenge Triple 2 Left | ||
| 625 | 0x00C59, // Challenge Triple 2 Center | ||
| 626 | 0x00C68, // Challenge Triple 2 Right*/ | ||
| 627 | 0x034EC, // Challenge Triangle | ||
| 628 | 0x034F4, // Challenge Triangle | ||
| 629 | 0x1C31A, // Challenge Left Pillar | ||
| 630 | 0x1C319, // Challenge Right Pillar | ||
| 631 | |||
| 632 | 0x33AF5, // Mountain 1 Blue 1 | ||
| 633 | 0x33AF7, // Mountain 1 Blue 2 | ||
| 634 | 0x09F6E, // Mountain 1 Blue 3 | ||
| 436 | }; | 635 | }; |
| 437 | 636 | ||
| 438 | std::vector<int> desertPanels = { | 637 | std::vector<int> desertPanels = { |
| diff --git a/Source/Randomizer.cpp b/Source/Randomizer.cpp index c573146..d67f153 100644 --- a/Source/Randomizer.cpp +++ b/Source/Randomizer.cpp | |||
| @@ -93,7 +93,6 @@ Things to do for V2: | |||
| 93 | */ | 93 | */ |
| 94 | #include "pch.h" | 94 | #include "pch.h" |
| 95 | #include "Randomizer.h" | 95 | #include "Randomizer.h" |
| 96 | #include "ChallengeRandomizer.h" | ||
| 97 | #include "Panels.h" | 96 | #include "Panels.h" |
| 98 | #include "Random.h" | 97 | #include "Random.h" |
| 99 | 98 | ||
| @@ -124,344 +123,108 @@ void Randomizer::Randomize() { | |||
| 124 | } | 123 | } |
| 125 | _memory->WriteData<byte>({index}, {0xEB}); // jz -> jmp | 124 | _memory->WriteData<byte>({index}, {0xEB}); // jz -> jmp |
| 126 | }); | 125 | }); |
| 127 | // Sig scans will be run during challenge randomization. | ||
| 128 | |||
| 129 | // Seed challenge first for future-proofing | ||
| 130 | MEMORY_CATCH(RandomizeChallenge()); | ||
| 131 | |||
| 132 | // Content swaps -- must happen before squarePanels | ||
| 133 | MEMORY_CATCH(Randomize(upDownPanels, SWAP::LINES | SWAP::COLORS)); | ||
| 134 | MEMORY_CATCH(Randomize(leftForwardRightPanels, SWAP::LINES | SWAP::COLORS)); | ||
| 135 | |||
| 136 | MEMORY_CATCH(Randomize(squarePanels, SWAP::LINES | SWAP::COLORS)); | ||
| 137 | |||
| 138 | // Individual area modifications | ||
| 139 | MEMORY_CATCH(RandomizeTutorial()); | ||
| 140 | MEMORY_CATCH(RandomizeDesert()); | ||
| 141 | MEMORY_CATCH(RandomizeQuarry()); | ||
| 142 | MEMORY_CATCH(RandomizeTreehouse()); | ||
| 143 | MEMORY_CATCH(RandomizeKeep()); | ||
| 144 | MEMORY_CATCH(RandomizeShadows()); | ||
| 145 | MEMORY_CATCH(RandomizeMonastery()); | ||
| 146 | MEMORY_CATCH(RandomizeBunker()); | ||
| 147 | MEMORY_CATCH(RandomizeJungle()); | ||
| 148 | MEMORY_CATCH(RandomizeSwamp()); | ||
| 149 | MEMORY_CATCH(RandomizeMountain()); | ||
| 150 | MEMORY_CATCH(RandomizeTown()); | ||
| 151 | MEMORY_CATCH(RandomizeSymmetry()); | ||
| 152 | // RandomizeAudioLogs(); | ||
| 153 | } | ||
| 154 | 126 | ||
| 155 | void Randomizer::AdjustSpeed() { | 127 | _memory->ExecuteSigScans(); |
| 156 | // Desert Surface Final Control | ||
| 157 | _memory->WriteEntityData<float>(0x09F95, OPEN_RATE, {0.04f}); // 4x | ||
| 158 | // Swamp Sliding Bridge | ||
| 159 | _memory->WriteEntityData<float>(0x0061A, OPEN_RATE, {0.1f}); // 4x | ||
| 160 | // Mountain 2 Elevator | ||
| 161 | _memory->WriteEntityData<float>(0x09EEC, OPEN_RATE, {0.075f}); // 3x | ||
| 162 | } | ||
| 163 | 128 | ||
| 164 | void Randomizer::RandomizeLasers() { | 129 | // Tutorial Bend |
| 165 | Randomize(lasers, SWAP::TARGETS); | 130 | for (int panel : utmPerspective) { |
| 166 | // Read the target of keep front laser, and write it to keep back laser. | 131 | Tutorialise(panel, 0x00182); |
| 167 | std::vector<int> keepFrontLaserTarget = _memory->ReadEntityData<int>(0x0360E, TARGET, 1); | 132 | } |
| 168 | _memory->WriteEntityData<int>(0x03317, TARGET, keepFrontLaserTarget); | 133 | // Tutorial Straight |
| 169 | } | 134 | for (int panel : squarePanels) { |
| 135 | Tutorialise(panel, 0x00064); | ||
| 136 | } | ||
| 137 | // Town Laser Redirect Control | ||
| 138 | for (int panel : treehousePivots) { | ||
| 139 | Tutorialise(panel, 0x09F98); | ||
| 170 | 140 | ||
| 171 | void Randomizer::PreventSnipes() | 141 | // Mark the panel as pivotable. |
| 172 | { | 142 | int panelFlags = _memory->ReadEntityData<int>(panel, STYLE_FLAGS, 1)[0]; |
| 173 | // Distance-gate swamp snipe 1 to prevent RNG swamp snipe | 143 | _memory->WriteEntityData<int>(panel, STYLE_FLAGS, { panelFlags | 0x8000 }); |
| 174 | _memory->WriteEntityData<float>(0x17C05, MAX_BROADCAST_DISTANCE, {15.0}); | 144 | } |
| 175 | // Distance-gate shadows laser to prevent sniping through the bars | ||
| 176 | _memory->WriteEntityData<float>(0x19650, MAX_BROADCAST_DISTANCE, {2.5}); | ||
| 177 | } | ||
| 178 | 145 | ||
| 179 | // Private methods | ||
| 180 | void Randomizer::RandomizeTutorial() { | ||
| 181 | // Disable tutorial cursor speed modifications (not working?) | 146 | // Disable tutorial cursor speed modifications (not working?) |
| 182 | _memory->WriteEntityData<float>(0x00295, CURSOR_SPEED_SCALE, {1.0}); | 147 | _memory->WriteEntityData<float>(0x00295, CURSOR_SPEED_SCALE, { 1.0 }); |
| 183 | _memory->WriteEntityData<float>(0x0C373, CURSOR_SPEED_SCALE, {1.0}); | 148 | _memory->WriteEntityData<float>(0x0C373, CURSOR_SPEED_SCALE, { 1.0 }); |
| 184 | _memory->WriteEntityData<float>(0x00293, CURSOR_SPEED_SCALE, {1.0}); | 149 | _memory->WriteEntityData<float>(0x00293, CURSOR_SPEED_SCALE, { 1.0 }); |
| 185 | _memory->WriteEntityData<float>(0x002C2, CURSOR_SPEED_SCALE, {1.0}); | 150 | _memory->WriteEntityData<float>(0x002C2, CURSOR_SPEED_SCALE, { 1.0 }); |
| 186 | } | ||
| 187 | 151 | ||
| 188 | void Randomizer::RandomizeSymmetry() { | ||
| 189 | std::vector<int> randomOrder(transparent.size(), 0); | ||
| 190 | std::iota(randomOrder.begin(), randomOrder.end(), 0); | ||
| 191 | Shuffle(randomOrder, 1, 5); | ||
| 192 | ReassignTargets(transparent, randomOrder); | ||
| 193 | } | ||
| 194 | |||
| 195 | void Randomizer::RandomizeDesert() { | ||
| 196 | Randomize(desertPanels, SWAP::LINES); | ||
| 197 | |||
| 198 | // Turn off desert surface 8 | ||
| 199 | _memory->WriteEntityData<float>(0x09F94, POWER, {0.0, 0.0}); | ||
| 200 | // Turn off desert flood final | ||
| 201 | _memory->WriteEntityData<float>(0x18076, POWER, {0.0, 0.0}); | ||
| 202 | // Change desert floating target to desert flood final | ||
| 203 | _memory->WriteEntityData<int>(0x17ECA, TARGET, {0x18077}); | ||
| 204 | } | ||
| 205 | |||
| 206 | void Randomizer::RandomizeQuarry() { | ||
| 207 | } | ||
| 208 | |||
| 209 | void Randomizer::RandomizeTreehouse() { | ||
| 210 | // Ensure that whatever pivot panels we have are flagged as "pivotable" | 152 | // Ensure that whatever pivot panels we have are flagged as "pivotable" |
| 211 | // @Bug: Can return {}, be careful! | 153 | // @Bug: Can return {}, be careful! |
| 212 | int panelFlags = _memory->ReadEntityData<int>(0x17DD1, STYLE_FLAGS, 1)[0]; | 154 | int panelFlags = _memory->ReadEntityData<int>(0x17DD1, STYLE_FLAGS, 1)[0]; |
| 213 | _memory->WriteEntityData<int>(0x17DD1, STYLE_FLAGS, {panelFlags | 0x8000}); | 155 | _memory->WriteEntityData<int>(0x17DD1, STYLE_FLAGS, { panelFlags | 0x8000 }); |
| 214 | panelFlags = _memory->ReadEntityData<int>(0x17CE3, STYLE_FLAGS, 1)[0]; | 156 | panelFlags = _memory->ReadEntityData<int>(0x17CE3, STYLE_FLAGS, 1)[0]; |
| 215 | _memory->WriteEntityData<int>(0x17CE3, STYLE_FLAGS, {panelFlags | 0x8000}); | 157 | _memory->WriteEntityData<int>(0x17CE3, STYLE_FLAGS, { panelFlags | 0x8000 }); |
| 216 | panelFlags = _memory->ReadEntityData<int>(0x17DB7, STYLE_FLAGS, 1)[0]; | 158 | panelFlags = _memory->ReadEntityData<int>(0x17DB7, STYLE_FLAGS, 1)[0]; |
| 217 | _memory->WriteEntityData<int>(0x17DB7, STYLE_FLAGS, {panelFlags | 0x8000}); | 159 | _memory->WriteEntityData<int>(0x17DB7, STYLE_FLAGS, { panelFlags | 0x8000 }); |
| 218 | panelFlags = _memory->ReadEntityData<int>(0x17E52, STYLE_FLAGS, 1)[0]; | 160 | panelFlags = _memory->ReadEntityData<int>(0x17E52, STYLE_FLAGS, 1)[0]; |
| 219 | _memory->WriteEntityData<int>(0x17E52, STYLE_FLAGS, {panelFlags | 0x8000}); | 161 | _memory->WriteEntityData<int>(0x17E52, STYLE_FLAGS, { panelFlags | 0x8000 }); |
| 220 | } | 162 | } |
| 221 | 163 | ||
| 222 | void Randomizer::RandomizeKeep() { | 164 | void Randomizer::Tutorialise(int panel1, int tutorialStraight) { |
| 223 | } | 165 | //const int tutorialStraight = 0x00064; |
| 224 | 166 | ||
| 225 | void Randomizer::RandomizeShadows() { | 167 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, PATH_COLOR, 16); |
| 226 | // Change the shadows tutorial cable to only activate avoid | 168 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, REFLECTION_PATH_COLOR, 16); |
| 227 | _memory->WriteEntityData<int>(0x319A8, CABLE_TARGET_2, {0}); | 169 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, DOT_COLOR, 16); |
| 228 | // Change shadows avoid 8 to power shadows follow | 170 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, ACTIVE_COLOR, 16); |
| 229 | _memory->WriteEntityData<int>(0x1972F, TARGET, {0x1C34C}); | 171 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, BACKGROUND_REGION_COLOR, 12); |
| 230 | 172 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, SUCCESS_COLOR_A, 16); | |
| 231 | std::vector<int> randomOrder(shadowsPanels.size(), 0); | 173 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, SUCCESS_COLOR_B, 16); |
| 232 | std::iota(randomOrder.begin(), randomOrder.end(), 0); | 174 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, STROBE_COLOR_A, 16); |
| 233 | Shuffle(randomOrder, 0, 8); // Tutorial | 175 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, STROBE_COLOR_B, 16); |
| 234 | Shuffle(randomOrder, 8, 16); // Avoid | 176 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, ERROR_COLOR, 16); |
| 235 | Shuffle(randomOrder, 16, 21); // Follow | 177 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, PATTERN_POINT_COLOR, 16); |
| 236 | ReassignTargets(shadowsPanels, randomOrder); | 178 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, PATTERN_POINT_COLOR_A, 16); |
| 237 | // Turn off original starting panel | 179 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, PATTERN_POINT_COLOR_B, 16); |
| 238 | _memory->WriteEntityData<float>(shadowsPanels[0], POWER, {0.0f, 0.0f}); | 180 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, SYMBOL_A, 16); |
| 239 | // Turn on new starting panel | 181 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, SYMBOL_B, 16); |
| 240 | _memory->WriteEntityData<float>(shadowsPanels[randomOrder[0]], POWER, {1.0f, 1.0f}); | 182 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, SYMBOL_C, 16); |
| 241 | } | 183 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, SYMBOL_D, 16); |
| 242 | 184 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, SYMBOL_E, 16); | |
| 243 | void Randomizer::RandomizeTown() { | 185 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, PUSH_SYMBOL_COLORS, sizeof(int)); |
| 244 | // @Hack...? To open the gate at the end | 186 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, OUTER_BACKGROUND, 16); |
| 245 | std::vector<int> randomOrder(orchard.size() + 1, 0); | 187 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, OUTER_BACKGROUND_MODE, sizeof(int)); |
| 246 | std::iota(randomOrder.begin(), randomOrder.end(), 0); | 188 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, NUM_COLORED_REGIONS, sizeof(int)); |
| 247 | Shuffle(randomOrder, 1, 5); | 189 | _memory->CopyArrayDynamicSize<int>(tutorialStraight, panel1, COLORED_REGIONS, NUM_COLORED_REGIONS); |
| 248 | // Ensure that we open the gate before the final puzzle (by swapping) | 190 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, TRACED_EDGES, 16); |
| 249 | int panel3Index = find(randomOrder, 3); | 191 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, PATH_WIDTH_SCALE, sizeof(float)); |
| 250 | int panel4Index = find(randomOrder, 4); | 192 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, STARTPOINT_SCALE, sizeof(float)); |
| 251 | randomOrder[std::min(panel3Index, panel4Index)] = 3; | 193 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, NUM_DOTS, sizeof(int)); |
| 252 | randomOrder[std::max(panel3Index, panel4Index)] = 4; | 194 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, NUM_CONNECTIONS, sizeof(int)); |
| 253 | ReassignTargets(orchard, randomOrder); | 195 | _memory->CopyArray<float>(tutorialStraight, panel1, DOT_POSITIONS, _memory->ReadEntityData<int>(tutorialStraight, NUM_DOTS, sizeof(int))[0]*2); |
| 254 | } | 196 | _memory->CopyArrayDynamicSize<int>(tutorialStraight, panel1, DOT_FLAGS, NUM_DOTS); |
| 255 | 197 | _memory->CopyArrayDynamicSize<int>(tutorialStraight, panel1, DOT_CONNECTION_A, NUM_CONNECTIONS); | |
| 256 | void Randomizer::RandomizeMonastery() { | 198 | _memory->CopyArrayDynamicSize<int>(tutorialStraight, panel1, DOT_CONNECTION_B, NUM_CONNECTIONS); |
| 257 | std::vector<int> randomOrder(monasteryPanels.size(), 0); | 199 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, NUM_DECORATIONS, sizeof(int)); |
| 258 | std::iota(randomOrder.begin(), randomOrder.end(), 0); | 200 | _memory->CopyArrayDynamicSize<int>(tutorialStraight, panel1, DECORATIONS, NUM_DECORATIONS); |
| 259 | Shuffle(randomOrder, 3, 9); // Outer 2 & 3, Inner 1-4 | 201 | _memory->CopyArrayDynamicSize<int>(tutorialStraight, panel1, DECORATION_FLAGS, NUM_DECORATIONS); |
| 260 | ReassignTargets(monasteryPanels, randomOrder); | 202 | _memory->CopyArrayDynamicSize<int>(tutorialStraight, panel1, DECORATION_COLORS, NUM_DECORATIONS); |
| 261 | } | 203 | if (_memory->ReadPanelData<int>(tutorialStraight, REFLECTION_DATA)) { |
| 262 | 204 | _memory->CopyArrayDynamicSize<int>(tutorialStraight, panel1, REFLECTION_DATA, NUM_DOTS); | |
| 263 | void Randomizer::RandomizeBunker() { | ||
| 264 | std::vector<int> randomOrder(bunkerPanels.size(), 0); | ||
| 265 | std::iota(randomOrder.begin(), randomOrder.end(), 0); | ||
| 266 | // Randomize Tutorial 2-Advanced Tutorial 4 + Glass 1 | ||
| 267 | // Tutorial 1 cannot be randomized, since no other panel can start on | ||
| 268 | // Glass 1 will become door + glass 1, due to the targetting system | ||
| 269 | Shuffle(randomOrder, 1, 10); | ||
| 270 | // Randomize Glass 1-3 into everything after the door/glass 1 | ||
| 271 | const size_t glass1Index = find(randomOrder, 9); | ||
| 272 | Shuffle(randomOrder, glass1Index + 1, 12); | ||
| 273 | ReassignTargets(bunkerPanels, randomOrder); | ||
| 274 | } | ||
| 275 | |||
| 276 | void Randomizer::RandomizeJungle() { | ||
| 277 | std::vector<int> randomOrder(junglePanels.size(), 0); | ||
| 278 | std::iota(randomOrder.begin(), randomOrder.end(), 0); | ||
| 279 | // Waves 1 cannot be randomized, since no other panel can start on | ||
| 280 | Shuffle(randomOrder, 1, 7); // Waves 2-7 | ||
| 281 | Shuffle(randomOrder, 8, 13); // Pitches 1-6 | ||
| 282 | ReassignTargets(junglePanels, randomOrder); | ||
| 283 | |||
| 284 | // Fix the wall's target to point back to the cable, and the cable to point to the pitches panel. | ||
| 285 | // auto wallTarget = _memory->ReadPanelData<int>(junglePanels[7], TARGET, 1); | ||
| 286 | // _memory->WritePanelData<int>(junglePanels[7], TARGET, {0x3C113}); | ||
| 287 | // _memory->WritePanelData<int>(0x3C112, CABLE_TARGET_1, wallTarget); | ||
| 288 | } | ||
| 289 | |||
| 290 | void Randomizer::RandomizeSwamp() { | ||
| 291 | } | ||
| 292 | |||
| 293 | void Randomizer::RandomizeMountain() { | ||
| 294 | // Randomize multipanel | ||
| 295 | Randomize(mountainMultipanel, SWAP::LINES | SWAP::COLORS); | ||
| 296 | |||
| 297 | // Randomize final pillars order | ||
| 298 | std::vector<int> targets = {pillars[0] + 1}; | ||
| 299 | for (const int pillar : pillars) { | ||
| 300 | int target = _memory->ReadEntityData<int>(pillar, TARGET, 1)[0]; | ||
| 301 | targets.push_back(target); | ||
| 302 | } | 205 | } |
| 303 | targets[5] = pillars[5] + 1; | 206 | else { |
| 304 | 207 | _memory->WritePanelData<long long>(panel1, REFLECTION_DATA, { 0 }); | |
| 305 | std::vector<int> randomOrder(pillars.size(), 0); | ||
| 306 | std::iota(randomOrder.begin(), randomOrder.end(), 0); | ||
| 307 | Shuffle(randomOrder, 0, 4); // Left Pillars 1-4 | ||
| 308 | Shuffle(randomOrder, 5, 9); // Right Pillars 1-4 | ||
| 309 | ReassignTargets(pillars, randomOrder, targets); | ||
| 310 | // Turn off original starting panels | ||
| 311 | _memory->WriteEntityData<float>(pillars[0], POWER, {0.0f, 0.0f}); | ||
| 312 | _memory->WriteEntityData<float>(pillars[5], POWER, {0.0f, 0.0f}); | ||
| 313 | // Turn on new starting panels | ||
| 314 | _memory->WriteEntityData<float>(pillars[randomOrder[0]], POWER, {1.0f, 1.0f}); | ||
| 315 | _memory->WriteEntityData<float>(pillars[randomOrder[5]], POWER, {1.0f, 1.0f}); | ||
| 316 | } | ||
| 317 | |||
| 318 | void Randomizer::RandomizeChallenge() { | ||
| 319 | ChallengeRandomizer cr(_memory, Random::RandInt(1, 0x7FFFFFFF)); // 0 will trigger an "RNG not initialized" block | ||
| 320 | for (int panel : challengePanels) { | ||
| 321 | _memory->WriteEntityData<int>(panel, POWER_OFF_ON_FAIL, {0}); | ||
| 322 | } | ||
| 323 | } | ||
| 324 | |||
| 325 | void Randomizer::RandomizeAudioLogs() { | ||
| 326 | std::vector<int> randomOrder(audiologs.size(), 0); | ||
| 327 | std::iota(randomOrder.begin(), randomOrder.end(), 0); | ||
| 328 | Shuffle(randomOrder, 0, randomOrder.size()); | ||
| 329 | ReassignNames(audiologs, randomOrder); | ||
| 330 | } | ||
| 331 | |||
| 332 | void Randomizer::Randomize(std::vector<int>& panels, int flags) { | ||
| 333 | return RandomizeRange(panels, flags, 0, panels.size()); | ||
| 334 | } | ||
| 335 | |||
| 336 | // Range is [start, end) | ||
| 337 | void Randomizer::Shuffle(std::vector<int> &order, size_t startIndex, size_t endIndex) { | ||
| 338 | if (order.size() == 0) return; | ||
| 339 | if (startIndex >= endIndex) return; | ||
| 340 | if (endIndex >= order.size()) endIndex = static_cast<int>(order.size()); | ||
| 341 | for (size_t i = endIndex - 1; i > startIndex; i--) { | ||
| 342 | const int target = Random::RandInt(static_cast<int>(startIndex), static_cast<int>(i)); | ||
| 343 | std::swap(order[i], order[target]); | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | // Range is [start, end) | ||
| 348 | void Randomizer::RandomizeRange(std::vector<int> panels, int flags, size_t startIndex, size_t endIndex) { | ||
| 349 | if (panels.size() == 0) return; | ||
| 350 | if (startIndex >= endIndex) return; | ||
| 351 | if (endIndex >= panels.size()) endIndex = static_cast<int>(panels.size()); | ||
| 352 | for (size_t i = endIndex-1; i > startIndex; i--) { | ||
| 353 | const int target = Random::RandInt(static_cast<int>(startIndex), static_cast<int>(i)); | ||
| 354 | if (i != target) { | ||
| 355 | // std::cout << "Swapping panels " << std::hex << panels[i] << " and " << std::hex << panels[target] << std::endl; | ||
| 356 | SwapPanels(panels[i], panels[target], flags); | ||
| 357 | std::swap(panels[i], panels[target]); // Panel indices in the array | ||
| 358 | } | ||
| 359 | } | 208 | } |
| 360 | } | 209 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, SEQUENCE_LEN, sizeof(int)); |
| 361 | 210 | _memory->CopyArrayDynamicSize<int>(tutorialStraight, panel1, SEQUENCE, SEQUENCE_LEN); | |
| 362 | void Randomizer::SwapPanels(int panel1, int panel2, int flags) { | 211 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, DOT_SEQUENCE_LEN, sizeof(int)); |
| 363 | std::map<int, int> offsets; | 212 | _memory->CopyArrayDynamicSize<int>(tutorialStraight, panel1, DOT_SEQUENCE, DOT_SEQUENCE_LEN); |
| 364 | 213 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, DOT_SEQUENCE_LEN_REFLECTION, sizeof(int)); | |
| 365 | if (flags & SWAP::TARGETS) { | 214 | _memory->CopyArrayDynamicSize<int>(tutorialStraight, panel1, DOT_SEQUENCE_REFLECTION, DOT_SEQUENCE_LEN_REFLECTION); |
| 366 | offsets[TARGET] = sizeof(int); | 215 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, GRID_SIZE_X, sizeof(int)); |
| 367 | } | 216 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, GRID_SIZE_Y, sizeof(int)); |
| 368 | if (flags & SWAP::AUDIO_NAMES) { | 217 | _memory->CopyEntityData<byte>(tutorialStraight, panel1, STYLE_FLAGS, sizeof(int)); |
| 369 | offsets[AUDIO_LOG_NAME] = sizeof(void*); | 218 | _memory->WritePanelData<byte>(panel1, RANDOMISE_ON_POWER_ON, { 0 }); |
| 370 | } | 219 | |
| 371 | if (flags & SWAP::COLORS) { | 220 | |
| 372 | offsets[PATH_COLOR] = 16; | 221 | //arrays.push_back(AUDIO_PREFIX); |
| 373 | offsets[REFLECTION_PATH_COLOR] = 16; | ||
| 374 | offsets[DOT_COLOR] = 16; | ||
| 375 | offsets[ACTIVE_COLOR] = 16; | ||
| 376 | offsets[BACKGROUND_REGION_COLOR] = 12; // Not copying alpha to preserve transparency. | ||
| 377 | offsets[SUCCESS_COLOR_A] = 16; | ||
| 378 | offsets[SUCCESS_COLOR_B] = 16; | ||
| 379 | offsets[STROBE_COLOR_A] = 16; | ||
| 380 | offsets[STROBE_COLOR_B] = 16; | ||
| 381 | offsets[ERROR_COLOR] = 16; | ||
| 382 | offsets[PATTERN_POINT_COLOR] = 16; | ||
| 383 | offsets[PATTERN_POINT_COLOR_A] = 16; | ||
| 384 | offsets[PATTERN_POINT_COLOR_B] = 16; | ||
| 385 | offsets[SYMBOL_A] = 16; | ||
| 386 | offsets[SYMBOL_B] = 16; | ||
| 387 | offsets[SYMBOL_C] = 16; | ||
| 388 | offsets[SYMBOL_D] = 16; | ||
| 389 | offsets[SYMBOL_E] = 16; | ||
| 390 | offsets[PUSH_SYMBOL_COLORS] = sizeof(int); | ||
| 391 | offsets[OUTER_BACKGROUND] = 16; | ||
| 392 | offsets[OUTER_BACKGROUND_MODE] = sizeof(int); | ||
| 393 | // These two control the "burn intensity", but I can't swap out burn marks in new ver, so they should probably stay tied to each frame. | ||
| 394 | // offsets[SPECULAR_ADD] = sizeof(float); | ||
| 395 | // offsets[SPECULAR_POWER] = sizeof(int); | ||
| 396 | offsets[NUM_COLORED_REGIONS] = sizeof(int); | ||
| 397 | offsets[COLORED_REGIONS] = sizeof(void*); | ||
| 398 | } | ||
| 399 | if (flags & SWAP::LINES) { | ||
| 400 | offsets[TRACED_EDGES] = 16; | ||
| 401 | offsets[AUDIO_PREFIX] = sizeof(void*); | ||
| 402 | // offsets[IS_CYLINDER] = sizeof(int); | 222 | // offsets[IS_CYLINDER] = sizeof(int); |
| 403 | // offsets[CYLINDER_Z0] = sizeof(float); | 223 | // offsets[CYLINDER_Z0] = sizeof(float); |
| 404 | // offsets[CYLINDER_Z1] = sizeof(float); | 224 | // offsets[CYLINDER_Z1] = sizeof(float); |
| 405 | // offsets[CYLINDER_RADIUS] = sizeof(float); | 225 | // offsets[CYLINDER_RADIUS] = sizeof(float); |
| 406 | offsets[PATH_WIDTH_SCALE] = sizeof(float); | ||
| 407 | offsets[STARTPOINT_SCALE] = sizeof(float); | ||
| 408 | offsets[NUM_DOTS] = sizeof(int); | ||
| 409 | offsets[NUM_CONNECTIONS] = sizeof(int); | ||
| 410 | offsets[DOT_POSITIONS] = sizeof(void*); | ||
| 411 | offsets[DOT_FLAGS] = sizeof(void*); | ||
| 412 | offsets[DOT_CONNECTION_A] = sizeof(void*); | ||
| 413 | offsets[DOT_CONNECTION_B] = sizeof(void*); | ||
| 414 | offsets[DECORATIONS] = sizeof(void*); | ||
| 415 | offsets[DECORATION_FLAGS] = sizeof(void*); | ||
| 416 | offsets[DECORATION_COLORS] = sizeof(void*); | ||
| 417 | offsets[NUM_DECORATIONS] = sizeof(int); | ||
| 418 | offsets[REFLECTION_DATA] = sizeof(void*); | ||
| 419 | offsets[GRID_SIZE_X] = sizeof(int); | ||
| 420 | offsets[GRID_SIZE_Y] = sizeof(int); | ||
| 421 | offsets[STYLE_FLAGS] = sizeof(int); | ||
| 422 | offsets[SEQUENCE_LEN] = sizeof(int); | ||
| 423 | offsets[SEQUENCE] = sizeof(void*); | ||
| 424 | offsets[DOT_SEQUENCE_LEN] = sizeof(int); | ||
| 425 | offsets[DOT_SEQUENCE] = sizeof(void*); | ||
| 426 | offsets[DOT_SEQUENCE_LEN_REFLECTION] = sizeof(int); | ||
| 427 | offsets[DOT_SEQUENCE_REFLECTION] = sizeof(void*); | ||
| 428 | offsets[PANEL_TARGET] = sizeof(void*); | ||
| 429 | offsets[SPECULAR_TEXTURE] = sizeof(void*); | ||
| 430 | } | ||
| 431 | 226 | ||
| 432 | for (auto const& [offset, size] : offsets) { | 227 | //arrays.push_back(PANEL_TARGET); |
| 433 | std::vector<byte> panel1data = _memory->ReadEntityData<byte>(panel1, offset, size); | 228 | //arrays.push_back(SPECULAR_TEXTURE); |
| 434 | std::vector<byte> panel2data = _memory->ReadEntityData<byte>(panel2, offset, size); | ||
| 435 | _memory->WriteEntityData<byte>(panel2, offset, panel1data); | ||
| 436 | _memory->WriteEntityData<byte>(panel1, offset, panel2data); | ||
| 437 | } | ||
| 438 | } | ||
| 439 | 229 | ||
| 440 | void Randomizer::ReassignTargets(const std::vector<int>& panels, const std::vector<int>& order, std::vector<int> targets) { | ||
| 441 | if (targets.empty()) { | ||
| 442 | // This list is offset by 1, so the target of the Nth panel is in position N (aka the N+1th element) | ||
| 443 | // The first panel may not have a wire to power it, so we use the panel ID itself. | ||
| 444 | targets = {panels[0] + 1}; | ||
| 445 | for (const int panel : panels) { | ||
| 446 | int target = _memory->ReadEntityData<int>(panel, TARGET, 1)[0]; | ||
| 447 | targets.push_back(target); | ||
| 448 | } | ||
| 449 | } | ||
| 450 | |||
| 451 | for (size_t i=0; i<order.size() - 1; i++) { | ||
| 452 | // Set the target of order[i] to order[i+1], using the "real" target as determined above. | ||
| 453 | const int panelTarget = targets[order[i+1]]; | ||
| 454 | _memory->WriteEntityData<int>(panels[order[i]], TARGET, {panelTarget}); | ||
| 455 | } | ||
| 456 | } | ||
| 457 | |||
| 458 | void Randomizer::ReassignNames(const std::vector<int>& panels, const std::vector<int>& order) { | ||
| 459 | std::vector<int64_t> names; | ||
| 460 | for (const int panel : panels) { | ||
| 461 | names.push_back(_memory->ReadEntityData<int64_t>(panel, AUDIO_LOG_NAME, 1)[0]); | ||
| 462 | } | ||
| 463 | |||
| 464 | for (int i=0; i<panels.size(); i++) { | ||
| 465 | _memory->WriteEntityData<int64_t>(panels[i], AUDIO_LOG_NAME, {names[order[i]]}); | ||
| 466 | } | ||
| 467 | } | 230 | } |
| diff --git a/Source/Randomizer.h b/Source/Randomizer.h index a416c98..625dcc0 100644 --- a/Source/Randomizer.h +++ b/Source/Randomizer.h | |||
| @@ -4,45 +4,14 @@ class Randomizer { | |||
| 4 | public: | 4 | public: |
| 5 | Randomizer(const std::shared_ptr<Memory>& memory); | 5 | Randomizer(const std::shared_ptr<Memory>& memory); |
| 6 | void Randomize(); | 6 | void Randomize(); |
| 7 | void RandomizeChallenge(); | ||
| 8 | |||
| 9 | void AdjustSpeed(); | ||
| 10 | void RandomizeLasers(); | ||
| 11 | void PreventSnipes(); | ||
| 12 | |||
| 13 | enum SWAP { | ||
| 14 | // NONE = 0, | ||
| 15 | TARGETS = 1, | ||
| 16 | LINES = 2, | ||
| 17 | AUDIO_NAMES = 4, | ||
| 18 | COLORS = 8, | ||
| 19 | }; | ||
| 20 | 7 | ||
| 21 | private: | 8 | private: |
| 22 | int _lastRandomizedFrame = 1 << 30; | 9 | int _lastRandomizedFrame = 1 << 30; |
| 23 | void RandomizeTutorial(); | ||
| 24 | void RandomizeSymmetry(); | ||
| 25 | void RandomizeDesert(); | ||
| 26 | void RandomizeQuarry(); | ||
| 27 | void RandomizeTreehouse(); | ||
| 28 | void RandomizeKeep(); | ||
| 29 | void RandomizeShadows(); | ||
| 30 | void RandomizeTown(); | ||
| 31 | void RandomizeMonastery(); | ||
| 32 | void RandomizeBunker(); | ||
| 33 | void RandomizeJungle(); | ||
| 34 | void RandomizeSwamp(); | ||
| 35 | void RandomizeMountain(); | ||
| 36 | void RandomizeAudioLogs(); | ||
| 37 | 10 | ||
| 38 | void Randomize(std::vector<int>& panels, int flags); | 11 | void Tutorialise(int panel1, int copyFrom); |
| 39 | void Shuffle(std::vector<int>&order, size_t startIndex, size_t endIndex); | ||
| 40 | void RandomizeRange(std::vector<int> panels, int flags, size_t startIndex, size_t endIndex); | ||
| 41 | void SwapPanels(int panel1, int panel2, int flags); | ||
| 42 | void ReassignTargets(const std::vector<int>& panels, const std::vector<int>& order, std::vector<int> targets = {}); | ||
| 43 | void ReassignNames(const std::vector<int>& panels, const std::vector<int>& order); | ||
| 44 | 12 | ||
| 45 | std::shared_ptr<Memory> _memory; | 13 | std::shared_ptr<Memory> _memory; |
| 46 | 14 | ||
| 47 | friend class SwapTests_Shipwreck_Test; | 15 | friend class SwapTests_Shipwreck_Test; |
| 48 | }; | 16 | }; |
| 17 | |||
| diff --git a/Source/Randomizer2.cpp b/Source/Randomizer2.cpp deleted file mode 100644 index 421ce69..0000000 --- a/Source/Randomizer2.cpp +++ /dev/null | |||
| @@ -1,644 +0,0 @@ | |||
| 1 | #include "pch.h" | ||
| 2 | #include "Randomizer2.h" | ||
| 3 | #include "PuzzleSerializer.h" | ||
| 4 | #include "Randomizer2Core.h" | ||
| 5 | #include "Random.h" | ||
| 6 | #include "Solver.h" | ||
| 7 | #include "Windows.h" | ||
| 8 | |||
| 9 | Randomizer2::Randomizer2(const PuzzleSerializer& serializer) : _serializer(serializer) { | ||
| 10 | } | ||
| 11 | |||
| 12 | void Randomizer2::Randomize() { | ||
| 13 | // RandomizeTutorial(); | ||
| 14 | // RandomizeGlassFactory(); | ||
| 15 | RandomizeSymmetryIsland(); | ||
| 16 | // RandomizeKeep(); | ||
| 17 | } | ||
| 18 | |||
| 19 | void Randomizer2::RandomizeTutorial() { | ||
| 20 | { // Far center | ||
| 21 | Puzzle p; | ||
| 22 | p.NewGrid(4, 4); | ||
| 23 | p.grid[0][8].start = true; | ||
| 24 | p.grid[8][0].end = Cell::Dir::UP; | ||
| 25 | |||
| 26 | for (Pos pos : Randomizer2Core::CutEdges(p, 14)) { | ||
| 27 | p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | ||
| 28 | } | ||
| 29 | _serializer.WritePuzzle(p, 0x293); | ||
| 30 | } | ||
| 31 | |||
| 32 | { // Center left | ||
| 33 | Puzzle p; | ||
| 34 | p.NewGrid(6, 6); | ||
| 35 | |||
| 36 | int x = Random::RandInt(0, (p.width-1)/2)*2; | ||
| 37 | int y = Random::RandInt(0, (p.height-1)/2)*2; | ||
| 38 | int rng = Random::RandInt(1, 4); | ||
| 39 | if (rng == 1) p.grid[x][0].end = Cell::Dir::UP; | ||
| 40 | else if (rng == 2) p.grid[x][p.height-1].end = Cell::Dir::DOWN; | ||
| 41 | else if (rng == 3) p.grid[0][y].end = Cell::Dir::LEFT; | ||
| 42 | else if (rng == 4) p.grid[p.width-1][y].end = Cell::Dir::RIGHT; | ||
| 43 | |||
| 44 | // [4/6/8][4/6/8] | ||
| 45 | p.grid[Random::RandInt(0, 2)*2 + 4][Random::RandInt(0, 2)*2 + 4].start = true; | ||
| 46 | |||
| 47 | for (Pos pos : Randomizer2Core::CutEdges(p, 35)) { | ||
| 48 | p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | ||
| 49 | } | ||
| 50 | |||
| 51 | _serializer.WritePuzzle(p, 0x295); | ||
| 52 | } | ||
| 53 | |||
| 54 | { // Far left | ||
| 55 | Puzzle p; | ||
| 56 | p.NewGrid(10, 10); | ||
| 57 | |||
| 58 | p.grid[0][20].start = true; | ||
| 59 | p.grid[20][0].end = Cell::Dir::RIGHT; | ||
| 60 | |||
| 61 | for (Pos pos : Randomizer2Core::CutEdges(p, 96)) { | ||
| 62 | p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | ||
| 63 | } | ||
| 64 | _serializer.WritePuzzle(p, 0x2C2); | ||
| 65 | } | ||
| 66 | |||
| 67 | { // Back left | ||
| 68 | Puzzle p; | ||
| 69 | p.NewGrid(6, 6); | ||
| 70 | |||
| 71 | p.grid[0][12].start = true; | ||
| 72 | p.grid[12][0].end = Cell::Dir::RIGHT; | ||
| 73 | p.grid[12][12].end = Cell::Dir::RIGHT; | ||
| 74 | |||
| 75 | for (Pos pos : Randomizer2Core::CutEdges(p, 27)) { | ||
| 76 | p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | ||
| 77 | } | ||
| 78 | _serializer.WritePuzzle(p, 0xA3B5); | ||
| 79 | } | ||
| 80 | |||
| 81 | { // Back right | ||
| 82 | Puzzle p; | ||
| 83 | p.NewGrid(6, 6); | ||
| 84 | |||
| 85 | p.grid[0][12].start = true; | ||
| 86 | p.grid[12][12].start = true; | ||
| 87 | p.grid[6][0].end = Cell::Dir::UP; | ||
| 88 | |||
| 89 | // @Cleanup | ||
| 90 | std::vector<Pos> cuts; | ||
| 91 | bool toTheRight; | ||
| 92 | // Start by generating a cut line, to ensure one of the two startpoints is inaccessible | ||
| 93 | int x, y; | ||
| 94 | switch (Random::RandInt(1, 4)) { | ||
| 95 | case 1: | ||
| 96 | x = 1; y = 1; | ||
| 97 | toTheRight = true; | ||
| 98 | cuts.emplace_back(0, 1); | ||
| 99 | break; | ||
| 100 | case 2: | ||
| 101 | x = 1; y = 1; | ||
| 102 | toTheRight = true; | ||
| 103 | cuts.emplace_back(1, 0); | ||
| 104 | break; | ||
| 105 | case 3: | ||
| 106 | x = 11; y = 1; | ||
| 107 | toTheRight = false; | ||
| 108 | cuts.emplace_back(12, 1); | ||
| 109 | break; | ||
| 110 | case 4: | ||
| 111 | x = 11; y = 1; | ||
| 112 | toTheRight = false; | ||
| 113 | cuts.emplace_back(11, 0); | ||
| 114 | break; | ||
| 115 | } | ||
| 116 | while (y < p.height) { // The final cut will push y below the bottom of the puzzle, which means we're done. | ||
| 117 | switch (Random::RandInt(1, 4)) { | ||
| 118 | case 1: // Go right | ||
| 119 | if (x < p.width-2) { | ||
| 120 | cuts.emplace_back(x+1, y); | ||
| 121 | x += 2; | ||
| 122 | } | ||
| 123 | break; | ||
| 124 | case 2: // Go left | ||
| 125 | if (x > 1) { | ||
| 126 | cuts.emplace_back(x-1, y); | ||
| 127 | x -= 2; | ||
| 128 | } | ||
| 129 | break; | ||
| 130 | case 3: | ||
| 131 | case 4: // Go down (biased x2) | ||
| 132 | cuts.emplace_back(x, y+1); | ||
| 133 | y += 2; | ||
| 134 | break; | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | for (Pos pos : cuts) { | ||
| 139 | p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | ||
| 140 | } | ||
| 141 | |||
| 142 | for (Pos pos : Randomizer2Core::CutEdges(p, 30 - cuts.size())) { | ||
| 143 | p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | ||
| 144 | } | ||
| 145 | _serializer.WritePuzzle(p, 0xA3B2); | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | void Randomizer2::RandomizeGlassFactory() { | ||
| 150 | { // Back wall 1 | ||
| 151 | Puzzle p; | ||
| 152 | p.NewGrid(3, 3); | ||
| 153 | p.symmetry = Puzzle::Symmetry::X; | ||
| 154 | p.grid[0][6].start = true; | ||
| 155 | p.grid[6][6].start = true; | ||
| 156 | p.grid[2][0].end = Cell::Dir::UP; | ||
| 157 | p.grid[4][0].end = Cell::Dir::UP; | ||
| 158 | |||
| 159 | std::vector<Pos> cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 2); | ||
| 160 | for (Pos pos : cutEdges) { | ||
| 161 | Pos sym = p.GetSymmetricalPos(pos.x, pos.y); | ||
| 162 | p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | ||
| 163 | p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK; | ||
| 164 | } | ||
| 165 | _serializer.WritePuzzle(p, 0x86); | ||
| 166 | } | ||
| 167 | { // Back wall 2 | ||
| 168 | Puzzle p; | ||
| 169 | p.NewGrid(4, 4); | ||
| 170 | p.symmetry = Puzzle::Symmetry::X; | ||
| 171 | p.grid[0][8].start = true; | ||
| 172 | p.grid[8][8].start = true; | ||
| 173 | p.grid[2][0].end = Cell::Dir::UP; | ||
| 174 | p.grid[6][0].end = Cell::Dir::UP; | ||
| 175 | std::vector<Pos> cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 4); | ||
| 176 | for (int i=0; i<cutEdges.size(); i++) { | ||
| 177 | Pos pos = cutEdges[i]; | ||
| 178 | if (i%2 == 0) { | ||
| 179 | p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | ||
| 180 | } else { | ||
| 181 | Pos sym = p.GetSymmetricalPos(pos.x, pos.y); | ||
| 182 | p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | _serializer.WritePuzzle(p, 0x87); | ||
| 187 | } | ||
| 188 | { // Back wall 3 | ||
| 189 | Puzzle p; | ||
| 190 | p.NewGrid(5, 6); | ||
| 191 | p.symmetry = Puzzle::Symmetry::X; | ||
| 192 | p.grid[2][10].start = true; | ||
| 193 | p.grid[8][10].start = true; | ||
| 194 | p.grid[4][0].end = Cell::Dir::UP; | ||
| 195 | p.grid[6][0].end = Cell::Dir::UP; | ||
| 196 | std::vector<Pos> cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 10); | ||
| 197 | for (int i=0; i<cutEdges.size(); i++) { | ||
| 198 | Pos pos = cutEdges[i]; | ||
| 199 | if (i%2 == 0) { | ||
| 200 | p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | ||
| 201 | } else { | ||
| 202 | Pos sym = p.GetSymmetricalPos(pos.x, pos.y); | ||
| 203 | p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK; | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | _serializer.WritePuzzle(p, 0x59); | ||
| 208 | } | ||
| 209 | { // Back wall 4 | ||
| 210 | Puzzle p; | ||
| 211 | p.NewGrid(5, 8); | ||
| 212 | p.symmetry = Puzzle::Symmetry::X; | ||
| 213 | p.grid[2][16].start = true; | ||
| 214 | p.grid[8][16].start = true; | ||
| 215 | p.grid[4][0].end = Cell::Dir::UP; | ||
| 216 | p.grid[6][0].end = Cell::Dir::UP; | ||
| 217 | std::vector<Pos> cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 15); | ||
| 218 | for (int i=0; i<cutEdges.size(); i++) { | ||
| 219 | Pos pos = cutEdges[i]; | ||
| 220 | if (i%2 == 0) { | ||
| 221 | p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | ||
| 222 | } else { | ||
| 223 | Pos sym = p.GetSymmetricalPos(pos.x, pos.y); | ||
| 224 | p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK; | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | _serializer.WritePuzzle(p, 0x62); | ||
| 229 | } | ||
| 230 | // TODO: Positioning is off, slightly -- which means you can start from the bottom left, if you peek around. | ||
| 231 | { // Back wall 5 | ||
| 232 | Puzzle p; | ||
| 233 | p.NewGrid(11, 8); | ||
| 234 | p.symmetry = Puzzle::Symmetry::X; | ||
| 235 | p.grid[0][16].start = true; | ||
| 236 | p.grid[10][16].start = true; | ||
| 237 | p.grid[12][16].start = true; | ||
| 238 | p.grid[22][16].start = true; | ||
| 239 | p.grid[2][0].end = Cell::Dir::UP; | ||
| 240 | p.grid[8][0].end = Cell::Dir::UP; | ||
| 241 | p.grid[14][0].end = Cell::Dir::UP; | ||
| 242 | p.grid[20][0].end = Cell::Dir::UP; | ||
| 243 | |||
| 244 | Puzzle q; | ||
| 245 | q.NewGrid(5, 8); | ||
| 246 | q.symmetry = Puzzle::Symmetry::X; | ||
| 247 | |||
| 248 | for (Pos pos : Randomizer2Core::CutSymmetricalEdgePairs(q, 16)) { | ||
| 249 | p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | ||
| 250 | } | ||
| 251 | for (Pos pos : Randomizer2Core::CutSymmetricalEdgePairs(q, 16)) { | ||
| 252 | p.grid[pos.x + 12][pos.y].gap = Cell::Gap::BREAK; | ||
| 253 | } | ||
| 254 | |||
| 255 | for (int y=0; y<p.height; y+=2) { | ||
| 256 | p.grid[5][y].gap = Cell::Gap::BREAK; | ||
| 257 | } | ||
| 258 | |||
| 259 | _serializer.WritePuzzle(p, 0x5C); | ||
| 260 | } | ||
| 261 | |||
| 262 | { // Rotational 1 | ||
| 263 | Puzzle p; | ||
| 264 | p.NewGrid(3, 3); | ||
| 265 | p.symmetry = Puzzle::Symmetry::XY; | ||
| 266 | p.grid[6][0].start = true; | ||
| 267 | p.grid[0][6].start = true; | ||
| 268 | p.grid[4][0].end = Cell::Dir::UP; | ||
| 269 | p.grid[2][6].end = Cell::Dir::DOWN; | ||
| 270 | |||
| 271 | p.grid[5][0].gap = Cell::Gap::BREAK; | ||
| 272 | p.grid[1][6].gap = Cell::Gap::BREAK; | ||
| 273 | |||
| 274 | for (Pos pos : Randomizer2Core::CutSymmetricalEdgePairs(p, 1)) { | ||
| 275 | p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | ||
| 276 | Pos sym = p.GetSymmetricalPos(pos.x, pos.y); | ||
| 277 | p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK; | ||
| 278 | } | ||
| 279 | _serializer.WritePuzzle(p, 0x8D); | ||
| 280 | } | ||
| 281 | { // Rotational 2 | ||
| 282 | Puzzle p; | ||
| 283 | p.NewGrid(3, 3); | ||
| 284 | p.symmetry = Puzzle::Symmetry::XY; | ||
| 285 | p.grid[6][0].start = true; | ||
| 286 | p.grid[0][6].start = true; | ||
| 287 | p.grid[4][0].end = Cell::Dir::UP; | ||
| 288 | p.grid[2][6].end = Cell::Dir::DOWN; | ||
| 289 | |||
| 290 | p.grid[5][0].gap = Cell::Gap::BREAK; | ||
| 291 | p.grid[1][6].gap = Cell::Gap::BREAK; | ||
| 292 | |||
| 293 | std::vector<Pos> cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 3); | ||
| 294 | for (int i=0; i<cutEdges.size(); i++) { | ||
| 295 | Pos pos = cutEdges[i]; | ||
| 296 | if (i%2 == 0) { | ||
| 297 | p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | ||
| 298 | } else { | ||
| 299 | Pos sym = p.GetSymmetricalPos(pos.x, pos.y); | ||
| 300 | p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK; | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 304 | p.grid[1][6].gap = Cell::Gap::NONE; | ||
| 305 | |||
| 306 | _serializer.WritePuzzle(p, 0x81); | ||
| 307 | } | ||
| 308 | { // Rotational 3 | ||
| 309 | Puzzle p; | ||
| 310 | p.NewGrid(4, 4); | ||
| 311 | p.symmetry = Puzzle::Symmetry::XY; | ||
| 312 | p.grid[8][0].start = true; | ||
| 313 | p.grid[0][8].start = true; | ||
| 314 | p.grid[0][0].end = Cell::Dir::LEFT; | ||
| 315 | p.grid[8][8].end = Cell::Dir::RIGHT; | ||
| 316 | |||
| 317 | std::vector<Pos> cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 7); | ||
| 318 | for (int i=0; i<cutEdges.size(); i++) { | ||
| 319 | Pos pos = cutEdges[i]; | ||
| 320 | if (i%2 == 0) { | ||
| 321 | p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | ||
| 322 | } else { | ||
| 323 | Pos sym = p.GetSymmetricalPos(pos.x, pos.y); | ||
| 324 | p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK; | ||
| 325 | } | ||
| 326 | } | ||
| 327 | |||
| 328 | _serializer.WritePuzzle(p, 0x83); | ||
| 329 | } | ||
| 330 | { // Melting | ||
| 331 | Puzzle p; | ||
| 332 | p.NewGrid(6, 6); | ||
| 333 | p.symmetry = Puzzle::Symmetry::XY; | ||
| 334 | p.grid[12][0].start = true; | ||
| 335 | p.grid[0][12].start = true; | ||
| 336 | p.grid[0][0].end = Cell::Dir::LEFT; | ||
| 337 | p.grid[12][12].end = Cell::Dir::RIGHT; | ||
| 338 | Puzzle q = p; | ||
| 339 | |||
| 340 | std::vector<Pos> cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 15); | ||
| 341 | for (int i=0; i<cutEdges.size(); i++) { | ||
| 342 | Pos pos = cutEdges[i]; | ||
| 343 | Pos sym = p.GetSymmetricalPos(pos.x, pos.y); | ||
| 344 | |||
| 345 | if (i%2 == 0) { | ||
| 346 | p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | ||
| 347 | } else { | ||
| 348 | p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK; | ||
| 349 | } | ||
| 350 | |||
| 351 | if (pos.x < sym.x) { | ||
| 352 | q.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | ||
| 353 | } else { | ||
| 354 | q.grid[sym.x][sym.y].gap = Cell::Gap::BREAK; | ||
| 355 | } | ||
| 356 | } | ||
| 357 | |||
| 358 | _serializer.WritePuzzle(p, 0x84); // Melting 1 | ||
| 359 | _serializer.WritePuzzle(q, 0x82); // Melting 2 | ||
| 360 | _serializer.WritePuzzle(q, 0x343A); // Melting 3 | ||
| 361 | } | ||
| 362 | } | ||
| 363 | |||
| 364 | void Randomizer2::RandomizeSymmetryIsland() { | ||
| 365 | { // Entry door | ||
| 366 | Puzzle p; | ||
| 367 | p.NewGrid(4, 3); | ||
| 368 | p.grid[0][6].start = true; | ||
| 369 | p.grid[8][0].end = Cell::Dir::RIGHT; | ||
| 370 | p.grid[4][3].gap = Cell::Gap::FULL; | ||
| 371 | |||
| 372 | std::vector<Pos> corners; | ||
| 373 | std::vector<Pos> cells; | ||
| 374 | std::vector<Pos> edges; | ||
| 375 | for (int x=0; x<p.width; x++) { | ||
| 376 | for (int y=0; y<p.height; y++) { | ||
| 377 | if (x%2 == 0 && y%2 == 0) corners.emplace_back(Pos{x, y}); | ||
| 378 | else if (x%2 == 1 && y%2 == 1) cells.emplace_back(Pos{x, y}); | ||
| 379 | else edges.emplace_back(Pos{x, y}); | ||
| 380 | } | ||
| 381 | } | ||
| 382 | |||
| 383 | for (int j=0;; j++) { | ||
| 384 | std::vector<Pos> dots = Random::SelectFromSet(edges, 4); | ||
| 385 | for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::BLACK; | ||
| 386 | |||
| 387 | auto solutions = Solver::Solve(p); | ||
| 388 | if (solutions.size() > 0 && solutions.size() < 10) break; | ||
| 389 | |||
| 390 | for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::NONE; | ||
| 391 | } | ||
| 392 | |||
| 393 | _serializer.WritePuzzle(p, 0xB0); | ||
| 394 | } | ||
| 395 | |||
| 396 | { // Dots 1 | ||
| 397 | Puzzle p; | ||
| 398 | p.NewGrid(3, 3); | ||
| 399 | p.symmetry = Puzzle::Symmetry::Y; | ||
| 400 | p.grid[0][2].start = true; | ||
| 401 | p.grid[0][4].start = true; | ||
| 402 | p.grid[6][2].end = Cell::Dir::RIGHT; | ||
| 403 | p.grid[6][4].end = Cell::Dir::RIGHT; | ||
| 404 | |||
| 405 | std::vector<Pos> corners; | ||
| 406 | std::vector<Pos> cells; | ||
| 407 | std::vector<Pos> edges; | ||
| 408 | for (int x=0; x<p.width; x++) { | ||
| 409 | for (int y=0; y<p.height/2; y++) { | ||
| 410 | if (x%2 == 0 && y%2 == 0) corners.emplace_back(Pos{x, y}); | ||
| 411 | else if (x%2 == 1 && y%2 == 1) cells.emplace_back(Pos{x, y}); | ||
| 412 | else edges.emplace_back(Pos{x, y}); | ||
| 413 | } | ||
| 414 | } | ||
| 415 | edges.insert(edges.end(), corners.begin(), corners.end()); | ||
| 416 | |||
| 417 | std::vector<Pos> dots; | ||
| 418 | for (int j=0;; j++) { | ||
| 419 | dots = Random::SelectFromSet(edges, 3); | ||
| 420 | for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::BLACK; | ||
| 421 | |||
| 422 | auto solutions = Solver::Solve(p); | ||
| 423 | if (solutions.size() == 2) break; | ||
| 424 | |||
| 425 | for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::NONE; | ||
| 426 | } | ||
| 427 | |||
| 428 | for (Pos pos : dots) { | ||
| 429 | Pos sym = p.GetSymmetricalPos(pos.x, pos.y); | ||
| 430 | p.grid[sym.x][sym.y].dot = Cell::Dot::BLACK; | ||
| 431 | } | ||
| 432 | |||
| 433 | _serializer.WritePuzzle(p, 0x22); | ||
| 434 | } | ||
| 435 | { // Dots 2 | ||
| 436 | Puzzle p; | ||
| 437 | p.NewGrid(3, 3); | ||
| 438 | p.symmetry = Puzzle::Symmetry::Y; | ||
| 439 | p.grid[0][2].start = true; | ||
| 440 | p.grid[0][4].start = true; | ||
| 441 | p.grid[6][2].end = Cell::Dir::RIGHT; | ||
| 442 | p.grid[6][4].end = Cell::Dir::RIGHT; | ||
| 443 | |||
| 444 | std::vector<Pos> corners; | ||
| 445 | std::vector<Pos> cells; | ||
| 446 | std::vector<Pos> edges; | ||
| 447 | for (int x=0; x<p.width; x++) { | ||
| 448 | for (int y=0; y<p.height/2; y++) { | ||
| 449 | if (x%2 == 0 && y%2 == 0) corners.emplace_back(Pos{x, y}); | ||
| 450 | else if (x%2 == 1 && y%2 == 1) cells.emplace_back(Pos{x, y}); | ||
| 451 | else edges.emplace_back(Pos{x, y}); | ||
| 452 | } | ||
| 453 | } | ||
| 454 | edges.insert(edges.end(), corners.begin(), corners.end()); | ||
| 455 | |||
| 456 | std::vector<Pos> dots; | ||
| 457 | for (int j=0;; j++) { | ||
| 458 | dots = Random::SelectFromSet(edges, 3); | ||
| 459 | for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::BLACK; | ||
| 460 | |||
| 461 | auto solutions = Solver::Solve(p); | ||
| 462 | if (solutions.size() == 2) break; | ||
| 463 | |||
| 464 | for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::NONE; | ||
| 465 | } | ||
| 466 | |||
| 467 | Pos pos = dots[1]; | ||
| 468 | Pos sym = p.GetSymmetricalPos(pos.x, pos.y); | ||
| 469 | p.grid[pos.x][pos.y].dot = Cell::Dot::NONE; | ||
| 470 | p.grid[sym.x][sym.y].dot = Cell::Dot::BLACK; | ||
| 471 | |||
| 472 | _serializer.WritePuzzle(p, 0x23); | ||
| 473 | } | ||
| 474 | } | ||
| 475 | |||
| 476 | void Randomizer2::RandomizeKeep() { | ||
| 477 | { // Hedges 1 | ||
| 478 | Puzzle p; | ||
| 479 | p.NewGrid(4, 4); | ||
| 480 | |||
| 481 | p.grid[2][1].gap = Cell::Gap::FULL; | ||
| 482 | p.grid[4][1].gap = Cell::Gap::FULL; | ||
| 483 | p.grid[6][1].gap = Cell::Gap::FULL; | ||
| 484 | p.grid[3][2].gap = Cell::Gap::FULL; | ||
| 485 | p.grid[5][2].gap = Cell::Gap::FULL; | ||
| 486 | p.grid[8][3].gap = Cell::Gap::FULL; | ||
| 487 | p.grid[2][5].gap = Cell::Gap::FULL; | ||
| 488 | p.grid[6][5].gap = Cell::Gap::FULL; | ||
| 489 | p.grid[7][6].gap = Cell::Gap::FULL; | ||
| 490 | p.grid[2][7].gap = Cell::Gap::FULL; | ||
| 491 | p.grid[4][7].gap = Cell::Gap::FULL; | ||
| 492 | |||
| 493 | p.grid[4][8].start = true; | ||
| 494 | p.grid[6][0].end = Cell::Dir::UP; | ||
| 495 | |||
| 496 | std::vector<Pos> cutEdges = Randomizer2Core::CutInsideEdges(p, 5); | ||
| 497 | Puzzle copy = p; | ||
| 498 | std::vector<int> gates = {0x00344, 0x00488, 0x00489, 0x00495, 0x00496}; | ||
| 499 | for (int i=0; i<cutEdges.size(); i++) { | ||
| 500 | Pos pos = cutEdges[i]; | ||
| 501 | copy.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | ||
| 502 | SetGate(gates[i], pos.x, pos.y); | ||
| 503 | } | ||
| 504 | auto solution = GetUniqueSolution(copy); | ||
| 505 | p.sequence = solution.sequence; | ||
| 506 | _serializer.WritePuzzle(p, 0x139); | ||
| 507 | } | ||
| 508 | |||
| 509 | { // Hedges 2 | ||
| 510 | Puzzle p; | ||
| 511 | p.NewGrid(4, 4); | ||
| 512 | |||
| 513 | p.grid[2][1].gap = Cell::Gap::FULL; | ||
| 514 | p.grid[1][2].gap = Cell::Gap::FULL; | ||
| 515 | p.grid[5][2].gap = Cell::Gap::FULL; | ||
| 516 | p.grid[7][4].gap = Cell::Gap::FULL; | ||
| 517 | p.grid[4][5].gap = Cell::Gap::FULL; | ||
| 518 | p.grid[6][5].gap = Cell::Gap::FULL; | ||
| 519 | p.grid[1][6].gap = Cell::Gap::FULL; | ||
| 520 | p.grid[2][7].gap = Cell::Gap::FULL; | ||
| 521 | p.grid[5][8].gap = Cell::Gap::FULL; | ||
| 522 | |||
| 523 | p.grid[0][8].start = true; | ||
| 524 | p.grid[8][0].end = Cell::Dir::RIGHT; | ||
| 525 | |||
| 526 | std::vector<Pos> cutEdges = Randomizer2Core::CutInsideEdges(p, 7); | ||
| 527 | for (Pos pos : cutEdges) { | ||
| 528 | p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | ||
| 529 | } | ||
| 530 | auto solution = GetUniqueSolution(p); | ||
| 531 | |||
| 532 | Puzzle q; | ||
| 533 | q.NewGrid(4, 4); | ||
| 534 | q.grid[0][8].start = true; | ||
| 535 | q.grid[8][0].end = Cell::Dir::RIGHT; | ||
| 536 | q.sequence = solution.sequence; | ||
| 537 | for (Pos pos : cutEdges) { | ||
| 538 | q.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | ||
| 539 | } | ||
| 540 | // Cut to 6 of 9 additional edges | ||
| 541 | for (Pos pos : Randomizer2Core::CutInsideEdges(q, 6)) { | ||
| 542 | q.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | ||
| 543 | } | ||
| 544 | _serializer.WritePuzzle(q, 0x19DC); | ||
| 545 | } | ||
| 546 | |||
| 547 | { // Hedges 3 [WIP] | ||
| 548 | Puzzle p; | ||
| 549 | p.NewGrid(4, 4); | ||
| 550 | |||
| 551 | p.grid[2][1].gap = Cell::Gap::FULL; | ||
| 552 | p.grid[5][2].gap = Cell::Gap::FULL; | ||
| 553 | p.grid[7][2].gap = Cell::Gap::FULL; | ||
| 554 | p.grid[4][3].gap = Cell::Gap::FULL; | ||
| 555 | p.grid[1][4].gap = Cell::Gap::FULL; | ||
| 556 | p.grid[6][5].gap = Cell::Gap::FULL; | ||
| 557 | p.grid[1][6].gap = Cell::Gap::FULL; | ||
| 558 | p.grid[3][6].gap = Cell::Gap::FULL; | ||
| 559 | p.grid[6][7].gap = Cell::Gap::FULL; | ||
| 560 | |||
| 561 | p.grid[0][8].start = true; | ||
| 562 | p.grid[8][2].end = Cell::Dir::RIGHT; | ||
| 563 | |||
| 564 | std::vector<Pos> cutEdges = Randomizer2Core::CutInsideEdges(p, 7); | ||
| 565 | for (Pos pos : cutEdges) { | ||
| 566 | p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | ||
| 567 | } | ||
| 568 | |||
| 569 | std::vector<int> pebbleMarkers = {0x034a9, 0x034b1, 0x034be, 0x034c4}; | ||
| 570 | |||
| 571 | // _serializer.WritePuzzle(p, 0x19E7); | ||
| 572 | } | ||
| 573 | |||
| 574 | { // Hedges 4 | ||
| 575 | Puzzle p; | ||
| 576 | p.NewGrid(4, 4); | ||
| 577 | |||
| 578 | p.grid[3][0].gap = Cell::Gap::FULL; | ||
| 579 | p.grid[4][1].gap = Cell::Gap::FULL; | ||
| 580 | p.grid[8][1].gap = Cell::Gap::FULL; | ||
| 581 | p.grid[1][2].gap = Cell::Gap::FULL; | ||
| 582 | p.grid[4][3].gap = Cell::Gap::FULL; | ||
| 583 | p.grid[8][3].gap = Cell::Gap::FULL; | ||
| 584 | p.grid[1][4].gap = Cell::Gap::FULL; | ||
| 585 | p.grid[5][4].gap = Cell::Gap::FULL; | ||
| 586 | p.grid[2][5].gap = Cell::Gap::FULL; | ||
| 587 | p.grid[6][5].gap = Cell::Gap::FULL; | ||
| 588 | p.grid[3][6].gap = Cell::Gap::FULL; | ||
| 589 | p.grid[0][7].gap = Cell::Gap::FULL; | ||
| 590 | p.grid[8][7].gap = Cell::Gap::FULL; | ||
| 591 | p.grid[5][8].gap = Cell::Gap::FULL; | ||
| 592 | |||
| 593 | p.grid[0][8].start = true; | ||
| 594 | p.grid[4][0].end = Cell::Dir::UP; | ||
| 595 | |||
| 596 | std::vector<Pos> cutEdges = Randomizer2Core::CutInsideEdges(p, 2); | ||
| 597 | for (Pos pos : cutEdges) { | ||
| 598 | p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | ||
| 599 | } | ||
| 600 | auto solution = GetUniqueSolution(p); | ||
| 601 | |||
| 602 | Puzzle q; | ||
| 603 | q.NewGrid(4, 4); | ||
| 604 | q.grid[0][8].start = true; | ||
| 605 | q.grid[4][0].end = Cell::Dir::UP; | ||
| 606 | q.sequence = solution.sequence; | ||
| 607 | for (Pos pos : cutEdges) { | ||
| 608 | q.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | ||
| 609 | } | ||
| 610 | for (Pos pos : Randomizer2Core::CutInsideEdges(q, 7)) { | ||
| 611 | q.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | ||
| 612 | } | ||
| 613 | _serializer.WritePuzzle(q, 0x1A0F); | ||
| 614 | } | ||
| 615 | } | ||
| 616 | |||
| 617 | Puzzle Randomizer2::GetUniqueSolution(Puzzle& p) { | ||
| 618 | auto solutions = Solver::Solve(p); | ||
| 619 | assert(solutions.size() == 1); | ||
| 620 | return solutions[0]; | ||
| 621 | } | ||
| 622 | |||
| 623 | void Randomizer2::SetGate(int panel, int X, int Y) { | ||
| 624 | float x, y, z, w; | ||
| 625 | if (X%2 == 0 && Y%2 == 1) { // Horizontal | ||
| 626 | x = -1.49f * X + 0.22f * Y + 66.58f; | ||
| 627 | y = 0.275f * X + 1.6f * Y + 108.4f; | ||
| 628 | z = -.77f; | ||
| 629 | w = .63f; | ||
| 630 | } else { // Vertical | ||
| 631 | assert(X%2 == 1 && Y%2 == 0); | ||
| 632 | x = -1.6f * X + 0.35f * Y + 66.5f; | ||
| 633 | y = 0.25f * X + 1.6f * Y + 108.55f; | ||
| 634 | z = -0.1f; | ||
| 635 | w = 1.0f; | ||
| 636 | } | ||
| 637 | |||
| 638 | SetPos(panel, x, y, 19.2f); | ||
| 639 | // _memory->WriteEntityData<float>(panel, ORIENTATION, {0.0f, 0.0f, z, w}); | ||
| 640 | } | ||
| 641 | |||
| 642 | void Randomizer2::SetPos(int panel, float x, float y, float z) { | ||
| 643 | // _memory->WriteEntityData<float>(panel, POSITION, {x, y, z}); | ||
| 644 | } \ No newline at end of file | ||
| diff --git a/Source/Randomizer2.h b/Source/Randomizer2.h deleted file mode 100644 index a2b5ebd..0000000 --- a/Source/Randomizer2.h +++ /dev/null | |||
| @@ -1,19 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | #include "PuzzleSerializer.h" | ||
| 3 | |||
| 4 | class Randomizer2 { | ||
| 5 | public: | ||
| 6 | Randomizer2(const PuzzleSerializer& serializer); | ||
| 7 | void Randomize(); | ||
| 8 | void RandomizeTutorial(); | ||
| 9 | void RandomizeGlassFactory(); | ||
| 10 | void RandomizeSymmetryIsland(); | ||
| 11 | void RandomizeKeep(); | ||
| 12 | |||
| 13 | private: | ||
| 14 | Puzzle GetUniqueSolution(Puzzle& p); | ||
| 15 | void SetGate(int panel, int X, int Y); | ||
| 16 | void SetPos(int panel, float x, float y, float z); | ||
| 17 | |||
| 18 | PuzzleSerializer _serializer; | ||
| 19 | }; | ||
| diff --git a/Source/Randomizer2Core.cpp b/Source/Randomizer2Core.cpp deleted file mode 100644 index 867fa5a..0000000 --- a/Source/Randomizer2Core.cpp +++ /dev/null | |||
| @@ -1,203 +0,0 @@ | |||
| 1 | #include "pch.h" | ||
| 2 | #include "Randomizer2Core.h" | ||
| 3 | #include "Random.h" | ||
| 4 | |||
| 5 | std::vector<Pos> Randomizer2Core::CutEdges(const Puzzle& p, size_t numEdges) { | ||
| 6 | return CutEdgesInternal(p, 0, p.width, 0, p.height, numEdges); | ||
| 7 | } | ||
| 8 | |||
| 9 | std::vector<Pos> Randomizer2Core::CutInsideEdges(const Puzzle& p, size_t numEdges) { | ||
| 10 | return CutEdgesInternal(p, 1, p.width-1, 1, p.height-1, numEdges); | ||
| 11 | } | ||
| 12 | |||
| 13 | std::vector<Pos> Randomizer2Core::CutSymmetricalEdgePairs(const Puzzle& p, size_t numEdges) { | ||
| 14 | Puzzle copy = p; | ||
| 15 | // Prevent cuts from landing on the midline | ||
| 16 | if (p.symmetry == Puzzle::Symmetry::X) { | ||
| 17 | for (int y=0; y<p.height; y++) { | ||
| 18 | copy.grid[p.width/2][y].gap = Cell::Gap::FULL; | ||
| 19 | } | ||
| 20 | return CutEdgesInternal(copy, 0, (p.width-1)/2, 0, p.height, numEdges); | ||
| 21 | } else if (p.symmetry == Puzzle::Symmetry::Y) { | ||
| 22 | for (int x=0; x<p.width; x++) { | ||
| 23 | copy.grid[x][p.height/2].gap = Cell::Gap::FULL; | ||
| 24 | } | ||
| 25 | return CutEdgesInternal(copy, 0, p.width, 0, (p.height-1)/2, numEdges); | ||
| 26 | } else { | ||
| 27 | assert(p.symmetry == Puzzle::Symmetry::XY); | ||
| 28 | int midX = p.width/2; | ||
| 29 | int midY = p.height/2; | ||
| 30 | if (p.width%4 == 1 && p.height%4 == 1) { // For double-even grids, cut around the center | ||
| 31 | copy.grid[midX-1][midY].gap = Cell::Gap::FULL; | ||
| 32 | copy.grid[midX][midY-1].gap = Cell::Gap::FULL; | ||
| 33 | copy.grid[midX][midY+1].gap = Cell::Gap::FULL; | ||
| 34 | copy.grid[midX+1][midY].gap = Cell::Gap::FULL; | ||
| 35 | } else if (p.width%4 == 1 && p.height%4 == 3) { // For half-even grids, there's only one line to cut | ||
| 36 | copy.grid[midX][midY].gap = Cell::Gap::FULL; | ||
| 37 | } else if (p.width%4 == 3 && p.height%4 == 1) { // For half-even grids, there's only one line to cut | ||
| 38 | copy.grid[midX][midY].gap = Cell::Gap::FULL; | ||
| 39 | } | ||
| 40 | return CutEdgesInternal(copy, 0, p.width, 0, p.height, numEdges); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | std::vector<Pos> Randomizer2Core::CutEdgesInternal(const Puzzle& p, int xMin, int xMax, int yMin, int yMax, size_t numEdges) { | ||
| 45 | std::vector<Pos> edges; | ||
| 46 | for (int x=xMin; x<xMax; x++) { | ||
| 47 | for (int y=yMin; y<yMax; y++) { | ||
| 48 | if (x%2 == y%2) continue; | ||
| 49 | if (p.grid[x][y].gap != Cell::Gap::NONE) continue; | ||
| 50 | if (p.grid[x][y].start) continue; | ||
| 51 | if (p.grid[x][y].end != Cell::Dir::NONE) continue; | ||
| 52 | |||
| 53 | if (p.symmetry == Puzzle::Symmetry::XY) { | ||
| 54 | assert(p.width == p.height); // TODO: This solution only supports square rotational symmetry. | ||
| 55 | if (x > y) continue; // Only allow cuts bottom-left of the diagonal | ||
| 56 | } | ||
| 57 | |||
| 58 | // If the puzzle already has a sequence, don't cut along it. | ||
| 59 | bool inSequence = false; | ||
| 60 | for (Pos pos : p.sequence) inSequence |= (pos.x == x && pos.y == y); | ||
| 61 | if (inSequence) continue; | ||
| 62 | edges.emplace_back(x, y); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | assert(numEdges <= edges.size()); | ||
| 66 | |||
| 67 | auto [colorGrid, numColors] = CreateColorGrid(p); | ||
| 68 | assert(numEdges <= numColors); | ||
| 69 | |||
| 70 | // @Hack... sort of. I couldn't think of a better way to do this. | ||
| 71 | if (p.symmetry == Puzzle::Symmetry::XY) { | ||
| 72 | // Recolor the diagonal so that opposite cells share a color. This is because we're only cutting along half their edges, | ||
| 73 | // so they are in fact two sides of the same cell. | ||
| 74 | for (int x=1; x<p.width/2; x+=2) { | ||
| 75 | assert(p.width == p.height); // TODO: This solution only supports square rotational symmetry. | ||
| 76 | colorGrid[x][x] = colorGrid[p.width-x-1][p.width-x-1]; | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | std::vector<Pos> cutEdges; | ||
| 81 | for (int i=0; i<numEdges; i++) { | ||
| 82 | while (edges.size() > 0) { | ||
| 83 | int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1)); | ||
| 84 | Pos pos = edges[edge]; | ||
| 85 | edges.erase(edges.begin() + edge); | ||
| 86 | |||
| 87 | int color1 = 0; | ||
| 88 | int color2 = 0; | ||
| 89 | if (pos.x%2 == 0 && pos.y%2 == 1) { // Vertical | ||
| 90 | if (pos.x > 0) color1 = colorGrid[pos.x-1][pos.y]; | ||
| 91 | else color1 = 1; | ||
| 92 | |||
| 93 | if (pos.x < p.width - 1) color2 = colorGrid[pos.x+1][pos.y]; | ||
| 94 | else color2 = 1; | ||
| 95 | } else { // Horizontal | ||
| 96 | assert(pos.x%2 == 1 && pos.y%2 == 0); | ||
| 97 | if (pos.y > 0) color1 = colorGrid[pos.x][pos.y-1]; | ||
| 98 | else color1 = 1; | ||
| 99 | |||
| 100 | if (pos.y < p.height - 1) color2 = colorGrid[pos.x][pos.y+1]; | ||
| 101 | else color2 = 1; | ||
| 102 | } | ||
| 103 | // Enforce color1 < color2 | ||
| 104 | if (color1 > color2) std::swap(color1, color2); | ||
| 105 | |||
| 106 | // Colors mismatch, valid cut | ||
| 107 | if (color1 != color2) { | ||
| 108 | // @Performance... have a lookup table instead? | ||
| 109 | for (int x=0; x<p.width; x++) { | ||
| 110 | for (int y=0; y<p.height; y++) { | ||
| 111 | if (colorGrid[x][y] == color2) colorGrid[x][y] = color1; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | cutEdges.emplace_back(pos); | ||
| 115 | break; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | } | ||
| 119 | assert(cutEdges.size() == numEdges); | ||
| 120 | return cutEdges; | ||
| 121 | } | ||
| 122 | |||
| 123 | #ifndef NDEBUG | ||
| 124 | #include <Windows.h> | ||
| 125 | #endif | ||
| 126 | |||
| 127 | void Randomizer2Core::DebugColorGrid(const std::vector<std::vector<int>>& colorGrid) { | ||
| 128 | #ifndef NDEBUG | ||
| 129 | static std::string colors = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||
| 130 | for (int y=0; y<colorGrid[0].size(); y++) { | ||
| 131 | std::string row; | ||
| 132 | for (int x=0; x<colorGrid.size(); x++) { | ||
| 133 | row += colors[colorGrid[x][y]]; | ||
| 134 | } | ||
| 135 | row += "\n"; | ||
| 136 | OutputDebugStringA(row.c_str()); | ||
| 137 | } | ||
| 138 | OutputDebugStringA("\n"); | ||
| 139 | #endif | ||
| 140 | } | ||
| 141 | |||
| 142 | void Randomizer2Core::FloodFill(const Puzzle& p, std::vector<std::vector<int>>& colorGrid, int color, int x, int y) { | ||
| 143 | if (!p.SafeCell(x, y)) return; | ||
| 144 | if (colorGrid[x][y] != 0) return; // Already processed. | ||
| 145 | colorGrid[x][y] = color; | ||
| 146 | |||
| 147 | FloodFill(p, colorGrid, color, x, y+1); | ||
| 148 | FloodFill(p, colorGrid, color, x, y-1); | ||
| 149 | FloodFill(p, colorGrid, color, x+1, y); | ||
| 150 | FloodFill(p, colorGrid, color, x-1, y); | ||
| 151 | } | ||
| 152 | |||
| 153 | void Randomizer2Core::FloodFillOutside(const Puzzle& p, std::vector<std::vector<int>>& colorGrid, int x, int y) { | ||
| 154 | if (!p.SafeCell(x, y)) return; | ||
| 155 | if (colorGrid[x][y] != 0) return; // Already processed. | ||
| 156 | if (x%2 != y%2 && p.grid[x][y].gap == Cell::Gap::NONE) return; // Only flood-fill through gaps | ||
| 157 | colorGrid[x][y] = 1; // Outside color | ||
| 158 | |||
| 159 | FloodFillOutside(p, colorGrid, x, y+1); | ||
| 160 | FloodFillOutside(p, colorGrid, x, y-1); | ||
| 161 | FloodFillOutside(p, colorGrid, x+1, y); | ||
| 162 | FloodFillOutside(p, colorGrid, x-1, y); | ||
| 163 | } | ||
| 164 | |||
| 165 | // Color key: | ||
| 166 | // 0 (default): Uncolored | ||
| 167 | // 1: Outside color and separator color | ||
| 168 | // 2+: Flood-filled region color | ||
| 169 | std::tuple<std::vector<std::vector<int>>, int> Randomizer2Core::CreateColorGrid(const Puzzle& p) { | ||
| 170 | std::vector<std::vector<int>> colorGrid; | ||
| 171 | colorGrid.resize(p.width); | ||
| 172 | |||
| 173 | for (int x=0; x<p.width; x++) { | ||
| 174 | colorGrid[x].resize(p.height); | ||
| 175 | for (int y=0; y<p.height; y++) { | ||
| 176 | if (x%2 == 1 && y%2 == 1) continue; | ||
| 177 | // Mark all unbroken edges and intersections as 'do not color' | ||
| 178 | if (p.grid[x][y].gap == Cell::Gap::NONE) colorGrid[x][y] = 1; | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | // @Future: Skip this loop if pillar = true; | ||
| 183 | for (int y=0; y<p.height; y++) { | ||
| 184 | FloodFillOutside(p, colorGrid, 0, y); | ||
| 185 | FloodFillOutside(p, colorGrid, p.width - 1, y); | ||
| 186 | } | ||
| 187 | |||
| 188 | for (int x=0; x<p.width; x++) { | ||
| 189 | FloodFillOutside(p, colorGrid, x, 0); | ||
| 190 | FloodFillOutside(p, colorGrid, x, p.height - 1); | ||
| 191 | } | ||
| 192 | |||
| 193 | int color = 1; | ||
| 194 | for (int x=0; x<p.width; x++) { | ||
| 195 | for (int y=0; y<p.height; y++) { | ||
| 196 | if (colorGrid[x][y] != 0) continue; // No dead colors | ||
| 197 | color++; | ||
| 198 | FloodFill(p, colorGrid, color, x, y); | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | return {colorGrid, color}; | ||
| 203 | } \ No newline at end of file | ||
| diff --git a/Source/Randomizer2Core.h b/Source/Randomizer2Core.h deleted file mode 100644 index df98de8..0000000 --- a/Source/Randomizer2Core.h +++ /dev/null | |||
| @@ -1,17 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | class Randomizer2Core { | ||
| 4 | public: | ||
| 5 | // CAUTION: These do not actually cut edges, they just returns a list of suggested cuts. | ||
| 6 | static std::vector<Pos> CutEdges(const Puzzle& p, size_t numEdges); | ||
| 7 | static std::vector<Pos> CutInsideEdges(const Puzzle& p, size_t numEdges); | ||
| 8 | static std::vector<Pos> CutSymmetricalEdgePairs(const Puzzle& p, size_t numEdges); | ||
| 9 | |||
| 10 | private: | ||
| 11 | static std::vector<Pos> CutEdgesInternal(const Puzzle& p, int xMin, int xMax, int yMin, int yMax, size_t numEdges); | ||
| 12 | static void DebugColorGrid(const std::vector<std::vector<int>>& colorGrid); | ||
| 13 | static void FloodFill(const Puzzle& p, std::vector<std::vector<int>>& colorGrid, int color, int x, int y); | ||
| 14 | static void FloodFillOutside(const Puzzle& p, std::vector<std::vector<int>>& colorGrid, int x, int y); | ||
| 15 | static std::tuple<std::vector<std::vector<int>>, int> CreateColorGrid(const Puzzle& p); | ||
| 16 | }; | ||
| 17 | |||
| diff --git a/Source/Solver.cpp b/Source/Solver.cpp deleted file mode 100644 index 74fa099..0000000 --- a/Source/Solver.cpp +++ /dev/null | |||
| @@ -1,76 +0,0 @@ | |||
| 1 | #include "pch.h" | ||
| 2 | #include "Solver.h" | ||
| 3 | #include "Validator.h" | ||
| 4 | |||
| 5 | int Solver::MAX_SOLUTIONS = 10000; | ||
| 6 | |||
| 7 | std::vector<Puzzle> Solver::Solve(Puzzle& p) { | ||
| 8 | std::vector<Puzzle> solutions; | ||
| 9 | // var start = (new Date()).getTime() | ||
| 10 | for (int x = 0; x < p.width; x++) { | ||
| 11 | for (int y = 0; y < p.height; y++) { | ||
| 12 | Cell cell = p.grid[x][y]; | ||
| 13 | if (cell.start) { | ||
| 14 | SolveLoop(p, x, y, solutions); | ||
| 15 | } | ||
| 16 | } | ||
| 17 | } | ||
| 18 | // var end = (new Date()).getTime() | ||
| 19 | // console.info('Solved', puzzle, 'in', (end-start)/1000, 'seconds') | ||
| 20 | return solutions; | ||
| 21 | } | ||
| 22 | |||
| 23 | void Solver::SolveLoop(Puzzle& p, int x, int y, std::vector<Puzzle>& solutions) { | ||
| 24 | // Stop trying to solve once we reach our goal | ||
| 25 | if (solutions.size() >= MAX_SOLUTIONS) return; | ||
| 26 | Cell cell = p.GetCell(x, y); | ||
| 27 | if (cell.undefined) return; | ||
| 28 | if (cell.gap != Cell::Gap::NONE) return; | ||
| 29 | |||
| 30 | if (p.symmetry == Puzzle::Symmetry::NONE) { | ||
| 31 | if (cell.color != Cell::Color::NONE) return; // Collided with ourselves | ||
| 32 | p.grid[x][y].color = Cell::Color::BLACK; // Otherwise, mark this cell as visited | ||
| 33 | } else { | ||
| 34 | // Get the symmetrical position, and try coloring it | ||
| 35 | auto sym = p.GetSymmetricalPos(x, y); | ||
| 36 | Cell::Color oldColor = p.GetLine(sym.x, sym.y); | ||
| 37 | p.grid[sym.x][sym.y].color = Cell::Color::YELLOW; | ||
| 38 | |||
| 39 | // Collided with ourselves or our reflection | ||
| 40 | if (cell.color != Cell::Color::NONE) { | ||
| 41 | p.grid[sym.x][sym.y].color = oldColor; | ||
| 42 | return; | ||
| 43 | } | ||
| 44 | p.grid[x][y].color = Cell::Color::BLUE; // Otherwise, mark this cell as visited | ||
| 45 | } | ||
| 46 | p.sequence.emplace_back(x, y); | ||
| 47 | |||
| 48 | if (cell.end != Cell::Dir::NONE) { | ||
| 49 | // Reached an endpoint, validate solution and keep going -- there may be other endpoints | ||
| 50 | Validator::Validate(p); | ||
| 51 | if (p.valid) { | ||
| 52 | Puzzle clone = p; | ||
| 53 | solutions.push_back(clone); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | // Recursion order (LRUD) is optimized for BL->TR and mid-start puzzles | ||
| 58 | // Extend path left and right | ||
| 59 | if (y % 2 == 0) { | ||
| 60 | SolveLoop(p, x - 1, y, solutions); | ||
| 61 | SolveLoop(p, x + 1, y, solutions); | ||
| 62 | } | ||
| 63 | // Extend path up and down | ||
| 64 | if (x % 2 == 0) { | ||
| 65 | SolveLoop(p, x, y - 1, solutions); | ||
| 66 | SolveLoop(p, x, y + 1, solutions); | ||
| 67 | } | ||
| 68 | |||
| 69 | // Tail recursion: Back out of this cell | ||
| 70 | p.grid[x][y].color = Cell::Color::NONE; | ||
| 71 | p.sequence.pop_back(); | ||
| 72 | if (p.symmetry != Puzzle::Symmetry::NONE) { | ||
| 73 | auto sym = p.GetSymmetricalPos(x, y); | ||
| 74 | p.grid[sym.x][sym.y].color = Cell::Color::NONE; | ||
| 75 | } | ||
| 76 | } | ||
| diff --git a/Source/Solver.h b/Source/Solver.h deleted file mode 100644 index 455d1eb..0000000 --- a/Source/Solver.h +++ /dev/null | |||
| @@ -1,13 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | #include <vector> | ||
| 3 | |||
| 4 | class Puzzle; | ||
| 5 | class Solver { | ||
| 6 | public: | ||
| 7 | static int MAX_SOLUTIONS; | ||
| 8 | static std::vector<Puzzle> Solve(Puzzle& p); | ||
| 9 | |||
| 10 | private: | ||
| 11 | static void SolveLoop(Puzzle& p, int x, int y, std::vector<Puzzle>& solutions); | ||
| 12 | }; | ||
| 13 | |||
| diff --git a/Source/Source.vcxproj b/Source/Source.vcxproj index 1cfb484..7d6b20a 100644 --- a/Source/Source.vcxproj +++ b/Source/Source.vcxproj | |||
| @@ -161,7 +161,6 @@ | |||
| 161 | </Link> | 161 | </Link> |
| 162 | </ItemDefinitionGroup> | 162 | </ItemDefinitionGroup> |
| 163 | <ItemGroup> | 163 | <ItemGroup> |
| 164 | <ClInclude Include="ChallengeRandomizer.h" /> | ||
| 165 | <ClInclude Include="json.hpp" /> | 164 | <ClInclude Include="json.hpp" /> |
| 166 | <ClInclude Include="Memory.h" /> | 165 | <ClInclude Include="Memory.h" /> |
| 167 | <ClInclude Include="MemoryException.h" /> | 166 | <ClInclude Include="MemoryException.h" /> |
| @@ -172,13 +171,8 @@ | |||
| 172 | <ClInclude Include="PuzzleSerializer.h" /> | 171 | <ClInclude Include="PuzzleSerializer.h" /> |
| 173 | <ClInclude Include="Random.h" /> | 172 | <ClInclude Include="Random.h" /> |
| 174 | <ClInclude Include="Randomizer.h" /> | 173 | <ClInclude Include="Randomizer.h" /> |
| 175 | <ClInclude Include="Randomizer2.h" /> | ||
| 176 | <ClInclude Include="Randomizer2Core.h" /> | ||
| 177 | <ClInclude Include="Solver.h" /> | ||
| 178 | <ClInclude Include="Validator.h" /> | ||
| 179 | </ItemGroup> | 174 | </ItemGroup> |
| 180 | <ItemGroup> | 175 | <ItemGroup> |
| 181 | <ClCompile Include="ChallengeRandomizer.cpp" /> | ||
| 182 | <ClCompile Include="Memory.cpp" /> | 176 | <ClCompile Include="Memory.cpp" /> |
| 183 | <ClCompile Include="pch.cpp"> | 177 | <ClCompile Include="pch.cpp"> |
| 184 | <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader> | 178 | <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader> |
| @@ -190,10 +184,6 @@ | |||
| 190 | <ClCompile Include="PuzzleSerializer.cpp" /> | 184 | <ClCompile Include="PuzzleSerializer.cpp" /> |
| 191 | <ClCompile Include="Random.cpp" /> | 185 | <ClCompile Include="Random.cpp" /> |
| 192 | <ClCompile Include="Randomizer.cpp" /> | 186 | <ClCompile Include="Randomizer.cpp" /> |
| 193 | <ClCompile Include="Randomizer2.cpp" /> | ||
| 194 | <ClCompile Include="Randomizer2Core.cpp" /> | ||
| 195 | <ClCompile Include="Solver.cpp" /> | ||
| 196 | <ClCompile Include="Validator.cpp" /> | ||
| 197 | </ItemGroup> | 187 | </ItemGroup> |
| 198 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | 188 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> |
| 199 | <ImportGroup Label="ExtensionTargets"> | 189 | <ImportGroup Label="ExtensionTargets"> |
| diff --git a/Source/Validator.cpp b/Source/Validator.cpp deleted file mode 100644 index 3b4020b..0000000 --- a/Source/Validator.cpp +++ /dev/null | |||
| @@ -1,97 +0,0 @@ | |||
| 1 | #include "pch.h" | ||
| 2 | #include "Validator.h" | ||
| 3 | |||
| 4 | void Validator::Validate(Puzzle& p) { | ||
| 5 | // console.log('Validating', puzzle); | ||
| 6 | p.valid = true; // Assume valid until we find an invalid element | ||
| 7 | p.invalidElements.clear(); | ||
| 8 | p.negations.clear(); | ||
| 9 | |||
| 10 | bool puzzleHasSymbols = false; | ||
| 11 | bool puzzleHasStart = false; | ||
| 12 | bool puzzleHasEnd = false; | ||
| 13 | // Validate gap failures as an early exit. | ||
| 14 | for (int x = 0; x < p.width; x++) { | ||
| 15 | for (int y = 0; y < p.height; y++) { | ||
| 16 | const Cell& cell = p.grid[x][y]; | ||
| 17 | const auto& decoration = cell.decoration; | ||
| 18 | if (decoration) { | ||
| 19 | if (decoration->type == Type::Stone || | ||
| 20 | decoration->type == Type::Star || | ||
| 21 | decoration->type == Type::Nega || | ||
| 22 | decoration->type == Type::Poly || | ||
| 23 | decoration->type == Type::Ylop) { | ||
| 24 | puzzleHasSymbols = true; | ||
| 25 | continue; | ||
| 26 | } | ||
| 27 | if (decoration->type == Type::Triangle) { | ||
| 28 | int actualCount = 0; | ||
| 29 | if (p.GetLine(x - 1, y) != Cell::Color::NONE) actualCount++; | ||
| 30 | if (p.GetLine(x + 1, y) != Cell::Color::NONE) actualCount++; | ||
| 31 | if (p.GetLine(x, y - 1) != Cell::Color::NONE) actualCount++; | ||
| 32 | if (p.GetLine(x, y + 1) != Cell::Color::NONE) actualCount++; | ||
| 33 | if (decoration->count != actualCount) { | ||
| 34 | // console.log('Triangle at grid['+x+']['+y+'] has', actualCount, 'borders') | ||
| 35 | p.invalidElements.emplace_back(x, y); | ||
| 36 | } | ||
| 37 | } | ||
| 38 | } | ||
| 39 | if (cell.gap != Cell::Gap::NONE && cell.color != Cell::Color::NONE) { | ||
| 40 | // console.log('Gap at', x, y, 'is covered') | ||
| 41 | p.valid = false; | ||
| 42 | } | ||
| 43 | if (cell.dot != Cell::Dot::NONE) { | ||
| 44 | if (cell.color == Cell::Color::NONE) { | ||
| 45 | // console.log('Dot at', x, y, 'is not covered') | ||
| 46 | p.invalidElements.emplace_back(x, y); | ||
| 47 | } else if (cell.color == Cell::Color::BLUE && cell.dot == Cell::Dot::YELLOW) { | ||
| 48 | // console.log('Yellow dot at', x, y, 'is covered by blue line') | ||
| 49 | p.valid = false; | ||
| 50 | } else if (cell.color == Cell::Color::YELLOW && cell.dot == Cell::Dot::BLUE) { | ||
| 51 | // console.log('Blue dot at', x, y, 'is covered by yellow line') | ||
| 52 | p.valid = false; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | if (cell.color != Cell::Color::NONE) { | ||
| 56 | if (cell.start == true) puzzleHasStart = true; | ||
| 57 | if (cell.end != Cell::Dir::NONE) puzzleHasEnd = true; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
| 61 | if (!puzzleHasStart || !puzzleHasEnd) { | ||
| 62 | // console.log('There is no covered start or endpoint') | ||
| 63 | p.valid = false; | ||
| 64 | } | ||
| 65 | |||
| 66 | // Perf optimization: We can skip computing regions if the grid has no symbols. | ||
| 67 | if (!puzzleHasSymbols) { // No additional symbols, and we already checked dots & gaps | ||
| 68 | p.valid &= (p.invalidElements.size() == 0); | ||
| 69 | } else { // Additional symbols, so we need to discard dots & divide them by region | ||
| 70 | /* | ||
| 71 | p.invalidElements.clear(); | ||
| 72 | std::vector<Region> regions = p.GetRegions(); | ||
| 73 | // console.log('Found', regions.length, 'regions'); | ||
| 74 | // console.debug(regions); | ||
| 75 | |||
| 76 | for (const Region& region : regions) { | ||
| 77 | std::string key = region.grid.ToString(); | ||
| 78 | auto regionData = puzzle.regionCache[key]; | ||
| 79 | if (regionData == undefined) { | ||
| 80 | console.log('Cache miss for region', region, 'key', key); | ||
| 81 | regionData = _regionCheckNegations(puzzle, region); | ||
| 82 | // Entirely for convenience | ||
| 83 | regionData.valid = (regionData.invalidElements.size() == 0) | ||
| 84 | // console.log('Region valid:', regionData.valid); | ||
| 85 | |||
| 86 | if (!DISABLE_CACHE) { | ||
| 87 | p.regionCache[key] = regionData; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | p.negations = p.negations.concat(regionData.negations); | ||
| 91 | p.invalidElements = p.invalidElements.concat(regionData.invalidElements); | ||
| 92 | p.valid = p.valid && regionData.valid; | ||
| 93 | } | ||
| 94 | */ | ||
| 95 | } | ||
| 96 | // console.log('Puzzle has', puzzle.invalidElements.length, 'invalid elements') | ||
| 97 | } | ||
| diff --git a/Source/Validator.h b/Source/Validator.h deleted file mode 100644 index 2a102dd..0000000 --- a/Source/Validator.h +++ /dev/null | |||
| @@ -1,27 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | #include <vector> | ||
| 3 | #include <tuple> | ||
| 4 | |||
| 5 | #ifndef NEGATIONS_CANCEL_NEGATIONS | ||
| 6 | #define NEGATIONS_CANCEL_NEGATIONS true | ||
| 7 | #endif | ||
| 8 | |||
| 9 | #ifndef SIMPLE_POLYOMINOS | ||
| 10 | #define SIMPLE_POLYOMINOS true | ||
| 11 | #endif | ||
| 12 | |||
| 13 | #ifndef DISABLE_CACHE | ||
| 14 | #define DISABLE_CACHE false | ||
| 15 | #endif | ||
| 16 | |||
| 17 | struct Region{}; | ||
| 18 | class Puzzle; | ||
| 19 | struct Pos; | ||
| 20 | class Validator { | ||
| 21 | public: | ||
| 22 | static void Validate(Puzzle& p); | ||
| 23 | |||
| 24 | private: | ||
| 25 | static void RegionCheckNegations(Puzzle& p, const Region& r); | ||
| 26 | static std::vector<Pos> RegionCheck(Puzzle& p, const Region& r); | ||
| 27 | }; | ||
