about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--App/App.vcxproj2
-rw-r--r--App/Main.cpp180
-rw-r--r--App/Version.h10
-rw-r--r--README.md20
-rw-r--r--Source/ChallengeRandomizer.cpp108
-rw-r--r--Source/ChallengeRandomizer.h13
-rw-r--r--Source/Memory.cpp3
-rw-r--r--Source/Memory.h38
-rw-r--r--Source/Panels.h203
-rw-r--r--Source/Randomizer.cpp405
-rw-r--r--Source/Randomizer.h35
-rw-r--r--Source/Randomizer2.cpp644
-rw-r--r--Source/Randomizer2.h19
-rw-r--r--Source/Randomizer2Core.cpp203
-rw-r--r--Source/Randomizer2Core.h17
-rw-r--r--Source/Solver.cpp76
-rw-r--r--Source/Solver.h13
-rw-r--r--Source/Source.vcxproj10
-rw-r--r--Source/Validator.cpp97
-rw-r--r--Source/Validator.h27
-rw-r--r--WitnessRandomizer.sln25
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
31HWND g_panelId; 21HWND g_panelId;
32Puzzle g_puzzle; 22Puzzle g_puzzle;
@@ -37,13 +27,11 @@ HWND g_rngDebug;
37 27
38// Globals 28// Globals
39HWND g_hwnd; 29HWND g_hwnd;
40HWND g_seed; 30//HWND g_seed;
41HWND g_randomizerStatus; 31HWND g_randomizerStatus;
42HINSTANCE g_hInstance; 32HINSTANCE g_hInstance;
43auto g_witnessProc = std::make_shared<Memory>(L"witness64_d3d11.exe"); 33auto g_witnessProc = std::make_shared<Memory>(L"witness64_d3d11.exe");
44std::shared_ptr<Randomizer> g_randomizer; 34std::shared_ptr<Randomizer> g_randomizer;
45std::shared_ptr<Randomizer2> g_randomizer2;
46void SetRandomSeed();
47 35
48LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { 36LRESULT 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
207void 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
226HWND CreateLabel(int x, int y, int width, LPCWSTR text) { 94HWND 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
100HWND 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
232HWND CreateText(int x, int y, int width, LPCWSTR defaultText = L"") { 106HWND 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
3This 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
7As 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
9The 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
5void 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.
12ChallengeRandomizer::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
3class ChallengeRandomizer {
4public:
5 ChallengeRandomizer(const std::shared_ptr<Memory>& memory, int seed);
6
7private:
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
190int 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; 190int 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
192void Memory::LoadPanelOffsets() { 192void 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
3extern 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; 3extern 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
5enum class ProcStatus { 5enum 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
58private: 94private:
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
70std::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
77std::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
70std::vector<int> pillars = { 84std::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
119std::vector<int> squarePanels = { 133std::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
438std::vector<int> desertPanels = { 637std::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
155void 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
164void 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
171void 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
180void 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
188void 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
195void 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
206void Randomizer::RandomizeQuarry() {
207}
208
209void 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
222void Randomizer::RandomizeKeep() { 164void Randomizer::Tutorialise(int panel1, int tutorialStraight) {
223} 165 //const int tutorialStraight = 0x00064;
224 166
225void 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);
243void 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);
256void 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);
263void 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
276void 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
290void Randomizer::RandomizeSwamp() {
291}
292
293void 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
318void 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
325void 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
332void Randomizer::Randomize(std::vector<int>& panels, int flags) {
333 return RandomizeRange(panels, flags, 0, panels.size());
334}
335
336// Range is [start, end)
337void 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)
348void 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);
362void 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
440void 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
458void 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 {
4public: 4public:
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
21private: 8private:
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
9Randomizer2::Randomizer2(const PuzzleSerializer& serializer) : _serializer(serializer) {
10}
11
12void Randomizer2::Randomize() {
13 // RandomizeTutorial();
14 // RandomizeGlassFactory();
15 RandomizeSymmetryIsland();
16 // RandomizeKeep();
17}
18
19void 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
149void 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
364void 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
476void 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
617Puzzle Randomizer2::GetUniqueSolution(Puzzle& p) {
618 auto solutions = Solver::Solve(p);
619 assert(solutions.size() == 1);
620 return solutions[0];
621}
622
623void 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
642void 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
4class Randomizer2 {
5public:
6 Randomizer2(const PuzzleSerializer& serializer);
7 void Randomize();
8 void RandomizeTutorial();
9 void RandomizeGlassFactory();
10 void RandomizeSymmetryIsland();
11 void RandomizeKeep();
12
13private:
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
5std::vector<Pos> Randomizer2Core::CutEdges(const Puzzle& p, size_t numEdges) {
6 return CutEdgesInternal(p, 0, p.width, 0, p.height, numEdges);
7}
8
9std::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
13std::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
44std::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
127void 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
142void 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
153void 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
169std::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
3class Randomizer2Core {
4public:
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
10private:
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
5int Solver::MAX_SOLUTIONS = 10000;
6
7std::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
23void 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
4class Puzzle;
5class Solver {
6public:
7 static int MAX_SOLUTIONS;
8 static std::vector<Puzzle> Solve(Puzzle& p);
9
10private:
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
4void 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
17struct Region{};
18class Puzzle;
19struct Pos;
20class Validator {
21public:
22 static void Validate(Puzzle& p);
23
24private:
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
12EndProject 12EndProject
13Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test\Test.vcxproj", "{128784C2-9157-4291-8FD6-44637BE162FB}" 13Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test\Test.vcxproj", "{128784C2-9157-4291-8FD6-44637BE162FB}"
14EndProject 14EndProject
15Project("{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
21EndProject
22Project("{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
26EndProject
27Global 15Global
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