about summary refs log tree commit diff stats
path: root/Source
diff options
context:
space:
mode:
Diffstat (limited to 'Source')
-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
16 files changed, 326 insertions, 1585 deletions
diff --git a/Source/ChallengeRandomizer.cpp b/Source/ChallengeRandomizer.cpp deleted file mode 100644 index 976374e..0000000 --- a/Source/ChallengeRandomizer.cpp +++ /dev/null
@@ -1,108 +0,0 @@
1#include "pch.h"
2#include "ChallengeRandomizer.h"
3
4// Modify an opcode to use RNG2 instead of main RNG
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};