summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--App/Main.cpp4
-rw-r--r--Source/ChallengeRandomizer.cpp108
-rw-r--r--Source/ChallengeRandomizer.h13
-rw-r--r--Source/Randomizer.cpp251
-rw-r--r--Source/Randomizer.h31
-rw-r--r--Source/Solver.cpp76
-rw-r--r--Source/Solver.h13
-rw-r--r--Source/Source.vcxproj6
-rw-r--r--Source/Validator.cpp97
-rw-r--r--Source/Validator.h27
10 files changed, 8 insertions, 618 deletions
diff --git a/App/Main.cpp b/App/Main.cpp index ad3d127..f0f770a 100644 --- a/App/Main.cpp +++ b/App/Main.cpp
@@ -13,7 +13,6 @@
13#define RANDOMIZE_DONE 0x404 13#define RANDOMIZE_DONE 0x404
14 14
15/* ------- Temp ------- */ 15/* ------- Temp ------- */
16#include "Solver.h"
17#include "PuzzleSerializer.h" 16#include "PuzzleSerializer.h"
18#include <sstream> 17#include <sstream>
19#include <iomanip> 18#include <iomanip>
@@ -150,12 +149,9 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
150 149
151 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."); 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.");
152 CreateLabel(390, 110, 90, L"Version: " VERSION_STR); 151 CreateLabel(390, 110, 90, L"Version: " VERSION_STR);
153 //g_seed = CreateText(10, 10, 100);
154 //PostMessage(g_seed, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
155 g_randomizerStatus = CreateButton(120, 105, 180, L"Tutorialise", RANDOMIZING); 152 g_randomizerStatus = CreateButton(120, 105, 180, L"Tutorialise", RANDOMIZING);
156 EnableWindow(g_randomizerStatus, FALSE); 153 EnableWindow(g_randomizerStatus, FALSE);
157 154
158
159 g_witnessProc->StartHeartbeat(g_hwnd, HEARTBEAT); 155 g_witnessProc->StartHeartbeat(g_hwnd, HEARTBEAT);
160 156
161 ShowWindow(g_hwnd, nCmdShow); 157 ShowWindow(g_hwnd, nCmdShow);
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/Randomizer.cpp b/Source/Randomizer.cpp index 74166af..5239cd6 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,14 +123,6 @@ 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 126
136 // Tutorial Bend 127 // Tutorial Bend
137 for (int panel : utmPerspective) { 128 for (int panel : utmPerspective) {
@@ -141,220 +132,23 @@ void Randomizer::Randomize() {
141 for (int panel : squarePanels) { 132 for (int panel : squarePanels) {
142 Tutorialise(panel, 0x00064); 133 Tutorialise(panel, 0x00064);
143 } 134 }
144 //Randomize(squarePanels, SWAP::LINES | SWAP::COLORS);
145
146 // Individual area modifications
147 MEMORY_CATCH(RandomizeTutorial());
148 MEMORY_CATCH(RandomizeDesert());
149 MEMORY_CATCH(RandomizeQuarry());
150 MEMORY_CATCH(RandomizeTreehouse());
151 MEMORY_CATCH(RandomizeKeep());
152 MEMORY_CATCH(RandomizeShadows());
153 MEMORY_CATCH(RandomizeMonastery());
154 MEMORY_CATCH(RandomizeBunker());
155 MEMORY_CATCH(RandomizeJungle());
156 MEMORY_CATCH(RandomizeSwamp());
157 MEMORY_CATCH(RandomizeMountain());
158 MEMORY_CATCH(RandomizeTown());
159 MEMORY_CATCH(RandomizeSymmetry());
160 // RandomizeAudioLogs();
161}
162
163void Randomizer::AdjustSpeed() {
164 // Desert Surface Final Control
165 _memory->WriteEntityData<float>(0x09F95, OPEN_RATE, {0.04f}); // 4x
166 // Swamp Sliding Bridge
167 _memory->WriteEntityData<float>(0x0061A, OPEN_RATE, {0.1f}); // 4x
168 // Mountain 2 Elevator
169 _memory->WriteEntityData<float>(0x09EEC, OPEN_RATE, {0.075f}); // 3x
170}
171
172void Randomizer::RandomizeLasers() {
173 Randomize(lasers, SWAP::TARGETS);
174 // Read the target of keep front laser, and write it to keep back laser.
175 std::vector<int> keepFrontLaserTarget = _memory->ReadEntityData<int>(0x0360E, TARGET, 1);
176 _memory->WriteEntityData<int>(0x03317, TARGET, keepFrontLaserTarget);
177}
178 135
179void Randomizer::PreventSnipes()
180{
181 // Distance-gate swamp snipe 1 to prevent RNG swamp snipe
182 //_memory->WriteEntityData<float>(0x17C05, MAX_BROADCAST_DISTANCE, {15.0});
183 // Distance-gate shadows laser to prevent sniping through the bars
184 //_memory->WriteEntityData<float>(0x19650, MAX_BROADCAST_DISTANCE, {2.5});
185}
186
187// Private methods
188void Randomizer::RandomizeTutorial() {
189 // Disable tutorial cursor speed modifications (not working?) 136 // Disable tutorial cursor speed modifications (not working?)
190 _memory->WriteEntityData<float>(0x00295, CURSOR_SPEED_SCALE, {1.0}); 137 _memory->WriteEntityData<float>(0x00295, CURSOR_SPEED_SCALE, { 1.0 });
191 _memory->WriteEntityData<float>(0x0C373, CURSOR_SPEED_SCALE, {1.0}); 138 _memory->WriteEntityData<float>(0x0C373, CURSOR_SPEED_SCALE, { 1.0 });
192 _memory->WriteEntityData<float>(0x00293, CURSOR_SPEED_SCALE, {1.0}); 139 _memory->WriteEntityData<float>(0x00293, CURSOR_SPEED_SCALE, { 1.0 });
193 _memory->WriteEntityData<float>(0x002C2, CURSOR_SPEED_SCALE, {1.0}); 140 _memory->WriteEntityData<float>(0x002C2, CURSOR_SPEED_SCALE, { 1.0 });
194}
195
196void Randomizer::RandomizeSymmetry() {
197 /*std::vector<int> randomOrder(transparent.size(), 0);
198 std::iota(randomOrder.begin(), randomOrder.end(), 0);
199 RandomizeRange(randomOrder, SWAP::NONE, 1, 5);
200 ReassignTargets(transparent, randomOrder);*/
201}
202
203void Randomizer::RandomizeDesert() {
204 //Randomize(desertPanels, SWAP::LINES);
205
206 // Turn off desert surface 8
207 /*_memory->WriteEntityData<float>(0x09F94, POWER, {0.0, 0.0});
208 // Turn off desert flood final
209 _memory->WriteEntityData<float>(0x18076, POWER, {0.0, 0.0});
210 // Change desert floating target to desert flood final
211 _memory->WriteEntityData<int>(0x17ECA, TARGET, {0x18077});*/
212}
213
214void Randomizer::RandomizeQuarry() {
215}
216 141
217void Randomizer::RandomizeTreehouse() {
218 // Ensure that whatever pivot panels we have are flagged as "pivotable" 142 // Ensure that whatever pivot panels we have are flagged as "pivotable"
219 // @Bug: Can return {}, be careful! 143 // @Bug: Can return {}, be careful!
220 int panelFlags = _memory->ReadEntityData<int>(0x17DD1, STYLE_FLAGS, 1)[0]; 144 int panelFlags = _memory->ReadEntityData<int>(0x17DD1, STYLE_FLAGS, 1)[0];
221 _memory->WriteEntityData<int>(0x17DD1, STYLE_FLAGS, {panelFlags | 0x8000}); 145 _memory->WriteEntityData<int>(0x17DD1, STYLE_FLAGS, { panelFlags | 0x8000 });
222 panelFlags = _memory->ReadEntityData<int>(0x17CE3, STYLE_FLAGS, 1)[0]; 146 panelFlags = _memory->ReadEntityData<int>(0x17CE3, STYLE_FLAGS, 1)[0];
223 _memory->WriteEntityData<int>(0x17CE3, STYLE_FLAGS, {panelFlags | 0x8000}); 147 _memory->WriteEntityData<int>(0x17CE3, STYLE_FLAGS, { panelFlags | 0x8000 });
224 panelFlags = _memory->ReadEntityData<int>(0x17DB7, STYLE_FLAGS, 1)[0]; 148 panelFlags = _memory->ReadEntityData<int>(0x17DB7, STYLE_FLAGS, 1)[0];
225 _memory->WriteEntityData<int>(0x17DB7, STYLE_FLAGS, {panelFlags | 0x8000}); 149 _memory->WriteEntityData<int>(0x17DB7, STYLE_FLAGS, { panelFlags | 0x8000 });
226 panelFlags = _memory->ReadEntityData<int>(0x17E52, STYLE_FLAGS, 1)[0]; 150 panelFlags = _memory->ReadEntityData<int>(0x17E52, STYLE_FLAGS, 1)[0];
227 _memory->WriteEntityData<int>(0x17E52, STYLE_FLAGS, {panelFlags | 0x8000}); 151 _memory->WriteEntityData<int>(0x17E52, STYLE_FLAGS, { panelFlags | 0x8000 });
228}
229
230void Randomizer::RandomizeKeep() {
231}
232
233void Randomizer::RandomizeShadows() {
234 // Change the shadows tutorial cable to only activate avoid
235 _memory->WriteEntityData<int>(0x319A8, CABLE_TARGET_2, {0});
236 // Change shadows avoid 8 to power shadows follow
237 _memory->WriteEntityData<int>(0x1972F, TARGET, {0x1C34C});
238
239 /*std::vector<int> randomOrder(shadowsPanels.size(), 0);
240 std::iota(randomOrder.begin(), randomOrder.end(), 0);
241 RandomizeRange(randomOrder, SWAP::NONE, 0, 8); // Tutorial
242 RandomizeRange(randomOrder, SWAP::NONE, 8, 16); // Avoid
243 RandomizeRange(randomOrder, SWAP::NONE, 16, 21); // Follow
244 ReassignTargets(shadowsPanels, randomOrder);
245 // Turn off original starting panel
246 _memory->WriteEntityData<float>(shadowsPanels[0], POWER, {0.0f, 0.0f});
247 // Turn on new starting panel
248 _memory->WriteEntityData<float>(shadowsPanels[randomOrder[0]], POWER, {1.0f, 1.0f});*/
249}
250
251void Randomizer::RandomizeTown() {
252 // @Hack...? To open the gate at the end
253 /*std::vector<int> randomOrder(orchard.size() + 1, 0);
254 std::iota(randomOrder.begin(), randomOrder.end(), 0);
255 RandomizeRange(randomOrder, SWAP::NONE, 1, 5);
256 // Ensure that we open the gate before the final puzzle (by swapping)
257 int panel3Index = find(randomOrder, 3);
258 int panel4Index = find(randomOrder, 4);
259 randomOrder[std::min(panel3Index, panel4Index)] = 3;
260 randomOrder[std::max(panel3Index, panel4Index)] = 4;
261 ReassignTargets(orchard, randomOrder);*/
262}
263
264void Randomizer::RandomizeMonastery() {
265 /*std::vector<int> randomOrder(monasteryPanels.size(), 0);
266 std::iota(randomOrder.begin(), randomOrder.end(), 0);
267 RandomizeRange(randomOrder, SWAP::NONE, 3, 9); // Outer 2 & 3, Inner 1-4
268 ReassignTargets(monasteryPanels, randomOrder);*/
269}
270
271void Randomizer::RandomizeBunker() {
272 /*std::vector<int> randomOrder(bunkerPanels.size(), 0);
273 std::iota(randomOrder.begin(), randomOrder.end(), 0);
274 // Randomize Tutorial 2-Advanced Tutorial 4 + Glass 1
275 // Tutorial 1 cannot be randomized, since no other panel can start on
276 // Glass 1 will become door + glass 1, due to the targetting system
277 RandomizeRange(randomOrder, SWAP::NONE, 1, 10);
278 // Randomize Glass 1-3 into everything after the door/glass 1
279 const size_t glass1Index = find(randomOrder, 9);
280 RandomizeRange(randomOrder, SWAP::NONE, glass1Index + 1, 12);
281 ReassignTargets(bunkerPanels, randomOrder);*/
282}
283
284void Randomizer::RandomizeJungle() {
285 /*std::vector<int> randomOrder(junglePanels.size(), 0);
286 std::iota(randomOrder.begin(), randomOrder.end(), 0);
287 // Waves 1 cannot be randomized, since no other panel can start on
288 RandomizeRange(randomOrder, SWAP::NONE, 1, 7); // Waves 2-7
289 RandomizeRange(randomOrder, SWAP::NONE, 8, 13); // Pitches 1-6
290 ReassignTargets(junglePanels, randomOrder);*/
291
292 // Fix the wall's target to point back to the cable, and the cable to point to the pitches panel.
293 // auto wallTarget = _memory->ReadPanelData<int>(junglePanels[7], TARGET, 1);
294 // _memory->WritePanelData<int>(junglePanels[7], TARGET, {0x3C113});
295 // _memory->WritePanelData<int>(0x3C112, CABLE_TARGET_1, wallTarget);
296}
297
298void Randomizer::RandomizeSwamp() {
299}
300
301void Randomizer::RandomizeMountain() {
302 // Randomize multipanel
303 //Randomize(mountainMultipanel, SWAP::LINES | SWAP::COLORS);
304
305 // Randomize final pillars order
306 /*std::vector<int> targets = {pillars[0] + 1};
307 for (const int pillar : pillars) {
308 int target = _memory->ReadEntityData<int>(pillar, TARGET, 1)[0];
309 targets.push_back(target);
310 }
311 targets[5] = pillars[5] + 1;
312
313 std::vector<int> randomOrder(pillars.size(), 0);
314 std::iota(randomOrder.begin(), randomOrder.end(), 0);
315 RandomizeRange(randomOrder, SWAP::NONE, 0, 4); // Left Pillars 1-4
316 RandomizeRange(randomOrder, SWAP::NONE, 5, 9); // Right Pillars 1-4
317 ReassignTargets(pillars, randomOrder, targets);
318 // Turn off original starting panels
319 _memory->WriteEntityData<float>(pillars[0], POWER, {0.0f, 0.0f});
320 _memory->WriteEntityData<float>(pillars[5], POWER, {0.0f, 0.0f});
321 // Turn on new starting panels
322 _memory->WriteEntityData<float>(pillars[randomOrder[0]], POWER, {1.0f, 1.0f});
323 _memory->WriteEntityData<float>(pillars[randomOrder[5]], POWER, {1.0f, 1.0f});*/
324}
325
326void Randomizer::RandomizeChallenge() {
327 /*ChallengeRandomizer cr(_memory, Random::RandInt(1, 0x7FFFFFFF)); // 0 will trigger an "RNG not initialized" block
328 for (int panel : challengePanels) {
329 _memory->WriteEntityData<int>(panel, POWER_OFF_ON_FAIL, {0});
330 }*/
331}
332
333void Randomizer::RandomizeAudioLogs() {
334 std::vector<int> randomOrder(audiologs.size(), 0);
335 std::iota(randomOrder.begin(), randomOrder.end(), 0);
336 Randomize(randomOrder, SWAP::NONE);
337 ReassignNames(audiologs, randomOrder);
338}
339
340void Randomizer::Randomize(std::vector<int>& panels, int flags) {
341 return RandomizeRange(panels, flags, 0, panels.size());
342}
343
344// Range is [start, end)
345void Randomizer::RandomizeRange(std::vector<int> &panels, int flags, size_t startIndex, size_t endIndex) {
346 if (panels.size() == 0) return;
347 if (startIndex >= endIndex) return;
348 if (endIndex >= panels.size()) endIndex = static_cast<int>(panels.size());
349 for (size_t i = 0; i < panels.size(); i++) {
350 /*const int target = Random::RandInt(static_cast<int>(startIndex), static_cast<int>(i));
351 if (i != target) {
352 // std::cout << "Swapping panels " << std::hex << panels[i] << " and " << std::hex << panels[target] << std::endl;
353 SwapPanels(panels[i], panels[target], flags);
354 //std::swap(panels[i], panels[target]); // Panel indices in the array
355 }*/
356
357 }
358} 152}
359 153
360void Randomizer::Tutorialise(int panel1, int tutorialStraight) { 154void Randomizer::Tutorialise(int panel1, int tutorialStraight) {
@@ -421,32 +215,3 @@ void Randomizer::Tutorialise(int panel1, int tutorialStraight) {
421 //arrays.push_back(SPECULAR_TEXTURE); 215 //arrays.push_back(SPECULAR_TEXTURE);
422 216
423} 217}
424
425void Randomizer::ReassignTargets(const std::vector<int>& panels, const std::vector<int>& order, std::vector<int> targets) {
426 if (targets.empty()) {
427 // This list is offset by 1, so the target of the Nth panel is in position N (aka the N+1th element)
428 // The first panel may not have a wire to power it, so we use the panel ID itself.
429 targets = {panels[0] + 1};
430 for (const int panel : panels) {
431 int target = _memory->ReadEntityData<int>(panel, TARGET, 1)[0];
432 targets.push_back(target);
433 }
434 }
435
436 for (size_t i=0; i<order.size() - 1; i++) {
437 // Set the target of order[i] to order[i+1], using the "real" target as determined above.
438 const int panelTarget = targets[order[i+1]];
439 _memory->WriteEntityData<int>(panels[order[i]], TARGET, {panelTarget});
440 }
441}
442
443void Randomizer::ReassignNames(const std::vector<int>& panels, const std::vector<int>& order) {
444 std::vector<int64_t> names;
445 for (const int panel : panels) {
446 names.push_back(_memory->ReadEntityData<int64_t>(panel, AUDIO_LOG_NAME, 1)[0]);
447 }
448
449 for (int i=0; i<panels.size(); i++) {
450 _memory->WriteEntityData<int64_t>(panels[i], AUDIO_LOG_NAME, {names[order[i]]});
451 }
452}
diff --git a/Source/Randomizer.h b/Source/Randomizer.h index ae89dc9..a26acab 100644 --- a/Source/Randomizer.h +++ b/Source/Randomizer.h
@@ -4,42 +4,11 @@ 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);
39 void RandomizeRange(std::vector<int> &panels, int flags, size_t startIndex, size_t endIndex);
40 void Tutorialise(int panel1, int copyFrom); 11 void Tutorialise(int panel1, int copyFrom);
41 void ReassignTargets(const std::vector<int>& panels, const std::vector<int>& order, std::vector<int> targets = {});
42 void ReassignNames(const std::vector<int>& panels, const std::vector<int>& order);
43 12
44 std::shared_ptr<Memory> _memory; 13 std::shared_ptr<Memory> _memory;
45 14
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 8d6104c..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,11 +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="Solver.h" />
176 <ClInclude Include="Validator.h" />
177 </ItemGroup> 174 </ItemGroup>
178 <ItemGroup> 175 <ItemGroup>
179 <ClCompile Include="ChallengeRandomizer.cpp" />
180 <ClCompile Include="Memory.cpp" /> 176 <ClCompile Include="Memory.cpp" />
181 <ClCompile Include="pch.cpp"> 177 <ClCompile Include="pch.cpp">
182 <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader> 178 <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
@@ -188,8 +184,6 @@
188 <ClCompile Include="PuzzleSerializer.cpp" /> 184 <ClCompile Include="PuzzleSerializer.cpp" />
189 <ClCompile Include="Random.cpp" /> 185 <ClCompile Include="Random.cpp" />
190 <ClCompile Include="Randomizer.cpp" /> 186 <ClCompile Include="Randomizer.cpp" />
191 <ClCompile Include="Solver.cpp" />
192 <ClCompile Include="Validator.cpp" />
193 </ItemGroup> 187 </ItemGroup>
194 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> 188 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
195 <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};