summary refs log tree commit diff stats
path: root/Source/Panel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Panel.cpp')
-rw-r--r--Source/Panel.cpp337
1 files changed, 193 insertions, 144 deletions
diff --git a/Source/Panel.cpp b/Source/Panel.cpp index 43e9763..b6f0403 100644 --- a/Source/Panel.cpp +++ b/Source/Panel.cpp
@@ -1,108 +1,36 @@
1#include "Panel.h" 1#include "Panel.h"
2#include "Random.h"
3#include "Memory.h" 2#include "Memory.h"
4#include "Randomizer.h"
5#include <sstream>
6 3
7#pragma warning (disable:26451) 4#pragma warning (disable:26451)
5#pragma warning (disable:26812)
8 6
9template <class T> 7PuzzleSerializer::PuzzleSerializer(const std::shared_ptr<Memory>& memory) : _memory(memory) {}
10int find(const std::vector<T> &data, T search, size_t startIndex = 0) {
11 for (size_t i=startIndex ; i<data.size(); i++) {
12 if (data[i] == search) return static_cast<int>(i);
13 }
14 return -1;
15}
16
17Panel::Panel(const std::shared_ptr<Memory>& memory, int id) : _memory(memory) {
18 _width = 2 * _memory->ReadPanelData<int>(id, GRID_SIZE_X, 1)[0] - 1;
19 _height = 2 * _memory->ReadPanelData<int>(id, GRID_SIZE_Y, 1)[0] - 1;
20 _grid.resize(_width);
21 for (auto& row : _grid) row.resize(_height);
22
23 ReadIntersections(id);
24 ReadDecorations(id);
25}
26
27void Panel::Write(int id) {
28 WriteIntersections(id);
29 WriteDecorations(id);
30
31 _memory->WritePanelData<int>(id, GRID_SIZE_X, {(_width + 1)/2});
32 _memory->WritePanelData<int>(id, GRID_SIZE_Y, {(_height + 1)/2});
33}
34
35nlohmann::json Panel::Serialize() {
36 nlohmann::json puzzle = {
37 {"pillar", false},
38 {"dots", nlohmann::json::array()},
39 {"gaps", nlohmann::json::array()},
40 {"name", "Imported from The Witness :O"},
41 {"regionCache", nlohmann::json::object()},
42 };
43 if (_grid.empty()) return {};
44 puzzle["grid"] = nlohmann::json::array();
45
46 for (int x=0; x<_width; x++) {
47 for (int y=0; y<_height; y++) {
48 if (x%2 == 1 && y%2 == 1) {
49 puzzle["grid"][x][y] = Decoration_to_json(_grid[x][y]);
50 } else {
51 if (_grid[x][y] & IntersectionFlags::HAS_DOT) {
52 puzzle["dots"].emplace_back(nlohmann::json({{"x", x}, {"y", y}}));
53 }
54 puzzle["grid"][x][y] = false;
55 }
56 }
57 }
58
59 puzzle["startPoints"] = nlohmann::json::array();
60 for (auto [x, y] : _startpoints) {
61 nlohmann::json startPoint = {{"x", x}, {"y", y}};
62 puzzle["startPoints"].emplace_back(startPoint);
63 }
64 puzzle["endPoints"] = nlohmann::json::array();
65 for (Endpoint endpoint : _endpoints) {
66 puzzle["endPoints"].emplace_back(endpoint.to_json());
67 }
68
69 std::string out = puzzle.dump();
70 return puzzle;
71}
72 8
73void Panel::ReadDecorations(int id) { 9Puzzle PuzzleSerializer::ReadPuzzle(int id) {
74 int numDecorations = _memory->ReadPanelData<int>(id, NUM_DECORATIONS, 1)[0]; 10 Puzzle p;
75 std::vector<int> decorations = _memory->ReadArray<int>(id, DECORATIONS, numDecorations); 11 p.width = 2 * _memory->ReadPanelData<int>(id, GRID_SIZE_X, 1)[0] - 1;
12 p.height = 2 * _memory->ReadPanelData<int>(id, GRID_SIZE_Y, 1)[0] - 1;
13 p.grid.resize(p.width);
14 for (auto& row : p.grid) row.resize(p.height);
76 15
77 for (int i=0; i<numDecorations; i++) { 16 ReadIntersections(p, id);
78 auto [x, y] = dloc_to_xy(i); 17 ReadDecorations(p, id);
79 _grid[x][y] = decorations[i];
80 }
81}
82
83void Panel::WriteDecorations(int id) {
84 std::vector<int> decorations;
85 for (int y=_height-2; y>0; y-=2) {
86 for (int x=1; x<_width - 1; x+=2) {
87 decorations.push_back(_grid[x][y]);
88 }
89 }
90 18
91 _memory->WritePanelData<int>(id, NUM_DECORATIONS, {static_cast<int>(decorations.size())}); 19 return p;
92 _memory->WriteArray<int>(id, DECORATIONS, decorations);
93} 20}
94 21
95void Panel::ReadIntersections(int id) { 22void PuzzleSerializer::ReadIntersections(Puzzle& p, int id) {
96 int numIntersections = _memory->ReadPanelData<int>(id, NUM_DOTS, 1)[0]; 23 int numIntersections = _memory->ReadPanelData<int>(id, NUM_DOTS, 1)[0];
97 std::vector<int> intersectionFlags = _memory->ReadArray<int>(id, DOT_FLAGS, numIntersections); 24 std::vector<int> intersectionFlags = _memory->ReadArray<int>(id, DOT_FLAGS, numIntersections);
98 25
99 int i = 0; 26 int i = 0;
100 for (;; i++) { 27 for (;; i++) {
101 auto [x, y] = loc_to_xy(i); 28 auto [x, y] = loc_to_xy(p, i);
102 if (y < 0) break; 29 if (y < 0) break;
103 if (intersectionFlags[i] & IntersectionFlags::IS_STARTPOINT) { 30 if (intersectionFlags[i] & Flags::IS_STARTPOINT) {
104 _startpoints.push_back({x, y}); 31 p.grid[x][y].start = true;
105 } 32 }
33 p.grid[x][y].dot = FlagsToDot(intersectionFlags[i]);
106 } 34 }
107 35
108 int numConnections = _memory->ReadPanelData<int>(id, NUM_CONNECTIONS, 1)[0]; 36 int numConnections = _memory->ReadPanelData<int>(id, NUM_CONNECTIONS, 1)[0];
@@ -112,34 +40,32 @@ void Panel::ReadIntersections(int id) {
112 40
113 // Iterate the remaining intersections (endpoints, dots, gaps) 41 // Iterate the remaining intersections (endpoints, dots, gaps)
114 for (; i < numIntersections; i++) { 42 for (; i < numIntersections; i++) {
115 if (intersectionFlags[i] & IntersectionFlags::IS_ENDPOINT) { 43 if (intersectionFlags[i] & Flags::IS_ENDPOINT) {
116 for (int j=0; j<numConnections; j++) { 44 for (int j=0; j<numConnections; j++) {
117 int location = 0; 45 int location = 0;
118 if (connections_a[j] == i) location = connections_b[j]; 46 if (connections_a[j] == i) location = connections_b[j];
119 if (connections_b[j] == i) location = connections_a[j]; 47 if (connections_b[j] == i) location = connections_a[j];
120 if (location != 0) { 48 if (location != 0) {
121 Endpoint::Direction dir; 49 auto [x, y] = loc_to_xy(p, location);
122 if (intersections[2*i] < intersections[2*location]) { // Our (i) x coordinate is less than the target's (location) 50 if (intersections[2*i] < intersections[2*location]) { // Our (i) x coordinate is less than the target's (location)
123 dir = Endpoint::Direction::LEFT; 51 p.grid[x][y].end = Cell::Dir::LEFT;
124 } else if (intersections[2*i] > intersections[2*location]) { 52 } else if (intersections[2*i] > intersections[2*location]) {
125 dir = Endpoint::Direction::RIGHT; 53 p.grid[x][y].end = Cell::Dir::RIGHT;
126 } else if (intersections[2*i + 1] > intersections[2*location + 1]) { // y coordinate is 0 (bottom) 1 (top), so this check is reversed. 54 } else if (intersections[2*i + 1] > intersections[2*location + 1]) { // y coordinate is 0 (bottom) 1 (top), so this check is reversed.
127 dir = Endpoint::Direction::UP; 55 p.grid[x][y].end = Cell::Dir::UP;
128 } else { 56 } else {
129 dir = Endpoint::Direction::DOWN; 57 p.grid[x][y].end = Cell::Dir::DOWN;
130 } 58 }
131 auto [x, y] = loc_to_xy(location);
132 _endpoints.push_back(Endpoint(x, y, dir));
133 break; 59 break;
134 } 60 }
135 } 61 }
136 } else if (intersectionFlags[i] & IntersectionFlags::HAS_DOT) { 62 } else if (intersectionFlags[i] & Flags::HAS_DOT) {
137 for (int j=0; j<numConnections; j++) { 63 for (int j=0; j<numConnections; j++) {
138 int location = 0; 64 int location = 0;
139 if (connections_a[j] == i) location = connections_b[j]; 65 if (connections_a[j] == i) location = connections_b[j];
140 if (connections_b[j] == i) location = connections_a[j]; 66 if (connections_b[j] == i) location = connections_a[j];
141 if (location != 0) { 67 if (location != 0) {
142 auto [x, y] = loc_to_xy(location); 68 auto [x, y] = loc_to_xy(p, location);
143 float x1 = intersections[2*i]; 69 float x1 = intersections[2*i];
144 float y1 = intersections[2*i+1]; 70 float y1 = intersections[2*i+1];
145 float x2 = intersections[2*location]; 71 float x2 = intersections[2*location];
@@ -156,7 +82,7 @@ void Panel::ReadIntersections(int id) {
156 y++; 82 y++;
157 } 83 }
158 84
159 _grid[x][y] |= IntersectionFlags::HAS_DOT; 85 p.grid[x][y].dot = FlagsToDot(intersectionFlags[i]);
160 break; 86 break;
161 } 87 }
162 } 88 }
@@ -164,72 +90,134 @@ void Panel::ReadIntersections(int id) {
164 } 90 }
165} 91}
166 92
167void Panel::WriteIntersections(int id) { 93void PuzzleSerializer::ReadDecorations(Puzzle& p, int id) {
94 int numDecorations = _memory->ReadPanelData<int>(id, NUM_DECORATIONS, 1)[0];
95 std::vector<int> decorations = _memory->ReadArray<int>(id, DECORATIONS, numDecorations);
96 if (numDecorations != decorations.size()) return; // @Error!
97
98 for (int i=0; i<numDecorations; i++) {
99 auto [x, y] = dloc_to_xy(p, i);
100 auto d = std::make_shared<Decoration>();
101 p.grid[x][y].decoration = d;
102 d->type = static_cast<Shape>(decorations[i] & 0xFF00);
103 switch(d->type) {
104 case Shape::Poly:
105 case Shape::RPoly:
106 case Shape::Ylop:
107 d->polyshape = decorations[i] & 0xFFFF0000;
108 break;
109 case Shape::Triangle:
110 d->count = decorations[i] & 0x000F0000;
111 break;
112 }
113 d->color = static_cast<Color>(decorations[i] & 0xF);
114 }
115}
116
117void PuzzleSerializer::WritePuzzle(const Puzzle& p, int id) {
118 _memory->WritePanelData<int>(id, GRID_SIZE_X, {(p.width + 1)/2});
119 _memory->WritePanelData<int>(id, GRID_SIZE_Y, {(p.height + 1)/2});
120
121 WriteIntersections(p, id);
122 WriteDecorations(p, id);
123
124 _memory->WritePanelData<int>(id, NEEDS_REDRAW, {1});
125}
126
127void PuzzleSerializer::WriteIntersections(const Puzzle& p, int id) {
168 std::vector<float> intersections; 128 std::vector<float> intersections;
169 std::vector<int> intersectionFlags; 129 std::vector<int> intersectionFlags;
170 std::vector<int> connections_a; 130 std::vector<int> connections_a;
171 std::vector<int> connections_b; 131 std::vector<int> connections_b;
172 132
173 double min = 0.1; 133 float min = 0.1f;
174 double max = 0.9; 134 float max = 0.9f;
175 double width_interval = (max - min) / (_width/2); 135 float width_interval = (max - min) / (p.width/2);
176 double height_interval = (max - min) / (_height/2); 136 float height_interval = (max - min) / (p.height/2);
177 137
178 // TODO(future): Stop using push_back and set these into explicit locations, unrequires loop iteration order 138 // @Cleanup: If I write directly to locations, then I can remove this gross loop iterator.
179 for (int y=_height-1; y>=0; y-=2) { 139 for (int y=p.height-1; y>=0; y-=2) {
180 for (int x=0; x<_width; x+=2) { 140 for (int x=0; x<p.width; x+=2) {
181 intersections.push_back(static_cast<float>(min + (x/2) * width_interval)); 141 intersections.push_back(static_cast<float>(min + (x/2) * width_interval));
182 intersections.push_back(static_cast<float>(max - (y/2) * height_interval)); 142 intersections.push_back(static_cast<float>(max - (y/2) * height_interval));
183 int flags = 0; 143 int flags = 0;
184 if (find(_startpoints, {x, y}) != -1) flags |= IntersectionFlags::IS_STARTPOINT; 144 if (p.grid[x][y].start) {
145 flags |= Flags::IS_STARTPOINT;
146 }
185 intersectionFlags.push_back(flags); 147 intersectionFlags.push_back(flags);
186 148
187 // Create connections for this intersection -- always write low -> high 149 // Create connections for this intersection -- always write low -> high
188 if (y > 0) { 150 if (y > 0) {
189 connections_a.push_back(xy_to_loc(x, y-2)); 151 connections_a.push_back(xy_to_loc(p, x, y-2));
190 connections_b.push_back(xy_to_loc(x, y)); 152 connections_b.push_back(xy_to_loc(p, x, y));
191 } 153 }
192 if (x > 0) { 154 if (x > 0) {
193 connections_a.push_back(xy_to_loc(x-2, y)); 155 connections_a.push_back(xy_to_loc(p, x-2, y));
194 connections_b.push_back(xy_to_loc(x, y)); 156 connections_b.push_back(xy_to_loc(p, x, y));
195 } 157 }
196 } 158 }
197 } 159 }
198 160
199 for (Endpoint endpoint : _endpoints) { 161 for (int x=0; x<p.width; x++) {
200 connections_a.push_back(xy_to_loc(endpoint.GetX(), endpoint.GetY())); // Target to connect to 162 for (int y=0; y<p.height; y++) {
201 connections_b.push_back(static_cast<int>(intersectionFlags.size())); // This endpoint 163 if (p.grid[x][y].end == Cell::Dir::NONE) continue;
202 164 connections_a.push_back(xy_to_loc(p, x, y)); // Target to connect to
203 double xPos = min + (endpoint.GetX()/2) * width_interval; 165 connections_b.push_back(static_cast<int>(intersectionFlags.size())); // This endpoint
204 double yPos = max - (endpoint.GetY()/2) * height_interval; 166
205 if (endpoint.GetDir()== Endpoint::Direction::LEFT) { 167 float xPos = min + (x/2) * width_interval;
206 xPos -= .05; 168 float yPos = max - (y/2) * height_interval;
207 } else if (endpoint.GetDir() == Endpoint::Direction::RIGHT) { 169 switch (p.grid[x][y].end) {
208 xPos += .05; 170 case Cell::Dir::LEFT:
209 } else if (endpoint.GetDir() == Endpoint::Direction::UP) { 171 xPos -= .05f;
210 yPos += .05; // Y position goes from 0 (bottom) to 1 (top), so this is reversed. 172 break;
211 } else if (endpoint.GetDir() == Endpoint::Direction::DOWN) { 173 case Cell::Dir::RIGHT:
212 yPos -= .05; 174 xPos += .05f;
213 } 175 break;
214 intersections.push_back(static_cast<float>(xPos)); 176 case Cell::Dir::UP:
215 intersections.push_back(static_cast<float>(yPos)); 177 yPos += .05f; // Y position goes from 0 (bottom) to 1 (top), so this is reversed.
216 intersectionFlags.push_back(IntersectionFlags::IS_ENDPOINT); 178 break;
217 } 179 case Cell::Dir::DOWN:
180 yPos -= .05f;
181 break;
182 }
183 intersections.push_back(xPos);
184 intersections.push_back(yPos);
185 intersectionFlags.push_back(Flags::IS_ENDPOINT);
186 }
187 }
218 188
219 // Dots 189 // Dots
220 for (int y=0; y<_height; y++) { 190 for (int x=0; x<p.width; x++) {
221 for (int x=0; x<_width; x++) { 191 for (int y=0; y<p.height; y++) {
222 if (!(_grid[x][y] & IntersectionFlags::HAS_DOT)) continue; 192 if (p.grid[x][y].dot == Cell::Dot::NONE) continue;
223 if (x%2 == 1 && y%2 == 1) continue; 193 if (x%2 == 1 && y%2 == 1) continue;
194
195 int flags = Flags::HAS_DOT;
196 switch (p.grid[x][y].dot) {
197 case Cell::Dot::BLACK:
198 break;
199 case Cell::Dot::BLUE:
200 flags |= DOT_IS_BLUE;
201 break;
202 case Cell::Dot::YELLOW:
203 flags |= DOT_IS_ORANGE;
204 break;
205 case Cell::Dot::INVISIBLE:
206 flags |= DOT_IS_INVISIBLE;
207 break;
208 }
209
210 // Dot is already a point the grid, just overwrite the flags
224 if (x%2 == 0 && y%2 == 0) { 211 if (x%2 == 0 && y%2 == 0) {
225 intersectionFlags[xy_to_loc(x, y)] |= _grid[x][y]; 212 intersectionFlags[xy_to_loc(p, x, y)] |= flags;
226 continue; 213 continue;
227 } 214 }
228 215
216 // Else, we need to introduce a new segment
229 // Locate the segment we're breaking 217 // Locate the segment we're breaking
230 for (int i=0; i<connections_a.size(); i++) { 218 for (int i=0; i<connections_a.size(); i++) {
231 auto [x1, y1] = loc_to_xy(connections_a[i]); 219 auto [x1, y1] = loc_to_xy(p, connections_a[i]);
232 auto [x2, y2] = loc_to_xy(connections_b[i]); 220 auto [x2, y2] = loc_to_xy(p, connections_b[i]);
233 if ((x1+1 == x && x2-1 == x && y1 == y && y2 == y) || 221 if ((x1+1 == x && x2-1 == x && y1 == y && y2 == y) ||
234 (y1+1 == y && y2-1 == y && x1 == x && x2 == x)) { 222 (y1+1 == y && y2-1 == y && x1 == x && x2 == x)) {
235 int other_connection = connections_b[i]; 223 int other_connection = connections_b[i];
@@ -241,21 +229,82 @@ void Panel::WriteIntersections(int id) {
241 } 229 }
242 } 230 }
243 // Add this dot to the end 231 // Add this dot to the end
244 double xPos = min + (x/2.0) * width_interval; 232 float xPos = min + (x/2.0f) * width_interval;
245 double yPos = max - (y/2.0) * height_interval; 233 float yPos = max - (y/2.0f) * height_interval;
246 intersections.push_back(static_cast<float>(xPos)); 234 intersections.push_back(xPos);
247 intersections.push_back(static_cast<float>(yPos)); 235 intersections.push_back(yPos);
248 intersectionFlags.push_back(_grid[x][y]); 236 intersectionFlags.push_back(flags);
249 } 237 }
250 } 238 }
251 239
252
253 _memory->WritePanelData<int>(id, NUM_DOTS, {static_cast<int>(intersectionFlags.size())}); 240 _memory->WritePanelData<int>(id, NUM_DOTS, {static_cast<int>(intersectionFlags.size())});
254 _memory->WriteArray<float>(id, DOT_POSITIONS, intersections); 241 _memory->WriteArray<float>(id, DOT_POSITIONS, intersections);
255 _memory->WriteArray<int>(id, DOT_FLAGS, intersectionFlags); 242 _memory->WriteArray<int>(id, DOT_FLAGS, intersectionFlags);
256 _memory->WritePanelData<int>(id, NUM_CONNECTIONS, {static_cast<int>(connections_a.size())}); 243 _memory->WritePanelData<int>(id, NUM_CONNECTIONS, {static_cast<int>(connections_a.size())});
257 _memory->WriteArray<int>(id, DOT_CONNECTION_A, connections_a); 244 _memory->WriteArray<int>(id, DOT_CONNECTION_A, connections_a);
258 _memory->WriteArray<int>(id, DOT_CONNECTION_B, connections_b); 245 _memory->WriteArray<int>(id, DOT_CONNECTION_B, connections_b);
259 _memory->WritePanelData<int>(id, NEEDS_REDRAW, {1});
260} 246}
261 247
248void PuzzleSerializer::WriteDecorations(const Puzzle& p, int id) {
249 std::vector<int> decorations;
250 for (int y=p.height-2; y>0; y-=2) {
251 for (int x=1; x<p.width-1; x+=2) {
252 auto d = p.grid[x][y].decoration;
253 if (d) {
254 decorations.push_back(d->color | d->type | d->count | d->polyshape);
255 } else {
256 decorations.push_back(0);
257 }
258 }
259 }
260
261 _memory->WritePanelData<int>(id, NUM_DECORATIONS, {static_cast<int>(decorations.size())});
262 _memory->WriteArray<int>(id, DECORATIONS, decorations);
263}
264
265std::tuple<int, int> PuzzleSerializer::loc_to_xy(const Puzzle& p, int location) {
266 int height2 = (p.height - 1) / 2;
267 int width2 = (p.width + 1) / 2;
268
269 int x = 2 * (location % width2);
270 int y = 2 * (height2 - location / width2);
271 return {x, y};
272}
273
274int PuzzleSerializer::xy_to_loc(const Puzzle& p, int x, int y) {
275 int height2 = (p.height - 1) / 2;
276 int width2 = (p.width + 1) / 2;
277
278 int rowsFromBottom = height2 - y/2;
279 return rowsFromBottom * width2 + x/2;
280}
281
282std::tuple<int, int> PuzzleSerializer::dloc_to_xy(const Puzzle& p, int location) {
283 int height2 = (p.height - 3) / 2;
284 int width2 = (p.width - 1) / 2;
285
286 int x = 2 * (location % width2) + 1;
287 int y = 2 * (height2 - location / width2) + 1;
288 return {x, y};
289}
290
291int PuzzleSerializer::xy_to_dloc(const Puzzle& p, int x, int y) {
292 int height2 = (p.height - 3) / 2;
293 int width2 = (p.width - 1) / 2;
294
295 int rowsFromBottom = height2 - (y - 1)/2;
296 return rowsFromBottom * width2 + (x - 1)/2;
297}
298
299Cell::Dot PuzzleSerializer::FlagsToDot(int flags) {
300 if (!(flags & Flags::HAS_DOT)) return Cell::Dot::NONE;
301 if (flags & Flags::DOT_IS_BLUE) {
302 return Cell::Dot::BLUE;
303 } else if (flags & Flags::DOT_IS_ORANGE) {
304 return Cell::Dot::YELLOW;
305 } else if (flags & Flags::DOT_IS_INVISIBLE) {
306 return Cell::Dot::INVISIBLE;
307 } else {
308 return Cell::Dot::BLACK;
309 }
310}