summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--App/Main.cpp1
-rw-r--r--Source/Memory.cpp25
-rw-r--r--Source/Memory.h27
-rw-r--r--Source/Puzzle.cpp11
-rw-r--r--Source/Puzzle.h20
-rw-r--r--Source/PuzzlerSerializer.cpp285
-rw-r--r--Source/PuzzlerSerializer.h24
-rw-r--r--Source/Randomizer2.cpp207
-rw-r--r--Source/Randomizer2.h1
-rw-r--r--Source/Solver.cpp2
10 files changed, 413 insertions, 190 deletions
diff --git a/App/Main.cpp b/App/Main.cpp index a96c34d..a815746 100644 --- a/App/Main.cpp +++ b/App/Main.cpp
@@ -24,6 +24,7 @@
24#include "Puzzle.h" 24#include "Puzzle.h"
25#include "Solver.h" 25#include "Solver.h"
26#include "Randomizer2.h" 26#include "Randomizer2.h"
27#include "PuzzlerSerializer.h"
27#include <sstream> 28#include <sstream>
28 29
29#define TMP1 0x501 30#define TMP1 0x501
diff --git a/Source/Memory.cpp b/Source/Memory.cpp index 98b06f9..bc0725b 100644 --- a/Source/Memory.cpp +++ b/Source/Memory.cpp
@@ -70,7 +70,6 @@ void Memory::Heartbeat(HWND window) {
70 PostMessage(window, WM_COMMAND, HEARTBEAT, (LPARAM)ProcStatus::Running); 70 PostMessage(window, WM_COMMAND, HEARTBEAT, (LPARAM)ProcStatus::Running);
71} 71}
72 72
73
74[[nodiscard]] 73[[nodiscard]]
75bool Memory::Initialize() { 74bool Memory::Initialize() {
76 // First, get the handle of the process 75 // First, get the handle of the process
@@ -106,6 +105,7 @@ bool Memory::Initialize() {
106 std::cerr << "Couldn't locate base address" << std::endl; 105 std::cerr << "Couldn't locate base address" << std::endl;
107 return false; 106 return false;
108 } 107 }
108
109 return true; 109 return true;
110} 110}
111 111
@@ -161,7 +161,7 @@ void Memory::ThrowError() {
161} 161}
162 162
163void* Memory::ComputeOffset(std::vector<int> offsets) { 163void* Memory::ComputeOffset(std::vector<int> offsets) {
164 // Leave off the last offset, since it will be either read/write, and may not be of type unitptr_t. 164 // Leave off the last offset, since it will be either read/write, and may not be of type uintptr_t.
165 int final_offset = offsets.back(); 165 int final_offset = offsets.back();
166 offsets.pop_back(); 166 offsets.pop_back();
167 167
@@ -176,6 +176,9 @@ void* Memory::ComputeOffset(std::vector<int> offsets) {
176 if (bool result = !ReadProcessMemory(_handle, reinterpret_cast<LPVOID>(cumulativeAddress), &computedAddress, sizeof(uintptr_t), NULL)) { 176 if (bool result = !ReadProcessMemory(_handle, reinterpret_cast<LPVOID>(cumulativeAddress), &computedAddress, sizeof(uintptr_t), NULL)) {
177 ThrowError(); 177 ThrowError();
178 } 178 }
179 if (computedAddress == 0) { // Attempting to dereference a nullptr
180 ThrowError();
181 }
179 _computedAddresses[cumulativeAddress] = computedAddress; 182 _computedAddresses[cumulativeAddress] = computedAddress;
180 } 183 }
181 184
@@ -183,3 +186,21 @@ void* Memory::ComputeOffset(std::vector<int> offsets) {
183 } 186 }
184 return reinterpret_cast<void*>(cumulativeAddress + final_offset); 187 return reinterpret_cast<void*>(cumulativeAddress + final_offset);
185} 188}
189
190uintptr_t Memory::Allocate(size_t bytes) {
191 uintptr_t current = _freeMem;
192 _freeMem += bytes;
193
194 if (_freeMem > _freeMemEnd) {
195 // If we don't have enough space at our current location, go allocate some more space.
196 // Note that the remaining space in our current page is unused. Oh well.
197 _freeMem = reinterpret_cast<uintptr_t>(::VirtualAllocEx(_handle, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
198 _freeMemEnd = _freeMem + 0x1000;
199
200 current = _freeMem;
201 _freeMem += bytes;
202 assert(_freeMem <= _freeMemEnd); // Don't allocate data > 0x1000 at a time. Duh.
203 }
204
205 return current;
206}
diff --git a/Source/Memory.h b/Source/Memory.h index 6b8403f..af4f0ae 100644 --- a/Source/Memory.h +++ b/Source/Memory.h
@@ -35,7 +35,14 @@ public:
35 35
36 template <class T> 36 template <class T>
37 void WriteArray(int panel, int offset, const std::vector<T>& data) { 37 void WriteArray(int panel, int offset, const std::vector<T>& data) {
38 WriteData<T>({GLOBALS, 0x18, panel*8, offset, 0}, data); 38 WriteData({GLOBALS, 0x18, panel*8, offset, 0}, data);
39 }
40
41 template <class T>
42 void WriteNewArray(int panel, int offset, const std::vector<T>& data) {
43 std::vector<uintptr_t> newAddr = {Allocate(data.size() * sizeof(T))};
44 WritePanelData(panel, offset, newAddr);
45 WriteArray(panel, offset, data);
39 } 46 }
40 47
41 template <class T> 48 template <class T>
@@ -45,7 +52,7 @@ public:
45 52
46 template <class T> 53 template <class T>
47 void WritePanelData(int panel, int offset, const std::vector<T>& data) { 54 void WritePanelData(int panel, int offset, const std::vector<T>& data) {
48 WriteData<T>({GLOBALS, 0x18, panel*8, offset}, data); 55 WriteData({GLOBALS, 0x18, panel*8, offset}, data);
49 } 56 }
50 57
51 void AddSigScan(const std::vector<byte>& scanBytes, const std::function<void(int index)>& scanFunc); 58 void AddSigScan(const std::vector<byte>& scanBytes, const std::function<void(int index)>& scanFunc);
@@ -57,9 +64,12 @@ private:
57 if (numItems == 0) return {}; 64 if (numItems == 0) return {};
58 std::vector<T> data; 65 std::vector<T> data;
59 data.resize(numItems); 66 data.resize(numItems);
67 void* computedOffset = ComputeOffset(offsets);
60 for (int i=0; i<5; i++) { 68 for (int i=0; i<5; i++) {
61 if (ReadProcessMemory(_handle, ComputeOffset(offsets), &data[0], sizeof(T) * numItems, nullptr)) 69 if (ReadProcessMemory(_handle, computedOffset, &data[0], sizeof(T) * numItems, nullptr)) {
62 { 70 if (i != 0) {
71 int k = 0;
72 }
63 return data; 73 return data;
64 } 74 }
65 } 75 }
@@ -70,8 +80,12 @@ private:
70 template <class T> 80 template <class T>
71 void WriteData(const std::vector<int>& offsets, const std::vector<T>& data) { 81 void WriteData(const std::vector<int>& offsets, const std::vector<T>& data) {
72 if (data.empty()) return; 82 if (data.empty()) return;
83 void* computedOffset = ComputeOffset(offsets);
73 for (int i=0; i<5; i++) { 84 for (int i=0; i<5; i++) {
74 if (WriteProcessMemory(_handle, ComputeOffset(offsets), &data[0], sizeof(T) * data.size(), nullptr)) { 85 if (WriteProcessMemory(_handle, computedOffset, &data[0], sizeof(T) * data.size(), nullptr)) {
86 if (i != 0) {
87 int k = 0;
88 }
75 return; 89 return;
76 } 90 }
77 } 91 }
@@ -82,6 +96,7 @@ private:
82 bool Initialize(); 96 bool Initialize();
83 void ThrowError(); 97 void ThrowError();
84 void* ComputeOffset(std::vector<int> offsets); 98 void* ComputeOffset(std::vector<int> offsets);
99 uintptr_t Allocate(size_t bytes);
85 100
86 int _previousFrame = 0; 101 int _previousFrame = 0;
87 bool _threadActive = false; 102 bool _threadActive = false;
@@ -90,6 +105,8 @@ private:
90 std::map<uintptr_t, uintptr_t> _computedAddresses; 105 std::map<uintptr_t, uintptr_t> _computedAddresses;
91 uintptr_t _baseAddress = 0; 106 uintptr_t _baseAddress = 0;
92 HANDLE _handle = nullptr; 107 HANDLE _handle = nullptr;
108 uintptr_t _freeMem = 0;
109 uintptr_t _freeMemEnd = 0;
93 struct SigScan { 110 struct SigScan {
94 std::function<void(int)> scanFunc; 111 std::function<void(int)> scanFunc;
95 bool found; 112 bool found;
diff --git a/Source/Puzzle.cpp b/Source/Puzzle.cpp index 72af129..d0ede27 100644 --- a/Source/Puzzle.cpp +++ b/Source/Puzzle.cpp
@@ -2,18 +2,17 @@
2#include "Memory.h" 2#include "Memory.h"
3#include <cassert> 3#include <cassert>
4 4
5 5Cell Puzzle::GetCell(int x, int y) const {
6inline Cell Puzzle::GetCell(int x, int y) const {
7 x = Mod(x); 6 x = Mod(x);
8 if (!SafeCell(x, y)) return Cell::Undefined(); 7 if (!SafeCell(x, y)) return Cell::Undefined();
9 return grid[x][y]; 8 return grid[x][y];
10} 9}
11 10
12inline Cell::Color Puzzle::GetLine(int x, int y) const { 11Cell::Color Puzzle::GetLine(int x, int y) const {
13 return grid[x][y].color; 12 return grid[x][y].color;
14} 13}
15 14
16inline void Puzzle::NewGrid(int newWidth, int newHeight) { 15void Puzzle::NewGrid(int newWidth, int newHeight) {
17 if (newWidth == 0) { 16 if (newWidth == 0) {
18 assert(false); 17 assert(false);
19 newWidth = width; 18 newWidth = width;
@@ -28,12 +27,12 @@ inline void Puzzle::NewGrid(int newWidth, int newHeight) {
28 for (int x=0; x<width; x++) grid[x].resize(height); 27 for (int x=0; x<width; x++) grid[x].resize(height);
29} 28}
30 29
31inline int Puzzle::Mod(int x) const { 30int Puzzle::Mod(int x) const {
32 if (!pillar) return x; 31 if (!pillar) return x;
33 return (x + width * height * 2) % width; 32 return (x + width * height * 2) % width;
34} 33}
35 34
36inline bool Puzzle::SafeCell(int x, int y) const { 35bool Puzzle::SafeCell(int x, int y) const {
37 if (x < 0 || x >= width) return false; 36 if (x < 0 || x >= width) return false;
38 if (y < 0 || y >= height) return false; 37 if (y < 0 || y >= height) return false;
39 return true; 38 return true;
diff --git a/Source/Puzzle.h b/Source/Puzzle.h index 3a8e73b..fdf51be 100644 --- a/Source/Puzzle.h +++ b/Source/Puzzle.h
@@ -64,21 +64,23 @@ struct Pos {int x; int y;};
64 64
65class Puzzle { 65class Puzzle {
66public: 66public:
67 int16_t height; 67 int16_t height = 0;
68 int16_t width; 68 int16_t width = 0;
69 bool hasDecorations = false; 69 bool hasDecorations = false;
70 70
71 enum class Symmetry {NONE, X, Y, XY}; 71 enum class Symmetry {NONE, X, Y, XY};
72 Symmetry sym = Symmetry::NONE; 72 Symmetry sym = Symmetry::NONE;
73 bool pillar = false; 73 bool pillar = false;
74 74
75 bool valid; 75 bool valid = false;
76 std::vector<Negation> negations; 76 std::vector<Negation> negations;
77 std::vector<Pos> invalidElements; 77 std::vector<Pos> invalidElements;
78 78
79 inline Cell GetCell(int x, int y) const; 79 std::vector<Pos> sequence;
80 inline Cell::Color GetLine(int x, int y) const; 80
81 inline void NewGrid(int newWidth, int newHeight); 81 Cell GetCell(int x, int y) const;
82 Cell::Color GetLine(int x, int y) const;
83 void NewGrid(int newWidth, int newHeight);
82 84
83 // @TODO: 85 // @TODO:
84 Pos GetSymmetricalPos(int x, int y); 86 Pos GetSymmetricalPos(int x, int y);
@@ -86,7 +88,7 @@ public:
86// private: 88// private:
87 std::vector<std::vector<Cell>> grid; 89 std::vector<std::vector<Cell>> grid;
88 90
89private: 91// private:
90 inline int Mod(int x) const; 92 int Mod(int x) const;
91 inline bool SafeCell(int x, int y) const; 93 bool SafeCell(int x, int y) const;
92}; 94};
diff --git a/Source/PuzzlerSerializer.cpp b/Source/PuzzlerSerializer.cpp index 779336d..86f59e7 100644 --- a/Source/PuzzlerSerializer.cpp +++ b/Source/PuzzlerSerializer.cpp
@@ -11,21 +11,52 @@ Puzzle PuzzleSerializer::ReadPuzzle(int id) {
11 int height = 2 * _memory->ReadPanelData<int>(id, GRID_SIZE_Y, 1)[0] - 1; 11 int height = 2 * _memory->ReadPanelData<int>(id, GRID_SIZE_Y, 1)[0] - 1;
12 if (width < 0 || height < 0) return Puzzle(); // @Error: Grid size should be always positive? Looks like the starting panels break this rule, though. 12 if (width < 0 || height < 0) return Puzzle(); // @Error: Grid size should be always positive? Looks like the starting panels break this rule, though.
13 13
14 int numIntersections = _memory->ReadPanelData<int>(id, NUM_DOTS, 1)[0];
15 _intersectionFlags = _memory->ReadArray<int>(id, DOT_FLAGS, numIntersections);
16 int numConnections = _memory->ReadPanelData<int>(id, NUM_CONNECTIONS, 1)[0];
17 _connectionsA = _memory->ReadArray<int>(id, DOT_CONNECTION_A, numConnections);
18 _connectionsB = _memory->ReadArray<int>(id, DOT_CONNECTION_B, numConnections);
19 _intersectionLocations = _memory->ReadArray<float>(id, DOT_POSITIONS, numIntersections*2);
20
14 Puzzle p; 21 Puzzle p;
15 p.NewGrid(width, height); 22 p.NewGrid(width, height);
16 ReadIntersections(p, id); 23 ReadIntersections(p);
24 ReadExtras(p);
17 ReadDecorations(p, id); 25 ReadDecorations(p, id);
26 ReadSequence(p, id);
18 return p; 27 return p;
19} 28}
20 29
21void PuzzleSerializer::ReadIntersections(Puzzle& p, int id) { 30void PuzzleSerializer::WritePuzzle(const Puzzle& p, int id) {
22 int numIntersections = _memory->ReadPanelData<int>(id, NUM_DOTS, 1)[0]; 31 _intersectionFlags.clear();
23 std::vector<int> intersectionFlags = _memory->ReadArray<int>(id, DOT_FLAGS, numIntersections); 32 _connectionsA.clear();
24 int numConnections = _memory->ReadPanelData<int>(id, NUM_CONNECTIONS, 1)[0]; 33 _connectionsB.clear();
25 std::vector<int> connections_a = _memory->ReadArray<int>(id, DOT_CONNECTION_A, numConnections); 34 _intersectionLocations.clear();
26 std::vector<int> connections_b = _memory->ReadArray<int>(id, DOT_CONNECTION_B, numConnections); 35
27 std::vector<float> intersectionLocations = _memory->ReadArray<float>(id, DOT_POSITIONS, numIntersections*2); 36 MIN = 0.1f;
37 MAX = 0.9f;
38 WIDTH_INTERVAL = (MAX - MIN) / (p.width/2);
39 HEIGHT_INTERVAL = (MAX - MIN) / (p.height/2);
40 HORIZ_GAP_SIZE = WIDTH_INTERVAL / 2;
41 VERTI_GAP_SIZE = HEIGHT_INTERVAL / 2;
42
43 WriteIntersections(p);
44 WriteEndpoints(p);
45 WriteDecorations(p, id);
46 WriteSequence(p, id);
47
48 _memory->WritePanelData<int>(id, GRID_SIZE_X, {(p.width + 1)/2});
49 _memory->WritePanelData<int>(id, GRID_SIZE_Y, {(p.height + 1)/2});
50 _memory->WritePanelData<int>(id, NUM_DOTS, {static_cast<int>(_intersectionFlags.size())});
51 _memory->WriteArray<float>(id, DOT_POSITIONS, _intersectionLocations);
52 _memory->WriteArray<int>(id, DOT_FLAGS, _intersectionFlags);
53 _memory->WritePanelData<int>(id, NUM_CONNECTIONS, {static_cast<int>(_connectionsA.size())});
54 _memory->WriteArray<int>(id, DOT_CONNECTION_A, _connectionsA);
55 _memory->WriteArray<int>(id, DOT_CONNECTION_B, _connectionsB);
56 _memory->WritePanelData<int>(id, NEEDS_REDRAW, {1});
57}
28 58
59void PuzzleSerializer::ReadIntersections(Puzzle& p) {
29 // @Cleanup: Change defaults? 60 // @Cleanup: Change defaults?
30 for (int x=0; x<p.width; x++) { 61 for (int x=0; x<p.width; x++) {
31 for (int y=0; y<p.height; y++) { 62 for (int y=0; y<p.height; y++) {
@@ -34,14 +65,14 @@ void PuzzleSerializer::ReadIntersections(Puzzle& p, int id) {
34 } 65 }
35 } 66 }
36 67
37 for (int j=0; j<numIntersections; j++) { 68 for (int j=0; j<_intersectionFlags.size(); j++) {
38 if (intersectionFlags[connections_a[j]] & Flags::IS_ENDPOINT) break; 69 if (_intersectionFlags[_connectionsA[j]] & Flags::IS_ENDPOINT) break;
39 if (intersectionFlags[connections_b[j]] & Flags::IS_ENDPOINT) break; 70 if (_intersectionFlags[_connectionsB[j]] & Flags::IS_ENDPOINT) break;
40 float x1 = intersectionLocations[2*connections_a[j]]; 71 float x1 = _intersectionLocations[2*_connectionsA[j]];
41 float y1 = intersectionLocations[2*connections_a[j]+1]; 72 float y1 = _intersectionLocations[2*_connectionsA[j]+1];
42 float x2 = intersectionLocations[2*connections_b[j]]; 73 float x2 = _intersectionLocations[2*_connectionsB[j]];
43 float y2 = intersectionLocations[2*connections_b[j]+1]; 74 float y2 = _intersectionLocations[2*_connectionsB[j]+1];
44 auto [x, y] = loc_to_xy(p, connections_a[j]); 75 auto [x, y] = loc_to_xy(p, _connectionsA[j]);
45 76
46 if (x1 < x2) x++; 77 if (x1 < x2) x++;
47 else if (x1 > x2) x--; 78 else if (x1 > x2) x--;
@@ -49,13 +80,15 @@ void PuzzleSerializer::ReadIntersections(Puzzle& p, int id) {
49 else if (y1 > y2) y++; 80 else if (y1 > y2) y++;
50 p.grid[x][y].gap = Cell::Gap::NONE; 81 p.grid[x][y].gap = Cell::Gap::NONE;
51 } 82 }
83}
52 84
85void PuzzleSerializer::ReadExtras(Puzzle& p) {
53 // This iterates bottom-top, left-right 86 // This iterates bottom-top, left-right
54 int i = 0; 87 int i = 0;
55 for (;; i++) { 88 for (; i < _intersectionFlags.size(); i++) {
56 int flags = intersectionFlags[i]; 89 int flags = _intersectionFlags[i];
57 auto [x, y] = loc_to_xy(p, i); 90 auto [x, y] = loc_to_xy(p, i);
58 if (y < 0) break; 91 if (y < 0) break; // This is the expected exit point
59 if (flags & Flags::IS_STARTPOINT) { 92 if (flags & Flags::IS_STARTPOINT) {
60 p.grid[x][y].start = true; 93 p.grid[x][y].start = true;
61 } 94 }
@@ -66,31 +99,31 @@ void PuzzleSerializer::ReadIntersections(Puzzle& p, int id) {
66 } 99 }
67 100
68 // Iterate the remaining intersections (endpoints, dots, gaps) 101 // Iterate the remaining intersections (endpoints, dots, gaps)
69 for (; i < numIntersections; i++) { 102 for (; i < _intersectionFlags.size(); i++) {
70 int location = FindConnection(i, connections_a, connections_b); 103 int location = FindConnection(i);
71 if (location == -1) continue; // @Error: Unable to find connection point 104 if (location == -1) continue; // @Error: Unable to find connection point
72 // (x1, y1) location of this intersection 105 // (x1, y1) location of this intersection
73 // (x2, y2) location of the connected intersection 106 // (x2, y2) location of the connected intersection
74 float x1 = intersectionLocations[2*i]; 107 float x1 = _intersectionLocations[2*i];
75 float y1 = intersectionLocations[2*i+1]; 108 float y1 = _intersectionLocations[2*i+1];
76 float x2 = intersectionLocations[2*location]; 109 float x2 = _intersectionLocations[2*location];
77 float y2 = intersectionLocations[2*location+1]; 110 float y2 = _intersectionLocations[2*location+1];
78 auto [x, y] = loc_to_xy(p, location); 111 auto [x, y] = loc_to_xy(p, location);
79 112
80 if (intersectionFlags[i] & Flags::IS_ENDPOINT) { 113 if (_intersectionFlags[i] & Flags::IS_ENDPOINT) {
81 // Our x coordinate is less than the target's 114 // Our x coordinate is less than the target's
82 if (x1 < x2) p.grid[x][y].end = Cell::Dir::LEFT; 115 if (x1 < x2) p.grid[x][y].end = Cell::Dir::LEFT;
83 else if (x1 > x2) p.grid[x][y].end = Cell::Dir::RIGHT; 116 else if (x1 > x2) p.grid[x][y].end = Cell::Dir::RIGHT;
84 // Note that Y coordinates are reversed: 0.0 (bottom) 1.0 (top) 117 // Note that Y coordinates are reversed: 0.0 (bottom) 1.0 (top)
85 else if (y1 < y2) p.grid[x][y].end = Cell::Dir::DOWN; 118 else if (y1 < y2) p.grid[x][y].end = Cell::Dir::DOWN;
86 else if (y1 > y2) p.grid[x][y].end = Cell::Dir::UP; 119 else if (y1 > y2) p.grid[x][y].end = Cell::Dir::UP;
87 } else if (intersectionFlags[i] & Flags::HAS_DOT) { 120 } else if (_intersectionFlags[i] & Flags::HAS_DOT) {
88 if (x1 < x2) x--; 121 if (x1 < x2) x--;
89 else if (x1 > x2) x++; 122 else if (x1 > x2) x++;
90 else if (y1 < y2) y++; 123 else if (y1 < y2) y++;
91 else if (y1 > y2) y--; 124 else if (y1 > y2) y--;
92 p.grid[x][y].dot = FlagsToDot(intersectionFlags[i]); 125 p.grid[x][y].dot = FlagsToDot(_intersectionFlags[i]);
93 } else if (intersectionFlags[i] & Flags::HAS_ONE_CONN) { 126 } else if (_intersectionFlags[i] & Flags::HAS_ONE_CONN) {
94 if (x1 < x2) x--; 127 if (x1 < x2) x--;
95 else if (x1 > x2) x++; 128 else if (x1 > x2) x++;
96 else if (y1 < y2) y++; 129 else if (y1 < y2) y++;
@@ -124,36 +157,24 @@ void PuzzleSerializer::ReadDecorations(Puzzle& p, int id) {
124 } 157 }
125} 158}
126 159
127void PuzzleSerializer::WritePuzzle(const Puzzle& p, int id) { 160void PuzzleSerializer::ReadSequence(Puzzle& p, int id) {
128 _memory->WritePanelData<int>(id, GRID_SIZE_X, {(p.width + 1)/2}); 161 int sequenceLength = _memory->ReadPanelData<int>(id, SEQUENCE_LEN, 1)[0];
129 _memory->WritePanelData<int>(id, GRID_SIZE_Y, {(p.height + 1)/2}); 162 std::vector<int> sequence = _memory->ReadArray<int>(id, SEQUENCE, sequenceLength);
130
131 WriteIntersections(p, id);
132 if (p.hasDecorations) WriteDecorations(p, id);
133 163
134 _memory->WritePanelData<int>(id, NEEDS_REDRAW, {1}); 164 for (int location : sequence) {
165 auto [x, y] = loc_to_xy(p, location);
166 p.sequence.emplace_back(Pos{x, y});
167 }
135} 168}
136 169
137void PuzzleSerializer::WriteIntersections(const Puzzle& p, int id) { 170void PuzzleSerializer::WriteIntersections(const Puzzle& p) {
138 std::vector<float> intersectionLocations;
139 std::vector<int> intersectionFlags;
140 std::vector<int> connections_a;
141 std::vector<int> connections_b;
142
143 float min = 0.1f;
144 float max = 0.9f;
145 float width_interval = (max - min) / (p.width/2);
146 float height_interval = (max - min) / (p.height/2);
147 float horiz_gap_size = width_interval / 2;
148 float verti_gap_size = height_interval / 2;
149
150 // @Cleanup: If I write directly to locations, then I can simplify this gross loop iterator. 171 // @Cleanup: If I write directly to locations, then I can simplify this gross loop iterator.
151 // int numIntersections = (p.width / 2 + 1) * (p.height / 2 + 1); 172 // int numIntersections = (p.width / 2 + 1) * (p.height / 2 + 1);
152 // Grided intersections 173 // Grided intersections
153 for (int y=p.height-1; y>=0; y-=2) { 174 for (int y=p.height-1; y>=0; y-=2) {
154 for (int x=0; x<p.width; x+=2) { 175 for (int x=0; x<p.width; x+=2) {
155 intersectionLocations.push_back(min + (x/2) * width_interval); 176 _intersectionLocations.push_back(MIN + (x/2) * WIDTH_INTERVAL);
156 intersectionLocations.push_back(max - (y/2) * height_interval); 177 _intersectionLocations.push_back(MAX - (y/2) * HEIGHT_INTERVAL);
157 int flags = 0; 178 int flags = 0;
158 if (p.grid[x][y].start) { 179 if (p.grid[x][y].start) {
159 flags |= Flags::IS_STARTPOINT; 180 flags |= Flags::IS_STARTPOINT;
@@ -180,43 +201,43 @@ void PuzzleSerializer::WriteIntersections(const Puzzle& p, int id) {
180 if (p.grid[x][y].end != Cell::Dir::NONE) numConnections++; 201 if (p.grid[x][y].end != Cell::Dir::NONE) numConnections++;
181 // Create connections for this intersection for bottom/left only. 202 // Create connections for this intersection for bottom/left only.
182 // Bottom connection 203 // Bottom connection
183 if (y > 0 && p.grid[x][y-1].gap == Cell::Gap::NONE) { 204 if (y > 0 && p.grid[x][y-1].gap != Cell::Gap::FULL) {
184 connections_a.push_back(xy_to_loc(p, x, y-2)); 205 _connectionsA.push_back(xy_to_loc(p, x, y-2));
185 connections_b.push_back(xy_to_loc(p, x, y)); 206 _connectionsB.push_back(xy_to_loc(p, x, y));
186 flags |= Flags::HAS_VERTI_CONN; 207 flags |= Flags::HAS_VERTI_CONN;
187 numConnections++; 208 numConnections++;
188 } 209 }
189 // Top connection 210 // Top connection
190 if (y < p.height - 1 && p.grid[x][y+1].gap == Cell::Gap::NONE) { 211 if (y < p.height - 1 && p.grid[x][y+1].gap != Cell::Gap::FULL) {
191 flags |= Flags::HAS_VERTI_CONN; 212 flags |= Flags::HAS_VERTI_CONN;
192 numConnections++; 213 numConnections++;
193 } 214 }
194 // Left connection 215 // Left connection
195 if (x > 0 && p.grid[x-1][y].gap == Cell::Gap::NONE) { 216 if (x > 0 && p.grid[x-1][y].gap != Cell::Gap::FULL) {
196 connections_a.push_back(xy_to_loc(p, x-2, y)); 217 _connectionsA.push_back(xy_to_loc(p, x-2, y));
197 connections_b.push_back(xy_to_loc(p, x, y)); 218 _connectionsB.push_back(xy_to_loc(p, x, y));
198 flags |= Flags::HAS_HORIZ_CONN; 219 flags |= Flags::HAS_HORIZ_CONN;
199 numConnections++; 220 numConnections++;
200 } 221 }
201 // Right connection 222 // Right connection
202 if (x < p.width - 1 && p.grid[x+1][y].gap == Cell::Gap::NONE) { 223 if (x < p.width - 1 && p.grid[x+1][y].gap != Cell::Gap::FULL) {
203 flags |= Flags::HAS_HORIZ_CONN; 224 flags |= Flags::HAS_HORIZ_CONN;
204 numConnections++; 225 numConnections++;
205 } 226 }
206 if (numConnections == 1) flags |= HAS_ONE_CONN; 227 if (numConnections == 1) flags |= HAS_ONE_CONN;
207 intersectionFlags.push_back(flags); 228 _intersectionFlags.push_back(flags);
208 } 229 }
209 } 230 }
231}
210 232
211 // Endpoints 233void PuzzleSerializer::WriteEndpoints(const Puzzle& p) {
212 for (int x=0; x<p.width; x++) { 234 for (int x=0; x<p.width; x++) {
213 for (int y=0; y<p.height; y++) { 235 for (int y=0; y<p.height; y++) {
214 if (p.grid[x][y].end == Cell::Dir::NONE) continue; 236 if (p.grid[x][y].end == Cell::Dir::NONE) continue;
215 connections_a.push_back(xy_to_loc(p, x, y)); // Target to connect to 237 _connectionsA.push_back(xy_to_loc(p, x, y)); // Target to connect to
216 connections_b.push_back(static_cast<int>(intersectionFlags.size())); // This endpoint 238 _connectionsB.push_back(static_cast<int>(_intersectionFlags.size())); // This endpoint
217 239
218 float xPos = min + (x/2) * width_interval; 240 auto [xPos, yPos] = xy_to_pos(p, x, y);
219 float yPos = max - (y/2) * height_interval;
220 switch (p.grid[x][y].end) { 241 switch (p.grid[x][y].end) {
221 case Cell::Dir::LEFT: 242 case Cell::Dir::LEFT:
222 xPos -= .05f; 243 xPos -= .05f;
@@ -231,13 +252,14 @@ void PuzzleSerializer::WriteIntersections(const Puzzle& p, int id) {
231 yPos -= .05f; 252 yPos -= .05f;
232 break; 253 break;
233 } 254 }
234 intersectionLocations.push_back(xPos); 255 _intersectionLocations.push_back(xPos);
235 intersectionLocations.push_back(yPos); 256 _intersectionLocations.push_back(yPos);
236 intersectionFlags.push_back(Flags::IS_ENDPOINT); 257 _intersectionFlags.push_back(Flags::IS_ENDPOINT);
237 } 258 }
238 } 259 }
260}
239 261
240 // Dots 262void PuzzleSerializer::WriteDots(const Puzzle& p) {
241 for (int x=0; x<p.width; x++) { 263 for (int x=0; x<p.width; x++) {
242 for (int y=0; y<p.height; y++) { 264 for (int y=0; y<p.height; y++) {
243 if (x%2 == y%2) continue; // Cells are invalid, intersections are already handled. 265 if (x%2 == y%2) continue; // Cells are invalid, intersections are already handled.
@@ -245,24 +267,23 @@ void PuzzleSerializer::WriteIntersections(const Puzzle& p, int id) {
245 267
246 // We need to introduce a new segment -- 268 // We need to introduce a new segment --
247 // Locate the segment we're breaking 269 // Locate the segment we're breaking
248 for (int i=0; i<connections_a.size(); i++) { 270 for (int i=0; i<_connectionsA.size(); i++) {
249 auto [x1, y1] = loc_to_xy(p, connections_a[i]); 271 auto [x1, y1] = loc_to_xy(p, _connectionsA[i]);
250 auto [x2, y2] = loc_to_xy(p, connections_b[i]); 272 auto [x2, y2] = loc_to_xy(p, _connectionsB[i]);
251 if ((x1+1 == x && x2-1 == x && y1 == y && y2 == y) || 273 if ((x1+1 == x && x2-1 == x && y1 == y && y2 == y) ||
252 (y1+1 == y && y2-1 == y && x1 == x && x2 == x)) { 274 (y1+1 == y && y2-1 == y && x1 == x && x2 == x)) {
253 int other_connection = connections_b[i]; 275 int other_connection = _connectionsB[i];
254 connections_b[i] = static_cast<int>(intersectionFlags.size()); // This endpoint 276 _connectionsB[i] = static_cast<int>(_intersectionFlags.size()); // This endpoint
255 277
256 connections_a.push_back(other_connection); 278 _connectionsA.push_back(other_connection);
257 connections_b.push_back(static_cast<int>(intersectionFlags.size())); // This endpoint 279 _connectionsB.push_back(static_cast<int>(_intersectionFlags.size())); // This endpoint
258 break; 280 break;
259 } 281 }
260 } 282 }
261 // Add this dot to the end 283 // Add this dot to the end
262 float xPos = min + (x/2.0f) * width_interval; 284 auto [xPos, yPos] = xy_to_pos(p, x, y);
263 float yPos = max - (y/2.0f) * height_interval; 285 _intersectionLocations.push_back(xPos);
264 intersectionLocations.push_back(xPos); 286 _intersectionLocations.push_back(yPos);
265 intersectionLocations.push_back(yPos);
266 287
267 int flags = Flags::HAS_DOT; 288 int flags = Flags::HAS_DOT;
268 switch (p.grid[x][y].dot) { 289 switch (p.grid[x][y].dot) {
@@ -278,56 +299,51 @@ void PuzzleSerializer::WriteIntersections(const Puzzle& p, int id) {
278 flags |= DOT_IS_INVISIBLE; 299 flags |= DOT_IS_INVISIBLE;
279 break; 300 break;
280 } 301 }
281 intersectionFlags.push_back(flags); 302 _intersectionFlags.push_back(flags);
282 } 303 }
283 } 304 }
305}
284 306
285 // Gaps 307void PuzzleSerializer::WriteGaps(const Puzzle& p) {
286 for (int x=0; x<p.width; x++) { 308 for (int x=0; x<p.width; x++) {
287 for (int y=0; y<p.height; y++) { 309 for (int y=0; y<p.height; y++) {
288 if (x%2 == y%2) continue; // Cells are invalid, intersections are already handled. 310 if (x%2 == y%2) continue; // Cells are invalid, intersections are already handled.
289 if (p.grid[x][y].gap != Cell::Gap::BREAK) continue; 311 if (p.grid[x][y].gap != Cell::Gap::BREAK) continue;
290 312
291 float xPos = min + (x/2.0f) * width_interval; 313 auto [xPos, yPos] = xy_to_pos(p, x, y);
292 float yPos = max - (y/2.0f) * height_interval;
293 // Reminder: Y goes from 0.0 (bottom) to 1.0 (top) 314 // Reminder: Y goes from 0.0 (bottom) to 1.0 (top)
294 if (x%2 == 0) { // Vertical gap 315 if (x%2 == 0) { // Vertical gap
295 connections_a.push_back(xy_to_loc(p, x, y-1)); 316 _connectionsA.push_back(xy_to_loc(p, x, y-1));
296 connections_b.push_back(static_cast<int>(intersectionFlags.size())); // This endpoint 317 _connectionsB.push_back(static_cast<int>(_intersectionFlags.size())); // This endpoint
297 intersectionLocations.push_back(xPos); 318 _intersectionLocations.push_back(xPos);
298 intersectionLocations.push_back(yPos + verti_gap_size / 2); 319 _intersectionLocations.push_back(yPos + VERTI_GAP_SIZE / 2);
299 intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN); 320 _intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN);
300 321
301 connections_a.push_back(xy_to_loc(p, x, y+1)); 322 _connectionsA.push_back(xy_to_loc(p, x, y+1));
302 connections_b.push_back(static_cast<int>(intersectionFlags.size())); // This endpoint 323 _connectionsB.push_back(static_cast<int>(_intersectionFlags.size())); // This endpoint
303 intersectionLocations.push_back(xPos); 324 _intersectionLocations.push_back(xPos);
304 intersectionLocations.push_back(yPos - verti_gap_size / 2); 325 _intersectionLocations.push_back(yPos - VERTI_GAP_SIZE / 2);
305 intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN); 326 _intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN);
306 } else if (y%2 == 0) { // Horizontal gap 327 } else if (y%2 == 0) { // Horizontal gap
307 connections_a.push_back(xy_to_loc(p, x-1, y)); 328 _connectionsA.push_back(xy_to_loc(p, x-1, y));
308 connections_b.push_back(static_cast<int>(intersectionFlags.size())); // This endpoint 329 _connectionsB.push_back(static_cast<int>(_intersectionFlags.size())); // This endpoint
309 intersectionLocations.push_back(xPos - horiz_gap_size / 2); 330 _intersectionLocations.push_back(xPos - HORIZ_GAP_SIZE / 2);
310 intersectionLocations.push_back(yPos); 331 _intersectionLocations.push_back(yPos);
311 intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN); 332 _intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN);
312 333
313 connections_a.push_back(xy_to_loc(p, x+1, y)); 334 _connectionsA.push_back(xy_to_loc(p, x+1, y));
314 connections_b.push_back(static_cast<int>(intersectionFlags.size())); // This endpoint 335 _connectionsB.push_back(static_cast<int>(_intersectionFlags.size())); // This endpoint
315 intersectionLocations.push_back(xPos + horiz_gap_size / 2); 336 _intersectionLocations.push_back(xPos + HORIZ_GAP_SIZE / 2);
316 intersectionLocations.push_back(yPos); 337 _intersectionLocations.push_back(yPos);
317 intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN); 338 _intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN);
318 } 339 }
319 } 340 }
320 } 341 }
321
322 _memory->WritePanelData<int>(id, NUM_DOTS, {static_cast<int>(intersectionFlags.size())});
323 _memory->WriteArray<float>(id, DOT_POSITIONS, intersectionLocations);
324 _memory->WriteArray<int>(id, DOT_FLAGS, intersectionFlags);
325 _memory->WritePanelData<int>(id, NUM_CONNECTIONS, {static_cast<int>(connections_a.size())});
326 _memory->WriteArray<int>(id, DOT_CONNECTION_A, connections_a);
327 _memory->WriteArray<int>(id, DOT_CONNECTION_B, connections_b);
328} 342}
329 343
330void PuzzleSerializer::WriteDecorations(const Puzzle& p, int id) { 344void PuzzleSerializer::WriteDecorations(const Puzzle& p, int id) {
345 if (!p.hasDecorations) return;
346
331 std::vector<int> decorations; 347 std::vector<int> decorations;
332 for (int y=p.height-2; y>0; y-=2) { 348 for (int y=p.height-2; y>0; y-=2) {
333 for (int x=1; x<p.width-1; x+=2) { 349 for (int x=1; x<p.width-1; x+=2) {
@@ -344,6 +360,21 @@ void PuzzleSerializer::WriteDecorations(const Puzzle& p, int id) {
344 _memory->WriteArray<int>(id, DECORATIONS, decorations); 360 _memory->WriteArray<int>(id, DECORATIONS, decorations);
345} 361}
346 362
363void PuzzleSerializer::WriteSequence(const Puzzle& p, int id) {
364 if (p.sequence.size() == 0) return;
365
366 std::vector<int> sequence;
367 for (Pos pos : p.sequence) {
368 // Only include intersections, the game does not treat segments as real objects
369 if (pos.x%2 == 0 && pos.y%2 == 0) {
370 sequence.emplace_back(xy_to_loc(p, pos.x, pos.y));
371 }
372 }
373
374 _memory->WritePanelData<int>(id, SEQUENCE_LEN, {static_cast<int>(sequence.size())});
375 _memory->WriteNewArray<int>(id, SEQUENCE, sequence);
376}
377
347std::tuple<int, int> PuzzleSerializer::loc_to_xy(const Puzzle& p, int location) const { 378std::tuple<int, int> PuzzleSerializer::loc_to_xy(const Puzzle& p, int location) const {
348 int height2 = (p.height - 1) / 2; 379 int height2 = (p.height - 1) / 2;
349 int width2 = (p.width + 1) / 2; 380 int width2 = (p.width + 1) / 2;
@@ -378,23 +409,25 @@ int PuzzleSerializer::xy_to_dloc(const Puzzle& p, int x, int y) const {
378 return rowsFromBottom * width2 + (x - 1)/2; 409 return rowsFromBottom * width2 + (x - 1)/2;
379} 410}
380 411
412std::tuple<float, float> PuzzleSerializer::xy_to_pos(const Puzzle& p, int x, int y) const {
413 return {
414 MIN + (x/2) * WIDTH_INTERVAL,
415 MAX - (y/2) * HEIGHT_INTERVAL
416 };
417}
418
381Cell::Dot PuzzleSerializer::FlagsToDot(int flags) const { 419Cell::Dot PuzzleSerializer::FlagsToDot(int flags) const {
382 if (!(flags & Flags::HAS_DOT)) return Cell::Dot::NONE; 420 if (!(flags & Flags::HAS_DOT)) return Cell::Dot::NONE;
383 if (flags & Flags::DOT_IS_BLUE) { 421 if (flags & Flags::DOT_IS_BLUE) return Cell::Dot::BLUE;
384 return Cell::Dot::BLUE; 422 else if (flags & Flags::DOT_IS_ORANGE) return Cell::Dot::YELLOW;
385 } else if (flags & Flags::DOT_IS_ORANGE) { 423 else if (flags & Flags::DOT_IS_INVISIBLE) return Cell::Dot::INVISIBLE;
386 return Cell::Dot::YELLOW; 424 else return Cell::Dot::BLACK;
387 } else if (flags & Flags::DOT_IS_INVISIBLE) {
388 return Cell::Dot::INVISIBLE;
389 } else {
390 return Cell::Dot::BLACK;
391 }
392} 425}
393 426
394int PuzzleSerializer::FindConnection(int i, const std::vector<int>& connections_a, const std::vector<int>& connections_b) const { 427int PuzzleSerializer::FindConnection(int location) const {
395 for (int j=0; j<connections_a.size(); j++) { 428 for (int j=0; j<_connectionsA.size(); j++) {
396 if (connections_a[j] == i) return connections_b[j]; 429 if (_connectionsA[j] == location) return _connectionsB[j];
397 if (connections_b[j] == i) return connections_a[j]; 430 if (_connectionsB[j] == location) return _connectionsA[j];
398 } 431 }
399 return -1; 432 return -1;
400} 433}
diff --git a/Source/PuzzlerSerializer.h b/Source/PuzzlerSerializer.h index 00c89f5..535d82f 100644 --- a/Source/PuzzlerSerializer.h +++ b/Source/PuzzlerSerializer.h
@@ -22,24 +22,40 @@ private:
22 DOT_IS_BLUE = 0x100, 22 DOT_IS_BLUE = 0x100,
23 DOT_IS_ORANGE = 0x200, 23 DOT_IS_ORANGE = 0x200,
24 DOT_IS_INVISIBLE = 0x1000, 24 DOT_IS_INVISIBLE = 0x1000,
25 HAS_ONE_CONN = 0x100000, 25 HAS_ONE_CONN = 0x100000,
26 HAS_VERTI_CONN = 0x200000, 26 HAS_VERTI_CONN = 0x200000,
27 HAS_HORIZ_CONN = 0x400000, 27 HAS_HORIZ_CONN = 0x400000,
28 }; 28 };
29 29
30 void ReadIntersections(Puzzle& p, int id); 30 void ReadIntersections(Puzzle& p);
31 void ReadExtras(Puzzle& p);
31 void ReadDecorations(Puzzle& p, int id); 32 void ReadDecorations(Puzzle& p, int id);
32 void WriteIntersections(const Puzzle& p, int id); 33 void ReadSequence(Puzzle& p, int id);
34
35 void WriteIntersections(const Puzzle& p);
36 void WriteDots(const Puzzle& p);
37 void WriteGaps(const Puzzle& p);
38 void WriteEndpoints(const Puzzle& p);
33 void WriteDecorations(const Puzzle& p, int id); 39 void WriteDecorations(const Puzzle& p, int id);
40 void WriteSequence(const Puzzle& p, int id);
34 41
35 std::tuple<int, int> loc_to_xy(const Puzzle& p, int location) const; 42 std::tuple<int, int> loc_to_xy(const Puzzle& p, int location) const;
36 int xy_to_loc(const Puzzle& p, int x, int y) const; 43 int xy_to_loc(const Puzzle& p, int x, int y) const;
37 // Decoration location 44 // Decoration location
38 std::tuple<int, int> dloc_to_xy(const Puzzle& p, int location) const; 45 std::tuple<int, int> dloc_to_xy(const Puzzle& p, int location) const;
39 int xy_to_dloc(const Puzzle& p, int x, int y) const; 46 int xy_to_dloc(const Puzzle& p, int x, int y) const;
47 // Grid coordinates
48 std::tuple<float, float> xy_to_pos(const Puzzle& p, int x, int y) const;
40 Cell::Dot FlagsToDot(int flags) const; 49 Cell::Dot FlagsToDot(int flags) const;
41 // Iterate connection lists for another location which is connected to us; return that other location. 50 // Iterate connection lists for another location which is connected to us; return that other location.
42 int FindConnection(int i, const std::vector<int>& connections_a, const std::vector<int>& connections_b) const; 51 int FindConnection(int location) const;
43 52
44 std::shared_ptr<Memory> _memory; 53 std::shared_ptr<Memory> _memory;
54
55 std::vector<float> _intersectionLocations;
56 std::vector<int> _intersectionFlags;
57 std::vector<int> _connectionsA;
58 std::vector<int> _connectionsB;
59
60 float MIN, MAX, WIDTH_INTERVAL, HEIGHT_INTERVAL, HORIZ_GAP_SIZE, VERTI_GAP_SIZE;
45}; 61};
diff --git a/Source/Randomizer2.cpp b/Source/Randomizer2.cpp index 537c30b..b5218bf 100644 --- a/Source/Randomizer2.cpp +++ b/Source/Randomizer2.cpp
@@ -3,6 +3,9 @@
3#include "Random.h" 3#include "Random.h"
4#include "Solver.h" 4#include "Solver.h"
5#include "Memory.h" 5#include "Memory.h"
6#include "PuzzlerSerializer.h"
7#include <cassert>
8#include <string>
6 9
7void FloodFillInternal(const Puzzle& p, std::vector<std::vector<bool>>& reached, int x, int y) { 10void FloodFillInternal(const Puzzle& p, std::vector<std::vector<bool>>& reached, int x, int y) {
8 if (x%2 == 1 && y%2 == 1) return; 11 if (x%2 == 1 && y%2 == 1) return;
@@ -62,10 +65,10 @@ void Randomizer2::Randomize() {
62 for (int j=0; j<edges.size(); j++) { 65 for (int j=0; j<edges.size(); j++) {
63 int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1)); 66 int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1));
64 Pos pos = edges[edge]; 67 Pos pos = edges[edge];
65 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
66 edges.erase(edges.begin() + edge); 68 edges.erase(edges.begin() + edge);
67 69
68 if (FloodFill(p)) { 70 if (FloodFill(p)) {
71 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
69 break; 72 break;
70 } else { 73 } else {
71 p.grid[pos.x][pos.y].gap = Cell::Gap::NONE; 74 p.grid[pos.x][pos.y].gap = Cell::Gap::NONE;
@@ -108,10 +111,10 @@ void Randomizer2::Randomize() {
108 111
109 int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1)); 112 int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1));
110 Pos pos = edges[edge]; 113 Pos pos = edges[edge];
111 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
112 edges.erase(edges.begin() + edge); 114 edges.erase(edges.begin() + edge);
113 115
114 if (FloodFill(p)) { 116 if (FloodFill(p)) {
117 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
115 break; 118 break;
116 } else { 119 } else {
117 p.grid[pos.x][pos.y].gap = Cell::Gap::NONE; 120 p.grid[pos.x][pos.y].gap = Cell::Gap::NONE;
@@ -152,19 +155,100 @@ void Randomizer2::Randomize() {
152 155
153} 156}
154 157
158void DebugColorGrid(const std::vector<std::vector<int>>& colorGrid) {
159 for (int y=0; y<colorGrid[0].size(); y++) {
160 std::wstring row;
161 for (int x=0; x<colorGrid.size(); x++) {
162 row += std::to_wstring(colorGrid[x][y]);
163 }
164 row += L'\n';
165 OutputDebugString(row.c_str());
166 }
167 OutputDebugString(L"\n");
168}
169
170void FloodFill(const Puzzle& p, std::vector<std::vector<int>>& colorGrid, int color, int x, int y) {
171 if (!p.SafeCell(x, y)) return;
172 if (colorGrid[x][y] != 0) return; // Already processed.
173 colorGrid[x][y] = color;
174
175 FloodFill(p, colorGrid, color, x, y+1);
176 FloodFill(p, colorGrid, color, x, y-1);
177 FloodFill(p, colorGrid, color, x+1, y);
178 FloodFill(p, colorGrid, color, x-1, y);
179}
180
181void FloodFillOutside(const Puzzle&p, std::vector<std::vector<int>>& colorGrid, int x, int y) {
182 if (!p.SafeCell(x, y)) return;
183 if (colorGrid[x][y] != 0) return; // Already processed.
184 if (x%2 != y%2 && p.grid[x][y].gap != Cell::Gap::FULL) return; // Only flood-fill through full gaps
185 colorGrid[x][y] = 1; // Outside color
186
187 FloodFillOutside(p, colorGrid, x, y+1);
188 FloodFillOutside(p, colorGrid, x, y-1);
189 FloodFillOutside(p, colorGrid, x+1, y);
190 FloodFillOutside(p, colorGrid, x-1, y);
191}
192
193/*
194 undefined -> 1 (color of outside) or * (any colored cell) or -1 (edge/intersection not part of any region)
195
196 0 -> {} (this is a special edge case, which I don't need right now)
197 1 -> 0 (uncolored / ready to color)
198 2 ->
199*/
200std::vector<std::vector<int>> CreateColorGrid(const Puzzle& p) {
201 std::vector<std::vector<int>> colorGrid;
202 colorGrid.resize(p.width);
203
204 for (int x=0; x<p.width; x++) {
205 colorGrid[x].resize(p.height);
206 for (int y=0; y<p.height; y++) {
207 // Mark all unbroken edges and intersections as 'do not color'
208 if (x%2 != y%2) {
209 if (p.grid[x][y].gap == Cell::Gap::NONE) colorGrid[x][y] = 1;
210 } else if (x%2 == 0 && y%2 == 0) {
211 // @Future: What about empty intersections?
212 colorGrid[x][y] = 1; // do not color intersections
213 }
214 }
215 }
216
217 // @Future: Skip this loop if pillar = true;
218 for (int y=1; y<p.height; y+=2) {
219 FloodFillOutside(p, colorGrid, 0, y);
220 FloodFillOutside(p, colorGrid, p.width - 1, y);
221 }
222
223 for (int x=1; x<p.width; x+=2) {
224 FloodFillOutside(p, colorGrid, x, 0);
225 FloodFillOutside(p, colorGrid, x, p.height - 1);
226 }
227
228 int color = 1;
229 for (int x=0; x<p.width; x++) {
230 for (int y=0; y<p.height; y++) {
231 if (colorGrid[x][y] != 0) continue; // No dead colors
232 color++;
233 FloodFill(p, colorGrid, color, x, y);
234 }
235 }
236
237 return colorGrid;
238}
239
155void Randomizer2::RandomizeKeep() { 240void Randomizer2::RandomizeKeep() {
156 Puzzle p; 241 Puzzle p;
157 p.width = 9; 242 p.NewGrid(4, 4);
158 p.height = 10; 243 // p.width = 9;
159 p.grid.clear(); 244 // p.height = 10;
160 p.grid.resize(p.width); 245 // p.grid.clear();
161 for (int x=0; x<p.width; x++) p.grid[x].resize(p.height); 246 // p.grid.resize(p.width);
247 // for (int x=0; x<p.width; x++) p.grid[x].resize(p.height);
162 248
163 p.NewGrid(5, 5);
164 p.grid[2][1].gap = Cell::Gap::FULL; 249 p.grid[2][1].gap = Cell::Gap::FULL;
165 p.grid[4][1].gap = Cell::Gap::FULL; 250 p.grid[4][1].gap = Cell::Gap::FULL;
166 p.grid[6][1].gap = Cell::Gap::FULL; 251 p.grid[6][1].gap = Cell::Gap::FULL;
167 p.grid[8][1].gap = Cell::Gap::FULL;
168 p.grid[3][2].gap = Cell::Gap::FULL; 252 p.grid[3][2].gap = Cell::Gap::FULL;
169 p.grid[5][2].gap = Cell::Gap::FULL; 253 p.grid[5][2].gap = Cell::Gap::FULL;
170 p.grid[8][3].gap = Cell::Gap::FULL; 254 p.grid[8][3].gap = Cell::Gap::FULL;
@@ -173,45 +257,92 @@ void Randomizer2::RandomizeKeep() {
173 p.grid[7][6].gap = Cell::Gap::FULL; 257 p.grid[7][6].gap = Cell::Gap::FULL;
174 p.grid[2][7].gap = Cell::Gap::FULL; 258 p.grid[2][7].gap = Cell::Gap::FULL;
175 p.grid[4][7].gap = Cell::Gap::FULL; 259 p.grid[4][7].gap = Cell::Gap::FULL;
176 p.grid[0][9].gap = Cell::Gap::FULL; 260 // p.grid[0][9].gap = Cell::Gap::FULL;
177 p.grid[2][9].gap = Cell::Gap::FULL; 261 // p.grid[2][9].gap = Cell::Gap::FULL;
178 p.grid[6][9].gap = Cell::Gap::FULL; 262 // p.grid[6][9].gap = Cell::Gap::FULL;
179 p.grid[8][9].gap = Cell::Gap::FULL; 263 // p.grid[8][9].gap = Cell::Gap::FULL;
180 264
181 p.grid[8][0].end = Cell::Dir::UP; 265 p.grid[6][0].end = Cell::Dir::UP;
182 p.grid[4][9].start = true; 266 p.grid[4][8].start = true;
183 267
184 // 0x00496 268 auto colorGrid = CreateColorGrid(p);
185 // 0x00344
186 // 0x00495
187 // 0x00488
188 // 0x00489
189 269
190 SetGate(0x00496, 2, 1); 270 std::vector<Pos> edges;
191} 271 for (int x=0; x<p.width; x++) {
272 for (int y=0; y<p.height; y++) {
273 if (x%2 == y%2) continue;
274 if (p.grid[x][y].gap != Cell::Gap::NONE) continue;
275 edges.emplace_back(Pos{x, y});
276 }
277 }
192 278
193void Randomizer2::SetGate(int panel, int X, int Y) { 279 Puzzle copy = p;
194 // x: (-1.5)X + (0.7)Y + 67.1 280 std::vector<int> gates = {0x00344, 0x00488, 0x00489, 0x00495, 0x00496};
195 // y: (0.3)X + (1.5)Y + 106.9 281 for (int i=0; i<5; i++) {
196 // z: -19.1 282 for (int j=0; j<edges.size(); j++) {
197 // Horizontal: (0, 0, -.1, 1) 283 int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1));
198 // Vertical: (0, 0, -.77, .63) 284 Pos pos = edges[edge];
285 edges.erase(edges.begin() + edge);
286
287 int color1 = 0;
288 int color2 = 0;
289 if (pos.x%2 == 0 && pos.y%2 == 1) { // Vertical
290 if (pos.x > 0) color1 = colorGrid[pos.x-1][pos.y];
291 else color1 = 1;
292
293 if (pos.x < p.width - 1) color2 = colorGrid[pos.x+1][pos.y];
294 else color2 = 1;
295 } else { // Horizontal
296 assert(pos.x%2 == 1 && pos.y%2 == 0);
297 if (pos.y > 0) color1 = colorGrid[pos.x][pos.y-1];
298 else color1 = 1;
299
300 if (pos.y < p.height - 1) color2 = colorGrid[pos.x][pos.y+1];
301 else color2 = 1;
302 }
303 // Enforce color1 < color2
304 if (color1 > color2) std::swap(color1, color2);
305
306 // Colors mismatch, valid cut
307 if (color1 != color2) {
308 // @Performance... have a lookup table instead?
309 for (int x=0; x<p.width; x++) {
310 for (int y=0; y<p.height; y++) {
311 if (colorGrid[x][y] == color2) colorGrid[x][y] = color1;
312 }
313 }
314 copy.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
315 SetGate(gates[i], pos.x, pos.y);
316 break;
317 }
318 }
319 }
199 320
200 float x = -1.5f * X + 0.7f * Y + 67.1f; 321 auto solutions = Solver::Solve(copy);
201 float y = 0.3f * X + 1.5f * Y + 106.9f; 322 assert(solutions.size() == 1);
202 _memory->WritePanelData<float>(panel, POSITION, {x, y, 19.1f}); 323 p.sequence = solutions[0].sequence;
324 PuzzleSerializer(_memory).WritePuzzle(solutions[0], 0x139);
325}
203 326
204 float z, w; 327void Randomizer2::SetGate(int panel, int X, int Y) {
328 float x, y, z, w;
205 if (X%2 == 0 && Y%2 == 1) { // Horizontal 329 if (X%2 == 0 && Y%2 == 1) { // Horizontal
206 z = -0.1f; 330 x = -1.49f * X + 0.22f * Y + 66.58f;
207 w = 1.0f; 331 y = 0.275f * X + 1.6f * Y + 108.4f;
208 } else if (X%2 == 1 && Y%2 == 0) { // Vertical
209 z = -.77f; 332 z = -.77f;
210 w = .63f; 333 w = .63f;
211 } else { 334 } else { // Vertical
212 assert(false); 335 assert(X%2 == 1 && Y%2 == 0);
213 return; 336 x = -1.6f * X + 0.35f * Y + 66.5f;
337 y = 0.25f * X + 1.6f * Y + 108.55f;
338 z = -0.1f;
339 w = 1.0f;
214 } 340 }
215 341
342 SetPos(panel, x, y, 19.2f);
216 _memory->WritePanelData<float>(panel, ORIENTATION, {0.0f, 0.0f, z, w}); 343 _memory->WritePanelData<float>(panel, ORIENTATION, {0.0f, 0.0f, z, w});
344}
345
346void Randomizer2::SetPos(int panel, float x, float y, float z) {
347 _memory->WritePanelData<float>(panel, POSITION, {x, y, z});
217} \ No newline at end of file 348} \ No newline at end of file
diff --git a/Source/Randomizer2.h b/Source/Randomizer2.h index 848fd22..359e357 100644 --- a/Source/Randomizer2.h +++ b/Source/Randomizer2.h
@@ -10,6 +10,7 @@ public:
10 10
11private: 11private:
12 void SetGate(int panel, int X, int Y); 12 void SetGate(int panel, int X, int Y);
13 void SetPos(int panel, float x, float y, float z);
13 14
14 std::shared_ptr<Memory> _memory; 15 std::shared_ptr<Memory> _memory;
15}; 16};
diff --git a/Source/Solver.cpp b/Source/Solver.cpp index bce1482..a8710a2 100644 --- a/Source/Solver.cpp +++ b/Source/Solver.cpp
@@ -30,6 +30,7 @@ void Solver::SolveLoop(Puzzle& p, int x, int y, std::vector<Puzzle>& solutions)
30 if (p.sym == Puzzle::Symmetry::NONE) { 30 if (p.sym == Puzzle::Symmetry::NONE) {
31 if (cell.color != Cell::Color::NONE) return; // Collided with ourselves 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 32 p.grid[x][y].color = Cell::Color::BLACK; // Otherwise, mark this cell as visited
33 p.sequence.emplace_back(Pos{x, y});
33 } else { 34 } else {
34 /* 35 /*
35 // Get the symmetrical position, and try coloring it 36 // Get the symmetrical position, and try coloring it
@@ -69,6 +70,7 @@ void Solver::SolveLoop(Puzzle& p, int x, int y, std::vector<Puzzle>& solutions)
69 70
70 // Tail recursion: Back out of this cell 71 // Tail recursion: Back out of this cell
71 p.grid[x][y].color = Cell::Color::NONE; 72 p.grid[x][y].color = Cell::Color::NONE;
73 p.sequence.pop_back();
72 if (p.sym != Puzzle::Symmetry::NONE) { 74 if (p.sym != Puzzle::Symmetry::NONE) {
73 /* 75 /*
74 auto sym = p.GetSymmetricalPos(x, y); 76 auto sym = p.GetSymmetricalPos(x, y);