diff options
-rw-r--r-- | App/App.vcxproj | 2 | ||||
-rw-r--r-- | App/Main.cpp | 180 | ||||
-rw-r--r-- | App/Version.h | 10 | ||||
-rw-r--r-- | README.md | 20 | ||||
-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 | ||||
-rw-r--r-- | WitnessRandomizer.sln | 25 |
21 files changed, 369 insertions, 1779 deletions
diff --git a/App/App.vcxproj b/App/App.vcxproj index e2e75a8..14d1d9d 100644 --- a/App/App.vcxproj +++ b/App/App.vcxproj | |||
@@ -84,7 +84,7 @@ | |||
84 | </PropertyGroup> | 84 | </PropertyGroup> |
85 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | 85 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> |
86 | <LinkIncremental>false</LinkIncremental> | 86 | <LinkIncremental>false</LinkIncremental> |
87 | <TargetName>WitnessRandomizer</TargetName> | 87 | <TargetName>WitnessTutorialiser</TargetName> |
88 | <OutDir>..\GithubPackage\</OutDir> | 88 | <OutDir>..\GithubPackage\</OutDir> |
89 | </PropertyGroup> | 89 | </PropertyGroup> |
90 | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | 90 | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> |
diff --git a/App/Main.cpp b/App/Main.cpp index 10aac89..f0f770a 100644 --- a/App/Main.cpp +++ b/App/Main.cpp | |||
@@ -5,28 +5,18 @@ | |||
5 | #include "Memory.h" | 5 | #include "Memory.h" |
6 | #include "Random.h" | 6 | #include "Random.h" |
7 | #include "Randomizer.h" | 7 | #include "Randomizer.h" |
8 | #include "Randomizer2.h" | ||
9 | #include "Panels_.h" | 8 | #include "Panels_.h" |
10 | 9 | ||
11 | #define HEARTBEAT 0x401 | 10 | #define HEARTBEAT 0x401 |
12 | #define RANDOMIZE_READY 0x402 | 11 | #define RANDOMIZE_READY 0x402 |
13 | #define RANDOMIZING 0403 | 12 | #define RANDOMIZING 0403 |
14 | #define RANDOMIZE_DONE 0x404 | 13 | #define RANDOMIZE_DONE 0x404 |
15 | #define RANDOMIZE_CHALLENGE_DONE 0x405 | ||
16 | #define CHALLENGE_ONLY 0x406 | ||
17 | #define DISABLE_SNIPES 0x407 | ||
18 | #define SPEED_UP_AUTOSCROLLERS 0x408 | ||
19 | 14 | ||
20 | /* ------- Temp ------- */ | 15 | /* ------- Temp ------- */ |
21 | #include "Solver.h" | ||
22 | #include "PuzzleSerializer.h" | 16 | #include "PuzzleSerializer.h" |
23 | #include <sstream> | 17 | #include <sstream> |
24 | #include <iomanip> | 18 | #include <iomanip> |
25 | 19 | ||
26 | #define TMP1 0x501 | ||
27 | #define TMP2 0x502 | ||
28 | #define TMP3 0x503 | ||
29 | #define TMP4 0x504 | ||
30 | 20 | ||
31 | HWND g_panelId; | 21 | HWND g_panelId; |
32 | Puzzle g_puzzle; | 22 | Puzzle g_puzzle; |
@@ -37,13 +27,11 @@ HWND g_rngDebug; | |||
37 | 27 | ||
38 | // Globals | 28 | // Globals |
39 | HWND g_hwnd; | 29 | HWND g_hwnd; |
40 | HWND g_seed; | 30 | //HWND g_seed; |
41 | HWND g_randomizerStatus; | 31 | HWND g_randomizerStatus; |
42 | HINSTANCE g_hInstance; | 32 | HINSTANCE g_hInstance; |
43 | auto g_witnessProc = std::make_shared<Memory>(L"witness64_d3d11.exe"); | 33 | auto g_witnessProc = std::make_shared<Memory>(L"witness64_d3d11.exe"); |
44 | std::shared_ptr<Randomizer> g_randomizer; | 34 | std::shared_ptr<Randomizer> g_randomizer; |
45 | std::shared_ptr<Randomizer2> g_randomizer2; | ||
46 | void SetRandomSeed(); | ||
47 | 35 | ||
48 | LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { | 36 | LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { |
49 | if (message == WM_DESTROY) { | 37 | if (message == WM_DESTROY) { |
@@ -64,171 +52,57 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |||
64 | // Shut down randomizer, wait for startup | 52 | // Shut down randomizer, wait for startup |
65 | if (g_randomizer) { | 53 | if (g_randomizer) { |
66 | g_randomizer = nullptr; | 54 | g_randomizer = nullptr; |
67 | g_randomizer2 = nullptr; | ||
68 | EnableWindow(g_randomizerStatus, FALSE); | 55 | EnableWindow(g_randomizerStatus, FALSE); |
69 | } | 56 | } |
70 | break; | 57 | break; |
71 | case ProcStatus::Running: | 58 | case ProcStatus::Running: |
72 | if (!g_randomizer) { | 59 | if (!g_randomizer) { |
73 | g_randomizer = std::make_shared<Randomizer>(g_witnessProc); | 60 | g_randomizer = std::make_shared<Randomizer>(g_witnessProc); |
74 | g_randomizer2 = std::make_shared<Randomizer2>(g_witnessProc); | ||
75 | PostMessage(g_hwnd, WM_COMMAND, RANDOMIZE_READY, NULL); | 61 | PostMessage(g_hwnd, WM_COMMAND, RANDOMIZE_READY, NULL); |
76 | } | 62 | } |
77 | break; | 63 | break; |
78 | case ProcStatus::NewGame: // This status will fire only once per new game | 64 | case ProcStatus::NewGame: // This status will fire only once per new game |
79 | SetWindowText(g_seed, L""); | 65 | //SetWindowText(g_seed, L""); |
80 | PostMessage(g_hwnd, WM_COMMAND, RANDOMIZE_READY, NULL); | 66 | PostMessage(g_hwnd, WM_COMMAND, RANDOMIZE_READY, NULL); |
81 | break; | 67 | break; |
82 | } | 68 | } |
83 | break; | 69 | break; |
84 | case RANDOMIZE_READY: | 70 | case RANDOMIZE_READY: |
85 | EnableWindow(g_randomizerStatus, TRUE); | 71 | EnableWindow(g_randomizerStatus, TRUE); |
86 | if (IsDlgButtonChecked(hwnd, CHALLENGE_ONLY)) { | 72 | SetWindowText(g_randomizerStatus, L"Tutorialise"); |
87 | SetWindowText(g_randomizerStatus, L"Randomize Challenge"); | ||
88 | } else { | ||
89 | SetWindowText(g_randomizerStatus, L"Randomize"); | ||
90 | } | ||
91 | break; | 73 | break; |
92 | case RANDOMIZING: | 74 | case RANDOMIZING: |
93 | if (!g_randomizer) break; // E.g. an enter press at the wrong time | 75 | if (!g_randomizer) break; // E.g. an enter press at the wrong time |
94 | EnableWindow(g_randomizerStatus, FALSE); | 76 | EnableWindow(g_randomizerStatus, FALSE); |
95 | 77 | ||
96 | SetRandomSeed(); | ||
97 | std::thread([]{ | 78 | std::thread([]{ |
98 | if (IsDlgButtonChecked(g_hwnd, DISABLE_SNIPES)) { | 79 | SetWindowText(g_randomizerStatus, L"Tutorialising..."); |
99 | MEMORY_CATCH(g_randomizer->PreventSnipes()); | 80 | g_randomizer->Randomize(); |
100 | } | 81 | PostMessage(g_hwnd, WM_COMMAND, RANDOMIZE_DONE, NULL); |
101 | if (IsDlgButtonChecked(g_hwnd, SPEED_UP_AUTOSCROLLERS)) { | ||
102 | MEMORY_CATCH(g_randomizer->AdjustSpeed()); | ||
103 | } | ||
104 | if (IsDlgButtonChecked(g_hwnd, CHALLENGE_ONLY)) { | ||
105 | SetWindowText(g_randomizerStatus, L"Randomizing Challenge..."); | ||
106 | MEMORY_CATCH(g_randomizer->RandomizeChallenge()); | ||
107 | PostMessage(g_hwnd, WM_COMMAND, RANDOMIZE_CHALLENGE_DONE, NULL); | ||
108 | } else { | ||
109 | SetWindowText(g_randomizerStatus, L"Randomizing..."); | ||
110 | MEMORY_CATCH(g_randomizer->Randomize()); | ||
111 | PostMessage(g_hwnd, WM_COMMAND, RANDOMIZE_DONE, NULL); | ||
112 | } | ||
113 | }).detach(); | 82 | }).detach(); |
114 | break; | 83 | break; |
115 | case RANDOMIZE_DONE: | 84 | case RANDOMIZE_DONE: |
116 | EnableWindow(g_randomizerStatus, FALSE); | 85 | EnableWindow(g_randomizerStatus, FALSE); |
117 | SetWindowText(g_randomizerStatus, L"Randomized!"); | 86 | SetWindowText(g_randomizerStatus, L"Tutorialised!"); |
118 | break; | ||
119 | case RANDOMIZE_CHALLENGE_DONE: | ||
120 | EnableWindow(g_randomizerStatus, FALSE); | ||
121 | SetWindowText(g_randomizerStatus, L"Randomized Challenge!"); | ||
122 | std::thread([]{ | ||
123 | // Allow re-randomization for challenge -- it doesn't break the rest of the game. | ||
124 | std::this_thread::sleep_for(std::chrono::seconds(10)); | ||
125 | PostMessage(g_hwnd, WM_COMMAND, RANDOMIZE_READY, NULL); | ||
126 | }).detach(); | ||
127 | break; | ||
128 | case CHALLENGE_ONLY: | ||
129 | CheckDlgButton(hwnd, CHALLENGE_ONLY, !IsDlgButtonChecked(hwnd, CHALLENGE_ONLY)); | ||
130 | if (IsWindowEnabled(g_randomizerStatus)) { | ||
131 | PostMessage(g_hwnd, WM_COMMAND, RANDOMIZE_READY, NULL); | ||
132 | } | ||
133 | break; | ||
134 | case DISABLE_SNIPES: | ||
135 | CheckDlgButton(hwnd, DISABLE_SNIPES, !IsDlgButtonChecked(hwnd, DISABLE_SNIPES)); | ||
136 | break; | ||
137 | case SPEED_UP_AUTOSCROLLERS: | ||
138 | CheckDlgButton(hwnd, SPEED_UP_AUTOSCROLLERS, !IsDlgButtonChecked(hwnd, SPEED_UP_AUTOSCROLLERS)); | ||
139 | break; | ||
140 | case TMP1: | ||
141 | { | ||
142 | std::wstring text(128, L'\0'); | ||
143 | int length = GetWindowText(g_panelId, text.data(), static_cast<int>(text.size())); | ||
144 | text.resize(length); | ||
145 | std::wstringstream s; | ||
146 | int panelId; | ||
147 | s << text; | ||
148 | s >> std::hex >> panelId; | ||
149 | g_puzzle = PuzzleSerializer(g_witnessProc).ReadPuzzle(panelId); | ||
150 | } | ||
151 | break; | ||
152 | case TMP2: | ||
153 | { | ||
154 | std::wstring text(128, L'\0'); | ||
155 | int length = GetWindowText(g_panelId, text.data(), static_cast<int>(text.size())); | ||
156 | text.resize(length); | ||
157 | std::wstringstream s; | ||
158 | int panelId; | ||
159 | s << text; | ||
160 | s >> std::hex >> panelId; | ||
161 | PuzzleSerializer(g_witnessProc).WritePuzzle(g_puzzle, panelId); | ||
162 | } | ||
163 | break; | 87 | break; |
164 | case TMP3: | ||
165 | { | ||
166 | for (auto [key, value] : PANELS) { | ||
167 | std::stringstream out; | ||
168 | std::string name(value); | ||
169 | out << " {'id': 0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(5) << key << ", 'area':'"; | ||
170 | int k; | ||
171 | for (k=0; name[k] != ' '; k++) out << name[k]; | ||
172 | if (name[k+2] == ' ') { | ||
173 | out << name[k] << name[k+1]; | ||
174 | k += 2; | ||
175 | } | ||
176 | out << "', 'name':'"; | ||
177 | k++; | ||
178 | for (k; k < name.size(); k++) out << name[k]; | ||
179 | out << "', 'data':'"; | ||
180 | auto puzzle = PuzzleSerializer(g_witnessProc).ReadPuzzle(key); | ||
181 | out << puzzle.Serialize(); | ||
182 | out << "'},\r\n"; | ||
183 | DebugPrint(out.str()); | ||
184 | } | ||
185 | } | ||
186 | 88 | ||
187 | // Solver::Solve(g_puzzle); | ||
188 | break; | ||
189 | case TMP4: | ||
190 | SetRandomSeed(); | ||
191 | g_randomizer2->Randomize(); | ||
192 | case TMP5: | ||
193 | { | ||
194 | std::wstring text; | ||
195 | for (int i=0; i<10; i++) { | ||
196 | Random::SetSeed(i); | ||
197 | int rng = Random::RandInt(0, 999999); | ||
198 | text += std::to_wstring(rng) + L"\n"; | ||
199 | } | ||
200 | SetWindowText(g_rngDebug, text.c_str()); | ||
201 | } | ||
202 | } | 89 | } |
203 | } | 90 | } |
204 | return DefWindowProc(hwnd, message, wParam, lParam); | 91 | return DefWindowProc(hwnd, message, wParam, lParam); |
205 | } | 92 | } |
206 | 93 | ||
207 | void SetRandomSeed() { | ||
208 | std::wstring text(128, L'\0'); | ||
209 | int length = GetWindowText(g_seed, text.data(), static_cast<int>(text.size())); | ||
210 | if (length > 0) { // Set seed | ||
211 | text.resize(length); | ||
212 | Random::SetSeed(_wtoi(text.c_str())); | ||
213 | } else { // Random seed | ||
214 | int seed = Random::RandInt(0, 999999); | ||
215 | |||
216 | text = std::to_wstring(seed); | ||
217 | SetWindowText(g_seed, text.c_str()); | ||
218 | CHARRANGE range = {0, static_cast<long>(text.length())}; | ||
219 | PostMessage(g_seed, EM_EXSETSEL, NULL, (LPARAM)&range); | ||
220 | SetFocus(g_seed); | ||
221 | |||
222 | Random::SetSeed(seed); | ||
223 | } | ||
224 | } | ||
225 | |||
226 | HWND CreateLabel(int x, int y, int width, LPCWSTR text) { | 94 | HWND CreateLabel(int x, int y, int width, LPCWSTR text) { |
227 | return CreateWindow(L"STATIC", text, | 95 | return CreateWindow(L"STATIC", text, |
228 | WS_TABSTOP | WS_VISIBLE | WS_CHILD | SS_LEFT, | 96 | WS_TABSTOP | WS_VISIBLE | WS_CHILD | SS_LEFT, |
229 | x, y, width, 16, g_hwnd, NULL, g_hInstance, NULL); | 97 | x, y, width, 16, g_hwnd, NULL, g_hInstance, NULL); |
230 | } | 98 | } |
231 | 99 | ||
100 | HWND CreateMultiLabel(int x, int y, int width, int height, LPCWSTR text) { | ||
101 | return CreateWindow(L"STATIC", text, | ||
102 | WS_TABSTOP | WS_VISIBLE | WS_CHILD | SS_LEFT, | ||
103 | x, y, width, height, g_hwnd, NULL, g_hInstance, NULL); | ||
104 | } | ||
105 | |||
232 | HWND CreateText(int x, int y, int width, LPCWSTR defaultText = L"") { | 106 | HWND CreateText(int x, int y, int width, LPCWSTR defaultText = L"") { |
233 | return CreateWindow(MSFTEDIT_CLASS, defaultText, | 107 | return CreateWindow(MSFTEDIT_CLASS, defaultText, |
234 | WS_TABSTOP | WS_VISIBLE | WS_CHILD | WS_BORDER, | 108 | WS_TABSTOP | WS_VISIBLE | WS_CHILD | WS_BORDER, |
@@ -271,32 +145,12 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance | |||
271 | RECT rect; | 145 | RECT rect; |
272 | GetClientRect(GetDesktopWindow(), &rect); | 146 | GetClientRect(GetDesktopWindow(), &rect); |
273 | g_hwnd = CreateWindow(WINDOW_CLASS, PRODUCT_NAME, WS_OVERLAPPEDWINDOW, | 147 | g_hwnd = CreateWindow(WINDOW_CLASS, PRODUCT_NAME, WS_OVERLAPPEDWINDOW, |
274 | rect.right - 550, 200, 500, 500, nullptr, nullptr, hInstance, nullptr); | 148 | rect.right - 550, 200, 500, 180, nullptr, nullptr, hInstance, nullptr); |
275 | 149 | ||
276 | CreateLabel(390, 15, 90, L"Version: " VERSION_STR); | 150 | CreateMultiLabel(10, 10, 460, 86, L"This mod replaces most puzzles in the game with Tutorial Straight (the first puzzle in the tunnel where you start the game). Certain special panels are unaffected. Additionally, some panels (e.g. the tutorial gate, and every puzzle in Bunker) behave a little strangely now, and can be solved by simply double clicking in the middle of the panel."); |
277 | g_seed = CreateText(10, 10, 100); | 151 | CreateLabel(390, 110, 90, L"Version: " VERSION_STR); |
278 | PostMessage(g_seed, EM_SETEVENTMASK, 0, ENM_KEYEVENTS); | 152 | g_randomizerStatus = CreateButton(120, 105, 180, L"Tutorialise", RANDOMIZING); |
279 | g_randomizerStatus = CreateButton(120, 10, 180, L"Randomize", RANDOMIZING); | ||
280 | EnableWindow(g_randomizerStatus, FALSE); | 153 | EnableWindow(g_randomizerStatus, FALSE); |
281 | CreateCheckbox(10, 300, CHALLENGE_ONLY); | ||
282 | CreateLabel(30, 300, 200, L"Randomize the challenge only"); | ||
283 | CreateCheckbox(10, 320, DISABLE_SNIPES); | ||
284 | CheckDlgButton(g_hwnd, DISABLE_SNIPES, TRUE); | ||
285 | CreateLabel(30, 320, 240, L"Disable Swamp and Shadows snipes"); | ||
286 | CreateCheckbox(10, 340, SPEED_UP_AUTOSCROLLERS); | ||
287 | CreateLabel(30, 340, 205, L"Speed up various autoscrollers"); | ||
288 | |||
289 | // CreateButton(200, 50, 200, L"Test RNG", TMP5); | ||
290 | // g_rngDebug = CreateWindow(L"STATIC", L"", | ||
291 | // WS_TABSTOP | WS_VISIBLE | WS_CHILD | SS_LEFT, | ||
292 | // 200, 80, 200, 200, g_hwnd, NULL, g_hInstance, NULL); | ||
293 | #ifndef NDEBUG | ||
294 | g_panelId = CreateText(200, 100, 100, L"59"); | ||
295 | CreateButton(200, 130, 100, L"Read", TMP1); | ||
296 | CreateButton(200, 160, 100, L"Write", TMP2); | ||
297 | CreateButton(200, 190, 100, L"Solve", TMP3); | ||
298 | CreateButton(200, 220, 100, L"Randomize2", TMP4); | ||
299 | #endif | ||
300 | 154 | ||
301 | g_witnessProc->StartHeartbeat(g_hwnd, HEARTBEAT); | 155 | g_witnessProc->StartHeartbeat(g_hwnd, HEARTBEAT); |
302 | 156 | ||
diff --git a/App/Version.h b/App/Version.h index bdb584e..7236d18 100644 --- a/App/Version.h +++ b/App/Version.h | |||
@@ -3,12 +3,12 @@ | |||
3 | #define TO_STRING2(s) L#s | 3 | #define TO_STRING2(s) L#s |
4 | #define TO_STRING(s) TO_STRING2(s) | 4 | #define TO_STRING(s) TO_STRING2(s) |
5 | 5 | ||
6 | #define MAJOR 6 | 6 | #define MAJOR 0 |
7 | #define MINOR 0 | 7 | #define MINOR 4 |
8 | #define PATCH 1 | 8 | #define PATCH 0 |
9 | 9 | ||
10 | #define VERSION_STR TO_STRING(MAJOR) L"." TO_STRING(MINOR) L"." TO_STRING(PATCH) | 10 | #define VERSION_STR TO_STRING(MAJOR) L"." TO_STRING(MINOR) L"." TO_STRING(PATCH) |
11 | #define VERSION MAJOR, MINOR, PATCH | 11 | #define VERSION MAJOR, MINOR, PATCH |
12 | 12 | ||
13 | #define PRODUCT_NAME L"Witness Randomizer" | 13 | #define PRODUCT_NAME L"Witness Tutorialiser Mod" |
14 | #define WINDOW_CLASS L"WitnessRandomizer" | 14 | #define WINDOW_CLASS L"WitnessTutorialiser" |
diff --git a/README.md b/README.md new file mode 100644 index 0000000..43e4045 --- /dev/null +++ b/README.md | |||
@@ -0,0 +1,20 @@ | |||
1 | # The Witness Tutorialiser Mod | ||
2 | |||
3 | This mod replaces most puzzles in the game with Tutorial Straight (the first puzzle in the tunnel where you start the game). Certain special panels are unaffected. Additionally, some panels (e.g. the tutorial gate, and every puzzle in Bunker) behave a little strangely now, and can be solved by simply double clicking in the middle of the panel. | ||
4 | |||
5 | ## Is everything in the game still possible? | ||
6 | |||
7 | As of version 0.2.0, all 523 counting panels can be solved. This may require some creativity on your part, as certain controls (e.g. everything that moves in Quarry) no longer function. | ||
8 | |||
9 | The following environmental puzzles are impossible in this mod: | ||
10 | |||
11 | * Mill Ramp Light EP | ||
12 | * Mill Lift Light EP | ||
13 | * Quarry Elevator EP | ||
14 | * Boathouse Front Ramp EP | ||
15 | * Boathouse Back Ramp EP | ||
16 | * Swamp Underwater Sliding Bridge EP | ||
17 | * Swamp Purple Sand Outward EP | ||
18 | * Swamp Purple Sand Middle EP | ||
19 | * Swamp Purple Sand Inward EP is a special case. This puzzle is solveable at the start of the game, but can become impossible depending on your actions. The Swamp Floodgate can only be closed in this mod, so if you want to solve this EP you must do it before solving the floodgate. | ||
20 | * Swamp Floodgate EP | ||
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 | }; | ||
diff --git a/WitnessRandomizer.sln b/WitnessRandomizer.sln index ef31a4e..473f4ff 100644 --- a/WitnessRandomizer.sln +++ b/WitnessRandomizer.sln | |||
@@ -12,18 +12,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Source", "Source\Source.vcx | |||
12 | EndProject | 12 | EndProject |
13 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test\Test.vcxproj", "{128784C2-9157-4291-8FD6-44637BE162FB}" | 13 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test\Test.vcxproj", "{128784C2-9157-4291-8FD6-44637BE162FB}" |
14 | EndProject | 14 | EndProject |
15 | Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "Installer", "Installer\Installer.vdproj", "{F1B9D251-3DCC-4B19-A5C9-CA23AFD38632}" | ||
16 | ProjectSection(ProjectDependencies) = postProject | ||
17 | {6B5DF051-A51A-48CB-8ACD-C6FAD726019F} = {6B5DF051-A51A-48CB-8ACD-C6FAD726019F} | ||
18 | {5A4E859A-9756-463E-86E6-6EADBBA23021} = {5A4E859A-9756-463E-86E6-6EADBBA23021} | ||
19 | {235D27F1-9907-489B-8D58-636A0C5CD079} = {235D27F1-9907-489B-8D58-636A0C5CD079} | ||
20 | EndProjectSection | ||
21 | EndProject | ||
22 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "App2", "App2\App2.vcxproj", "{5A4E859A-9756-463E-86E6-6EADBBA23021}" | ||
23 | ProjectSection(ProjectDependencies) = postProject | ||
24 | {6B5DF051-A51A-48CB-8ACD-C6FAD726019F} = {6B5DF051-A51A-48CB-8ACD-C6FAD726019F} | ||
25 | EndProjectSection | ||
26 | EndProject | ||
27 | Global | 15 | Global |
28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution | 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution |
29 | Debug|x64 = Debug|x64 | 17 | Debug|x64 = Debug|x64 |
@@ -56,19 +44,6 @@ Global | |||
56 | {128784C2-9157-4291-8FD6-44637BE162FB}.Release|x64.Build.0 = Release|x64 | 44 | {128784C2-9157-4291-8FD6-44637BE162FB}.Release|x64.Build.0 = Release|x64 |
57 | {128784C2-9157-4291-8FD6-44637BE162FB}.Release|x86.ActiveCfg = Debug|x64 | 45 | {128784C2-9157-4291-8FD6-44637BE162FB}.Release|x86.ActiveCfg = Debug|x64 |
58 | {128784C2-9157-4291-8FD6-44637BE162FB}.Release|x86.Build.0 = Debug|x64 | 46 | {128784C2-9157-4291-8FD6-44637BE162FB}.Release|x86.Build.0 = Debug|x64 |
59 | {F1B9D251-3DCC-4B19-A5C9-CA23AFD38632}.Debug|x64.ActiveCfg = Debug | ||
60 | {F1B9D251-3DCC-4B19-A5C9-CA23AFD38632}.Debug|x64.Build.0 = Debug | ||
61 | {F1B9D251-3DCC-4B19-A5C9-CA23AFD38632}.Debug|x86.ActiveCfg = Debug | ||
62 | {F1B9D251-3DCC-4B19-A5C9-CA23AFD38632}.Release|x64.ActiveCfg = Release | ||
63 | {F1B9D251-3DCC-4B19-A5C9-CA23AFD38632}.Release|x86.ActiveCfg = Release | ||
64 | {5A4E859A-9756-463E-86E6-6EADBBA23021}.Debug|x64.ActiveCfg = Debug|x64 | ||
65 | {5A4E859A-9756-463E-86E6-6EADBBA23021}.Debug|x64.Build.0 = Debug|x64 | ||
66 | {5A4E859A-9756-463E-86E6-6EADBBA23021}.Debug|x86.ActiveCfg = Debug|Win32 | ||
67 | {5A4E859A-9756-463E-86E6-6EADBBA23021}.Debug|x86.Build.0 = Debug|Win32 | ||
68 | {5A4E859A-9756-463E-86E6-6EADBBA23021}.Release|x64.ActiveCfg = Release|x64 | ||
69 | {5A4E859A-9756-463E-86E6-6EADBBA23021}.Release|x64.Build.0 = Release|x64 | ||
70 | {5A4E859A-9756-463E-86E6-6EADBBA23021}.Release|x86.ActiveCfg = Release|Win32 | ||
71 | {5A4E859A-9756-463E-86E6-6EADBBA23021}.Release|x86.Build.0 = Release|Win32 | ||
72 | EndGlobalSection | 47 | EndGlobalSection |
73 | GlobalSection(SolutionProperties) = preSolution | 48 | GlobalSection(SolutionProperties) = preSolution |
74 | HideSolutionNode = FALSE | 49 | HideSolutionNode = FALSE |