about summary refs log tree commit diff stats
path: root/Source/Randomizer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Randomizer.cpp')
-rw-r--r--Source/Randomizer.cpp286
1 files changed, 104 insertions, 182 deletions
diff --git a/Source/Randomizer.cpp b/Source/Randomizer.cpp index b8462e5..8c4297d 100644 --- a/Source/Randomizer.cpp +++ b/Source/Randomizer.cpp
@@ -2,15 +2,10 @@
2 * TODO: Split out main() logic into another file, and move into separate functions for easier testing. Then write tests. 2 * TODO: Split out main() logic into another file, and move into separate functions for easier testing. Then write tests.
3 * BUGS: 3 * BUGS:
4 * Shipwreck vault fails, possibly because of dot_reflection? Sometimes? 4 * Shipwreck vault fails, possibly because of dot_reflection? Sometimes?
5 * Treehouse pivots *should* work, but I need to not copy style_flags.
6 This seems to cause crashes when pivots appear elsewhere in the world.
7 * Some panels are impossible casually: (idc, I think) 5 * Some panels are impossible casually: (idc, I think)
8 ** Town Stars, Invisible dots 6 ** Town Stars, Invisible dots
9 * Something is wrong with jungle re: softlocks
10 * FEATURES: 7 * FEATURES:
11 * SWAP_TARGETS should still require the full panel sequence (and have ways to prevent softlocks?) 8 * SWAP_TARGETS should still require the full panel sequence (and have ways to prevent softlocks?)
12 ** Think about: Jungle
13 ** Hard: Monastery
14 ** Do: Challenge 9 ** Do: Challenge
15 * Randomize audio logs 10 * Randomize audio logs
16 * Swap sounds in jungle (along with panels) -- maybe impossible 11 * Swap sounds in jungle (along with panels) -- maybe impossible
@@ -28,212 +23,139 @@
28#include <chrono> 23#include <chrono>
29 24
30template <class T> 25template <class T>
31size_t find(const std::vector<T> &data, T search, size_t startIndex = 0) { 26int find(const std::vector<T> &data, T search, size_t startIndex = 0) {
32 for (size_t i=startIndex ; i<data.size(); i++) { 27 for (size_t i=startIndex ; i<data.size(); i++) {
33 if (data[i] == search) return i; 28 if (data[i] == search) return static_cast<int>(i);
34 } 29 }
35 std::cout << "Couldn't find " << search << " in data!" << std::endl; 30 std::cout << "Couldn't find " << search << " in data!" << std::endl;
36 exit(-1); 31 exit(-1);
37} 32}
38 33
39void Randomizer::Randomize(int seed) 34void Randomizer::Randomize()
40{ 35{
41 // Content swaps -- must happen before squarePanels 36 // Content swaps -- must happen before squarePanels
42 Randomize(tallUpDownPanels, SWAP_LINES | SWAP_STYLE); 37 _core.Randomize(tallUpDownPanels, SWAP_LINES|SWAP_LINES);
43 Randomize(upDownPanels, SWAP_LINES | SWAP_STYLE); 38 _core.Randomize(upDownPanels, SWAP_LINES|SWAP_LINES);
44 Randomize(leftForwardRightPanels, SWAP_LINES); 39 _core.Randomize(leftForwardRightPanels, SWAP_LINES|SWAP_LINES);
45 40
46 Randomize(squarePanels, SWAP_LINES | SWAP_STYLE); 41 _core.Randomize(squarePanels, SWAP_LINES|SWAP_LINES);
47 42
48 // Frame swaps -- must happen after squarePanels 43 // Individual area modifications
49 Randomize(burnablePanels, SWAP_LINES | SWAP_STYLE); 44 RandomizeTutorial();
45 RandomizeSymmetry();
46 RandomizeDesert();
47 RandomizeQuarry();
48 RandomizeTreehouse();
49 RandomizeKeep();
50 RandomizeShadows();
51 RandomizeTown();
52 RandomizeMonastery();
53 RandomizeBunker();
54 RandomizeJungle();
55 RandomizeSwamp();
56 RandomizeMountain();
57}
50 58
51 // Target swaps, can happen whenever 59void Randomizer::RandomizeTutorial() {
52 Randomize(lasers, SWAP_TARGETS); 60 // Disable tutorial cursor speed modifications (not working?)
53 // Read the target of keep front laser, and write it to keep back laser. 61 _core.WritePanelData<float>(0x00295, CURSOR_SPEED_SCALE, {1.0});
54 std::vector<int> keepFrontLaserTarget = ReadPanelData<int>(0x0360E, TARGET, 1); 62 _core.WritePanelData<float>(0x0C373, CURSOR_SPEED_SCALE, {1.0});
55 WritePanelData<int>(0x03317, TARGET, keepFrontLaserTarget); 63 _core.WritePanelData<float>(0x00293, CURSOR_SPEED_SCALE, {1.0});
64 _core.WritePanelData<float>(0x002C2, CURSOR_SPEED_SCALE, {1.0});
65}
56 66
57 std::vector<int> randomOrder; 67void Randomizer::RandomizeSymmetry() {
68}
58 69
59 /* Jungle 70void Randomizer::RandomizeDesert() {
60 randomOrder = std::vector(junglePanels.size(), 0); 71 _core.Randomize(desertPanels, SWAP_LINES|SWAP_LINES);
61 std::iota(randomOrder.begin(), randomOrder.end(), 0);
62 // Randomize Waves 2-7
63 // Waves 1 cannot be randomized, since no other panel can start on
64 randomizer.RandomizeRange(randomOrder, SWAP_NONE, 1, 7);
65 // Randomize Pitches 1-6 onto themselves
66 randomizer.RandomizeRange(randomOrder, SWAP_NONE, 7, 13);
67 randomizer.ReassignTargets(junglePanels, randomOrder);
68 */
69 72
70 /* Bunker */ 73 // Turn off desert surface 8
71 randomOrder = std::vector(bunkerPanels.size(), 0); 74 _core.WritePanelData<float>(0x09F94, POWER, {0.0, 0.0});
72 std::iota(randomOrder.begin(), randomOrder.end(), 0); 75 // Turn off desert flood final
73 // Randomize Tutorial 2-Advanced Tutorial 4 + Glass 1 76 _core.WritePanelData<float>(0x18076, POWER, {0.0, 0.0});
74 // Tutorial 1 cannot be randomized, since no other panel can start on 77 // Change desert floating target to desert flood final
75 // Glass 1 will become door + glass 1, due to the targetting system 78 _core.WritePanelData<int>(0x17ECA, TARGET, {0x18077});
76 RandomizeRange(randomOrder, SWAP_NONE, 1, 10); 79}
77 // Randomize Glass 1-3 into everything after the door
78 const size_t glassDoorIndex = find(randomOrder, 9) + 1;
79 RandomizeRange(randomOrder, SWAP_NONE, glassDoorIndex, 12);
80 ReassignTargets(bunkerPanels, randomOrder);
81
82 /* Shadows */
83 randomOrder = std::vector(shadowsPanels.size(), 0);
84 std::iota(randomOrder.begin(), randomOrder.end(), 0);
85 RandomizeRange(randomOrder, SWAP_NONE, 0, 8); // Tutorial
86 RandomizeRange(randomOrder, SWAP_NONE, 8, 16); // Avoid
87 RandomizeRange(randomOrder, SWAP_NONE, 16, 21); // Follow
88 ReassignTargets(shadowsPanels, randomOrder);
89 // Turn off original starting panel
90 WritePanelData<float>(shadowsPanels[0], POWER, {0.0f, 0.0f});
91 // Turn on new starting panel
92 WritePanelData<float>(shadowsPanels[randomOrder[0]], POWER, {1.0f, 1.0f});
93 80
94 /* Monastery 81void Randomizer::RandomizeQuarry() {
95 randomOrder = std::vector(monasteryPanels.size(), 0); 82}
96 std::iota(randomOrder.begin(), randomOrder.end(), 0);
97 randomizer.RandomizeRange(randomOrder, SWAP_NONE, 2, 6); // outer 2 & 3, inner 1
98 // Once outer 3 and right door are solved, inner 2-4 are accessible
99 int innerPanelsIndex = max(find(randomOrder, 2), find(randomOrder, 4));
100 randomizer.RandomizeRange(randomOrder, SWAP_NONE, innerPanelsIndex, 9); // Inner 2-4
101 83
102 randomizer.ReassignTargets(monasteryPanels, randomOrder); 84void Randomizer::RandomizeTreehouse() {
103 */ 85 // Ensure that whatever pivot panels we have are flagged as "pivotable"
86 _core.WritePanelData<int>(0x17DD1, STYLE_FLAGS, {0x8000});
87 _core.WritePanelData<int>(0x17CE3, STYLE_FLAGS, {0x8000});
88 _core.WritePanelData<int>(0x17DB7, STYLE_FLAGS, {0x8000});
89 _core.WritePanelData<int>(0x17E52, STYLE_FLAGS, {0x8000});
104} 90}
105 91
106Randomizer::Randomizer() 92void Randomizer::RandomizeKeep() {
107{ 93}
108 // Turn off desert surface 8
109 WritePanelData<float>(0x09F94, POWER, {0.0, 0.0});
110 // Turn off desert flood final
111 WritePanelData<float>(0x18076, POWER, {0.0, 0.0});
112 // Change desert floating target to desert flood final
113 WritePanelData<int>(0x17ECA, TARGET, {0x18077});
114 94
95void Randomizer::RandomizeShadows() {
115 // Distance-gate shadows laser to prevent sniping through the bars 96 // Distance-gate shadows laser to prevent sniping through the bars
116 WritePanelData<float>(0x19650, MAX_BROADCAST_DISTANCE, {2.5}); 97 _core.WritePanelData<float>(0x19650, MAX_BROADCAST_DISTANCE, {2.5});
117 // Change the shadows tutorial cable to only activate avoid 98 // Change the shadows tutorial cable to only activate avoid
118 WritePanelData<int>(0x319A8, CABLE_TARGET_2, {0}); 99 _core.WritePanelData<int>(0x319A8, CABLE_TARGET_2, {0});
119 // Change shadows avoid 8 to power shadows follow 100 // Change shadows avoid 8 to power shadows follow
120 WritePanelData<int>(0x1972F, TARGET, {0x1C34C}); 101 _core.WritePanelData<int>(0x1972F, TARGET, {0x1C34C});
121
122 // Distance-gate swamp snipe 1 to prevent RNG swamp snipe
123 WritePanelData<float>(0x17C05, MAX_BROADCAST_DISTANCE, {5.0});
124 102
125 // Disable tutorial cursor speed modifications (not working?) 103 std::vector<int> randomOrder(shadowsPanels.size(), 0);
126 WritePanelData<float>(0x00295, CURSOR_SPEED_SCALE, {1.0}); 104 std::iota(randomOrder.begin(), randomOrder.end(), 0);
127 WritePanelData<float>(0x0C373, CURSOR_SPEED_SCALE, {1.0}); 105 _core.RandomizeRange(randomOrder, SWAP_NONE, 0, 8); // Tutorial
128 WritePanelData<float>(0x00293, CURSOR_SPEED_SCALE, {1.0}); 106 _core.RandomizeRange(randomOrder, SWAP_NONE, 8, 16); // Avoid
129 WritePanelData<float>(0x002C2, CURSOR_SPEED_SCALE, {1.0}); 107 _core.RandomizeRange(randomOrder, SWAP_NONE, 16, 21); // Follow
108 _core.ReassignTargets(shadowsPanels, randomOrder);
109 // Turn off original starting panel
110 _core.WritePanelData<float>(shadowsPanels[0], POWER, {0.0f, 0.0f});
111 // Turn on new starting panel
112 _core.WritePanelData<float>(shadowsPanels[randomOrder[0]], POWER, {1.0f, 1.0f});
130} 113}
131 114
132void Randomizer::Randomize(std::vector<int>& panels, int flags) { 115void Randomizer::RandomizeTown() {
133 return RandomizeRange(panels, flags, 0, panels.size());
134} 116}
135 117
136// Range is [start, end) 118void Randomizer::RandomizeMonastery() {
137void Randomizer::RandomizeRange(std::vector<int> &panels, int flags, size_t startIndex, size_t endIndex) { 119 std::vector<int> randomOrder(monasteryPanels.size(), 0);
138 if (panels.size() == 0) return; 120 std::iota(randomOrder.begin(), randomOrder.end(), 0);
139 if (startIndex >= endIndex) return; 121 _core.RandomizeRange(randomOrder, SWAP_NONE, 3, 9); // Outer 2 & 3, Inner 1-4
140 if (endIndex >= panels.size()) endIndex = panels.size(); 122 _core.ReassignTargets(monasteryPanels, randomOrder);
141 for (size_t i = endIndex-1; i > startIndex+1; i--) {
142 const size_t target = rand() % (i - startIndex) + startIndex;
143 if (i != target) {
144 // std::cout << "Swapping panels " << std::hex << panels[i] << " and " << std::hex << panels[target] << std::endl;
145 SwapPanels(panels[i], panels[target], flags);
146 std::swap(panels[i], panels[target]); // Panel indices in the array
147 }
148 }
149} 123}
150 124
151void Randomizer::SwapPanels(int panel1, int panel2, int flags) { 125void Randomizer::RandomizeBunker() {
152 std::map<int, int> offsets; 126 std::vector<int> randomOrder(bunkerPanels.size(), 0);
127 std::iota(randomOrder.begin(), randomOrder.end(), 0);
128 // Randomize Tutorial 2-Advanced Tutorial 4 + Glass 1
129 // Tutorial 1 cannot be randomized, since no other panel can start on
130 // Glass 1 will become door + glass 1, due to the targetting system
131 _core.RandomizeRange(randomOrder, SWAP_NONE, 1, 10);
132 // Randomize Glass 1-3 into everything after the door/glass 1
133 const size_t glass1Index = find(randomOrder, 9);
134 _core.RandomizeRange(randomOrder, SWAP_NONE, glass1Index + 1, 12);
135 _core.ReassignTargets(bunkerPanels, randomOrder);
136}
153 137
154 if (flags & SWAP_TARGETS) { 138void Randomizer::RandomizeJungle() {
155 offsets[TARGET] = sizeof(int); 139 std::vector<int> randomOrder(junglePanels.size(), 0);
156 } 140 std::iota(randomOrder.begin(), randomOrder.end(), 0);
157 if (flags & SWAP_STYLE) { 141 // Randomize Waves 2-7
158 offsets[STYLE_FLAGS] = sizeof(int); 142 // Waves 1 cannot be randomized, since no other panel can start on
159 } 143 _core.RandomizeRange(randomOrder, SWAP_NONE, 1, 7);
160 if (flags & SWAP_LINES) { 144 // Randomize Pitches 1-6 onto themselves
161 offsets[PATH_COLOR] = 16; 145 _core.RandomizeRange(randomOrder, SWAP_NONE, 8, 13);
162 offsets[REFLECTION_PATH_COLOR] = 16; 146 _core.ReassignTargets(junglePanels, randomOrder);
163 offsets[DOT_COLOR] = 16; 147}
164 offsets[ACTIVE_COLOR] = 16;
165 offsets[BACKGROUND_REGION_COLOR] = 16;
166 offsets[SUCCESS_COLOR_A] = 16;
167 offsets[SUCCESS_COLOR_B] = 16;
168 offsets[STROBE_COLOR_A] = 16;
169 offsets[STROBE_COLOR_B] = 16;
170 offsets[ERROR_COLOR] = 16;
171 offsets[PATTERN_POINT_COLOR] = 16;
172 offsets[PATTERN_POINT_COLOR_A] = 16;
173 offsets[PATTERN_POINT_COLOR_B] = 16;
174 offsets[SYMBOL_A] = 16;
175 offsets[SYMBOL_B] = 16;
176 offsets[SYMBOL_C] = 16;
177 offsets[SYMBOL_D] = 16;
178 offsets[SYMBOL_E] = 16;
179 offsets[PUSH_SYMBOL_COLORS] = sizeof(int);
180 offsets[OUTER_BACKGROUND] = 16;
181 offsets[OUTER_BACKGROUND_MODE] = sizeof(int);
182 offsets[TRACED_EDGES] = 16;
183 offsets[AUDIO_PREFIX] = sizeof(void*);
184// offsets[IS_CYLINDER] = sizeof(int);
185// offsets[CYLINDER_Z0] = sizeof(float);
186// offsets[CYLINDER_Z1] = sizeof(float);
187// offsets[CYLINDER_RADIUS] = sizeof(float);
188 offsets[SPECULAR_ADD] = sizeof(float);
189 offsets[SPECULAR_POWER] = sizeof(int);
190 offsets[PATH_WIDTH_SCALE] = sizeof(float);
191 offsets[STARTPOINT_SCALE] = sizeof(float);
192 offsets[NUM_DOTS] = sizeof(int);
193 offsets[NUM_CONNECTIONS] = sizeof(int);
194 offsets[DOT_POSITIONS] = sizeof(void*);
195 offsets[DOT_FLAGS] = sizeof(void*);
196 offsets[DOT_CONNECTION_A] = sizeof(void*);
197 offsets[DOT_CONNECTION_B] = sizeof(void*);
198 offsets[DECORATIONS] = sizeof(void*);
199 offsets[DECORATION_FLAGS] = sizeof(void*);
200 offsets[DECORATION_COLORS] = sizeof(void*);
201 offsets[NUM_DECORATIONS] = sizeof(int);
202 offsets[REFLECTION_DATA] = sizeof(void*);
203 offsets[GRID_SIZE_X] = sizeof(int);
204 offsets[GRID_SIZE_Y] = sizeof(int);
205 offsets[SEQUENCE_LEN] = sizeof(int);
206 offsets[SEQUENCE] = sizeof(void*);
207 offsets[DOT_SEQUENCE_LEN] = sizeof(int);
208 offsets[DOT_SEQUENCE] = sizeof(void*);
209 offsets[DOT_SEQUENCE_LEN_REFLECTION] = sizeof(int);
210 offsets[DOT_SEQUENCE_REFLECTION] = sizeof(void*);
211 offsets[NUM_COLORED_REGIONS] = sizeof(int);
212 offsets[COLORED_REGIONS] = sizeof(void*);
213 offsets[PANEL_TARGET] = sizeof(void*);
214 offsets[SPECULAR_TEXTURE] = sizeof(void*);
215 }
216 148
217 for (auto const& [offset, size] : offsets) { 149void Randomizer::RandomizeSwamp() {
218 std::vector<byte> panel1data = ReadPanelData<byte>(panel1, offset, size); 150 // Distance-gate swamp snipe 1 to prevent RNG swamp snipe
219 std::vector<byte> panel2data = ReadPanelData<byte>(panel2, offset, size); 151 _core.WritePanelData<float>(0x17C05, MAX_BROADCAST_DISTANCE, {15.0});
220 WritePanelData<byte>(panel2, offset, panel1data);
221 WritePanelData<byte>(panel1, offset, panel2data);
222 }
223} 152}
224 153
225void Randomizer::ReassignTargets(const std::vector<int>& panels, const std::vector<int>& order) { 154void Randomizer::RandomizeMountain() {
226 // This list is offset by 1, so the target of the Nth panel is in position N (aka the N+1th element) 155 _core.Randomize(lasers, SWAP_TARGETS);
227 // The first panel may not have a wire to power it, so we use the panel ID itself. 156 _core.Randomize(pillars, SWAP_LINES|SWAP_LINES);
228 std::vector<int> targetToActivatePanel = {panels[0] + 1};
229 for (const int panel : panels) {
230 int target = ReadPanelData<int>(panel, TARGET, 1)[0];
231 targetToActivatePanel.push_back(target);
232 }
233 157
234 for (size_t i=0; i<order.size() - 1; i++) { 158 // Read the target of keep front laser, and write it to keep back laser.
235 // Set the target of order[i] to order[i+1], using the "real" target as determined above. 159 std::vector<int> keepFrontLaserTarget = _core.ReadPanelData<int>(0x0360E, TARGET, 1);
236 const int panelTarget = targetToActivatePanel[order[i+1]]; 160 _core.WritePanelData<int>(0x03317, TARGET, keepFrontLaserTarget);
237 WritePanelData<int>(panels[order[i]], TARGET, {panelTarget}); 161} \ No newline at end of file
238 }
239}