summary refs log tree commit diff stats
path: root/Source
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2021-08-21 17:18:02 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2021-08-21 17:18:02 -0400
commitba04ac934dea549497e2e89fa7a9e5bc8723f5f8 (patch)
treef852810377bfe663dfd73bedfaaf4dca8c657f37 /Source
parentc60df0e75e63f488d94fd744ad70df8124dc7724 (diff)
downloadwitness-tutorializer-ba04ac934dea549497e2e89fa7a9e5bc8723f5f8.tar.gz
witness-tutorializer-ba04ac934dea549497e2e89fa7a9e5bc8723f5f8.tar.bz2
witness-tutorializer-ba04ac934dea549497e2e89fa7a9e5bc8723f5f8.zip
Cleaned up a bunch of stuff I'm not using
Diffstat (limited to 'Source')
-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
9 files changed, 8 insertions, 614 deletions
diff --git a/Source/ChallengeRandomizer.cpp b/Source/ChallengeRandomizer.cpp deleted file mode 100644 index 976374e..0000000 --- a/Source/ChallengeRandomizer.cpp +++ /dev/null
@@ -1,108 +0,0 @@
1#include "pch.h"
2#include "ChallengeRandomizer.h"
3
4// Modify an opcode to use RNG2 instead of main RNG
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};