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 | }; | ||