summary refs log tree commit diff stats
path: root/Source/Panel.cpp
diff options
context:
space:
mode:
authorjbzdarkid <jbzdarkid@gmail.com>2019-11-09 13:39:10 -0800
committerjbzdarkid <jbzdarkid@gmail.com>2019-11-09 13:39:10 -0800
commit36be1ed32ac9a554f0b11fcc13b5699e717b81f2 (patch)
tree383618d781bc5b4701b31555f90b8a629fe6d205 /Source/Panel.cpp
parent413e1f0aaae961660781675158e38520126c11b6 (diff)
downloadwitness-tutorializer-36be1ed32ac9a554f0b11fcc13b5699e717b81f2.tar.gz
witness-tutorializer-36be1ed32ac9a554f0b11fcc13b5699e717b81f2.tar.bz2
witness-tutorializer-36be1ed32ac9a554f0b11fcc13b5699e717b81f2.zip
Functioning solver/validator (at least for mazes)
Diffstat (limited to 'Source/Panel.cpp')
-rw-r--r--Source/Panel.cpp387
1 files changed, 0 insertions, 387 deletions
diff --git a/Source/Panel.cpp b/Source/Panel.cpp deleted file mode 100644 index b204a28..0000000 --- a/Source/Panel.cpp +++ /dev/null
@@ -1,387 +0,0 @@
1#include "Panel.h"
2#include "Memory.h"
3
4#pragma warning (disable:26451)
5#pragma warning (disable:26812)
6
7PuzzleSerializer::PuzzleSerializer(const std::shared_ptr<Memory>& memory) : _memory(memory) {}
8
9Puzzle PuzzleSerializer::ReadPuzzle(int id) {
10 Puzzle p;
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 if (p.width < 0 || p.height < 0) return p; // @Error: Grid size should be always positive? Looks like the starting panels break this rule, though.
14 p.grid.resize(p.width);
15 for (auto& row : p.grid) row.resize(p.height);
16
17 ReadIntersections(p, id);
18 ReadDecorations(p, id);
19
20 return p;
21}
22
23void PuzzleSerializer::ReadIntersections(Puzzle& p, int id) {
24 int numIntersections = _memory->ReadPanelData<int>(id, NUM_DOTS, 1)[0];
25 std::vector<int> intersectionFlags = _memory->ReadArray<int>(id, DOT_FLAGS, numIntersections);
26 int numConnections = _memory->ReadPanelData<int>(id, NUM_CONNECTIONS, 1)[0];
27 std::vector<int> connections_a = _memory->ReadArray<int>(id, DOT_CONNECTION_A, numConnections);
28 std::vector<int> connections_b = _memory->ReadArray<int>(id, DOT_CONNECTION_B, numConnections);
29 std::vector<float> intersectionLocations = _memory->ReadArray<float>(id, DOT_POSITIONS, numIntersections*2);
30
31 // @Cleanup: Change defaults?
32 for (int x=0; x<p.width; x++) {
33 for (int y=0; y<p.height; y++) {
34 if (x%2 == y%2) continue;
35 p.grid[x][y].gap = Cell::Gap::FULL;
36 }
37 }
38
39 for (int j=0; j<numIntersections; j++) {
40 if (intersectionFlags[connections_a[j]] & Flags::IS_ENDPOINT) break;
41 if (intersectionFlags[connections_b[j]] & Flags::IS_ENDPOINT) break;
42 float x1 = intersectionLocations[2*connections_a[j]];
43 float y1 = intersectionLocations[2*connections_a[j]+1];
44 float x2 = intersectionLocations[2*connections_b[j]];
45 float y2 = intersectionLocations[2*connections_b[j]+1];
46 auto [x, y] = loc_to_xy(p, connections_a[j]);
47
48 if (x1 < x2) x++;
49 else if (x1 > x2) x--;
50 else if (y1 < y2) y--;
51 else if (y1 > y2) y++;
52 p.grid[x][y].gap = Cell::Gap::NONE;
53 }
54
55 // This iterates bottom-top, left-right
56 int i = 0;
57 for (;; i++) {
58 int flags = intersectionFlags[i];
59 auto [x, y] = loc_to_xy(p, i);
60 if (y < 0) break;
61 if (flags & Flags::IS_STARTPOINT) {
62 p.grid[x][y].start = true;
63 }
64 p.grid[x][y].dot = FlagsToDot(flags);
65 if (flags & Flags::IS_FULL_GAP) {
66 p.grid[x][y].gap = Cell::Gap::FULL;
67 }
68 }
69
70 // Iterate the remaining intersections (endpoints, dots, gaps)
71 for (; i < numIntersections; i++) {
72 int location = FindConnection(i, connections_a, connections_b);
73 if (location == -1) continue; // @Error: Unable to find connection point
74 // (x1, y1) location of this intersection
75 // (x2, y2) location of the connected intersection
76 float x1 = intersectionLocations[2*i];
77 float y1 = intersectionLocations[2*i+1];
78 float x2 = intersectionLocations[2*location];
79 float y2 = intersectionLocations[2*location+1];
80 auto [x, y] = loc_to_xy(p, location);
81
82 if (intersectionFlags[i] & Flags::IS_ENDPOINT) {
83 // Our x coordinate is less than the target's
84 if (x1 < x2) p.grid[x][y].end = Cell::Dir::LEFT;
85 else if (x1 > x2) p.grid[x][y].end = Cell::Dir::RIGHT;
86 // Note that Y coordinates are reversed: 0.0 (bottom) 1.0 (top)
87 else if (y1 < y2) p.grid[x][y].end = Cell::Dir::DOWN;
88 else if (y1 > y2) p.grid[x][y].end = Cell::Dir::UP;
89 } else if (intersectionFlags[i] & Flags::HAS_DOT) {
90 if (x1 < x2) x--;
91 else if (x1 > x2) x++;
92 else if (y1 < y2) y++;
93 else if (y1 > y2) y--;
94 p.grid[x][y].dot = FlagsToDot(intersectionFlags[i]);
95 } else if (intersectionFlags[i] & Flags::HAS_NO_CONN) {
96 if (x1 < x2) x--;
97 else if (x1 > x2) x++;
98 else if (y1 < y2) y++;
99 else if (y1 > y2) y--;
100 p.grid[x][y].gap = Cell::Gap::BREAK;
101 }
102 }
103}
104
105void PuzzleSerializer::ReadDecorations(Puzzle& p, int id) {
106 int numDecorations = _memory->ReadPanelData<int>(id, NUM_DECORATIONS, 1)[0];
107 std::vector<int> decorations = _memory->ReadArray<int>(id, DECORATIONS, numDecorations);
108 if (numDecorations > 0) p.hasDecorations = true;
109
110 for (int i=0; i<numDecorations; i++) {
111 auto [x, y] = dloc_to_xy(p, i);
112 auto d = std::make_shared<Decoration>();
113 p.grid[x][y].decoration = d;
114 d->type = static_cast<Shape>(decorations[i] & 0xFF00);
115 switch(d->type) {
116 case Shape::Poly:
117 case Shape::RPoly:
118 case Shape::Ylop:
119 d->polyshape = decorations[i] & 0xFFFF0000;
120 break;
121 case Shape::Triangle:
122 d->count = decorations[i] & 0x000F0000;
123 break;
124 }
125 d->color = static_cast<Color>(decorations[i] & 0xF);
126 }
127}
128
129void PuzzleSerializer::WritePuzzle(const Puzzle& p, int id) {
130 _memory->WritePanelData<int>(id, GRID_SIZE_X, {(p.width + 1)/2});
131 _memory->WritePanelData<int>(id, GRID_SIZE_Y, {(p.height + 1)/2});
132
133 WriteIntersections(p, id);
134 if (p.hasDecorations) WriteDecorations(p, id);
135
136 _memory->WritePanelData<int>(id, NEEDS_REDRAW, {1});
137}
138
139void PuzzleSerializer::WriteIntersections(const Puzzle& p, int id) {
140 std::vector<float> intersectionLocations;
141 std::vector<int> intersectionFlags;
142 std::vector<int> connections_a;
143 std::vector<int> connections_b;
144
145 float min = 0.1f;
146 float max = 0.9f;
147 float width_interval = (max - min) / (p.width/2);
148 float height_interval = (max - min) / (p.height/2);
149 float horiz_gap_size = width_interval / 2;
150 float verti_gap_size = height_interval / 2;
151
152 // TODO: Compute HAS_NO_CONN / HAS_HORIZ_CONN / HAS_VERTI_CONN in this loop.
153 // @Cleanup: If I write directly to locations, then I can simplify this gross loop iterator.
154 // int numIntersections = (p.width / 2 + 1) * (p.height / 2 + 1);
155 // Grided intersections
156 for (int y=p.height-1; y>=0; y-=2) {
157 for (int x=0; x<p.width; x+=2) {
158 intersectionLocations.push_back(min + (x/2) * width_interval);
159 intersectionLocations.push_back(max - (y/2) * height_interval);
160 int flags = 0;
161 if (p.grid[x][y].start) {
162 flags |= Flags::IS_STARTPOINT;
163 }
164 if (p.grid[x][y].gap == Cell::Gap::FULL) {
165 flags |= Flags::IS_FULL_GAP;
166 }
167 switch (p.grid[x][y].dot) {
168 case Cell::Dot::BLACK:
169 flags |= Flags::HAS_DOT;
170 break;
171 case Cell::Dot::BLUE:
172 flags |= Flags::HAS_DOT | Flags::DOT_IS_BLUE;
173 break;
174 case Cell::Dot::YELLOW:
175 flags |= Flags::HAS_DOT | Flags::DOT_IS_ORANGE;
176 break;
177 case Cell::Dot::INVISIBLE:
178 flags |= Flags::HAS_DOT | Flags::DOT_IS_INVISIBLE;
179 break;
180 }
181
182 intersectionFlags.push_back(flags);
183
184 // Create connections for this intersection -- always write the smaller value into a, the larger into b
185 // Bottom connection
186 if (y > 0 && p.grid[x][y-1].gap == Cell::Gap::NONE) {
187 connections_a.push_back(xy_to_loc(p, x, y-2));
188 connections_b.push_back(xy_to_loc(p, x, y));
189 }
190 // Left connection
191 if (x > 0 && p.grid[x-1][y].gap == Cell::Gap::NONE) {
192 connections_a.push_back(xy_to_loc(p, x-2, y));
193 connections_b.push_back(xy_to_loc(p, x, y));
194 }
195 }
196 }
197
198 // Endpoints
199 for (int x=0; x<p.width; x++) {
200 for (int y=0; y<p.height; y++) {
201 if (p.grid[x][y].end == Cell::Dir::NONE) continue;
202 connections_a.push_back(xy_to_loc(p, x, y)); // Target to connect to
203 connections_b.push_back(static_cast<int>(intersectionFlags.size())); // This endpoint
204
205 float xPos = min + (x/2) * width_interval;
206 float yPos = max - (y/2) * height_interval;
207 switch (p.grid[x][y].end) {
208 case Cell::Dir::LEFT:
209 xPos -= .05f;
210 break;
211 case Cell::Dir::RIGHT:
212 xPos += .05f;
213 break;
214 case Cell::Dir::UP:
215 yPos += .05f; // Y position goes from 0 (bottom) to 1 (top), so this is reversed.
216 break;
217 case Cell::Dir::DOWN:
218 yPos -= .05f;
219 break;
220 }
221 intersectionLocations.push_back(xPos);
222 intersectionLocations.push_back(yPos);
223 intersectionFlags.push_back(Flags::IS_ENDPOINT);
224 }
225 }
226
227 // Dots
228 for (int x=0; x<p.width; x++) {
229 for (int y=0; y<p.height; y++) {
230 if (x%2 == y%2) continue; // Cells are invalid, intersections are already handled.
231 if (p.grid[x][y].dot == Cell::Dot::NONE) continue;
232
233 // We need to introduce a new segment --
234 // Locate the segment we're breaking
235 for (int i=0; i<connections_a.size(); i++) {
236 auto [x1, y1] = loc_to_xy(p, connections_a[i]);
237 auto [x2, y2] = loc_to_xy(p, connections_b[i]);
238 if ((x1+1 == x && x2-1 == x && y1 == y && y2 == y) ||
239 (y1+1 == y && y2-1 == y && x1 == x && x2 == x)) {
240 int other_connection = connections_b[i];
241 connections_b[i] = static_cast<int>(intersectionFlags.size()); // This endpoint
242
243 connections_a.push_back(other_connection);
244 connections_b.push_back(static_cast<int>(intersectionFlags.size())); // This endpoint
245 break;
246 }
247 }
248 // Add this dot to the end
249 float xPos = min + (x/2.0f) * width_interval;
250 float yPos = max - (y/2.0f) * height_interval;
251 intersectionLocations.push_back(xPos);
252 intersectionLocations.push_back(yPos);
253
254 int flags = Flags::HAS_DOT;
255 switch (p.grid[x][y].dot) {
256 case Cell::Dot::BLACK:
257 break;
258 case Cell::Dot::BLUE:
259 flags |= DOT_IS_BLUE;
260 break;
261 case Cell::Dot::YELLOW:
262 flags |= DOT_IS_ORANGE;
263 break;
264 case Cell::Dot::INVISIBLE:
265 flags |= DOT_IS_INVISIBLE;
266 break;
267 }
268 intersectionFlags.push_back(flags);
269 }
270 }
271
272 // Gaps
273 for (int x=0; x<p.width; x++) {
274 for (int y=0; y<p.height; y++) {
275 if (x%2 == y%2) continue; // Cells are invalid, intersections are already handled.
276 if (p.grid[x][y].gap == Cell::Gap::NONE) continue;
277
278 float xPos = min + (x/2.0f) * width_interval;
279 float yPos = max - (y/2.0f) * height_interval;
280 // Reminder: Y goes from 0.0 (bottom) to 1.0 (top)
281 if (x%2 == 0) { // Vertical gap
282 connections_a.push_back(xy_to_loc(p, x, y-1));
283 connections_b.push_back(static_cast<int>(intersectionFlags.size())); // This endpoint
284 intersectionLocations.push_back(xPos);
285 intersectionLocations.push_back(yPos + verti_gap_size / 2);
286 intersectionFlags.push_back(Flags::HAS_NO_CONN | Flags::HAS_VERTI_CONN);
287
288 connections_a.push_back(xy_to_loc(p, x, y+1));
289 connections_b.push_back(static_cast<int>(intersectionFlags.size())); // This endpoint
290 intersectionLocations.push_back(xPos);
291 intersectionLocations.push_back(yPos - verti_gap_size / 2);
292 intersectionFlags.push_back(Flags::HAS_NO_CONN | Flags::HAS_VERTI_CONN);
293 } else if (y%2 == 0) { // Horizontal gap
294 connections_a.push_back(xy_to_loc(p, x-1, y));
295 connections_b.push_back(static_cast<int>(intersectionFlags.size())); // This endpoint
296 intersectionLocations.push_back(xPos - horiz_gap_size / 2);
297 intersectionLocations.push_back(yPos);
298 intersectionFlags.push_back(Flags::HAS_NO_CONN | Flags::HAS_HORIZ_CONN);
299
300 connections_a.push_back(xy_to_loc(p, x+1, y));
301 connections_b.push_back(static_cast<int>(intersectionFlags.size())); // This endpoint
302 intersectionLocations.push_back(xPos + horiz_gap_size / 2);
303 intersectionLocations.push_back(yPos);
304 intersectionFlags.push_back(Flags::HAS_NO_CONN | Flags::HAS_HORIZ_CONN);
305 }
306 }
307 }
308
309 _memory->WritePanelData<int>(id, NUM_DOTS, {static_cast<int>(intersectionFlags.size())});
310 _memory->WriteArray<float>(id, DOT_POSITIONS, intersectionLocations);
311 _memory->WriteArray<int>(id, DOT_FLAGS, intersectionFlags);
312 _memory->WritePanelData<int>(id, NUM_CONNECTIONS, {static_cast<int>(connections_a.size())});
313 _memory->WriteArray<int>(id, DOT_CONNECTION_A, connections_a);
314 _memory->WriteArray<int>(id, DOT_CONNECTION_B, connections_b);
315}
316
317void PuzzleSerializer::WriteDecorations(const Puzzle& p, int id) {
318 std::vector<int> decorations;
319 for (int y=p.height-2; y>0; y-=2) {
320 for (int x=1; x<p.width-1; x+=2) {
321 auto d = p.grid[x][y].decoration;
322 if (d) {
323 decorations.push_back(d->color | d->type | d->count | d->polyshape);
324 } else {
325 decorations.push_back(0);
326 }
327 }
328 }
329
330 _memory->WritePanelData<int>(id, NUM_DECORATIONS, {static_cast<int>(decorations.size())});
331 _memory->WriteArray<int>(id, DECORATIONS, decorations);
332}
333
334std::tuple<int, int> PuzzleSerializer::loc_to_xy(const Puzzle& p, int location) const {
335 int height2 = (p.height - 1) / 2;
336 int width2 = (p.width + 1) / 2;
337
338 int x = 2 * (location % width2);
339 int y = 2 * (height2 - location / width2);
340 return {x, y};
341}
342
343int PuzzleSerializer::xy_to_loc(const Puzzle& p, int x, int y) const {
344 int height2 = (p.height - 1) / 2;
345 int width2 = (p.width + 1) / 2;
346
347 int rowsFromBottom = height2 - y/2;
348 return rowsFromBottom * width2 + x/2;
349}
350
351std::tuple<int, int> PuzzleSerializer::dloc_to_xy(const Puzzle& p, int location) const {
352 int height2 = (p.height - 3) / 2;
353 int width2 = (p.width - 1) / 2;
354
355 int x = 2 * (location % width2) + 1;
356 int y = 2 * (height2 - location / width2) + 1;
357 return {x, y};
358}
359
360int PuzzleSerializer::xy_to_dloc(const Puzzle& p, int x, int y) const {
361 int height2 = (p.height - 3) / 2;
362 int width2 = (p.width - 1) / 2;
363
364 int rowsFromBottom = height2 - (y - 1)/2;
365 return rowsFromBottom * width2 + (x - 1)/2;
366}
367
368Cell::Dot PuzzleSerializer::FlagsToDot(int flags) const {
369 if (!(flags & Flags::HAS_DOT)) return Cell::Dot::NONE;
370 if (flags & Flags::DOT_IS_BLUE) {
371 return Cell::Dot::BLUE;
372 } else if (flags & Flags::DOT_IS_ORANGE) {
373 return Cell::Dot::YELLOW;
374 } else if (flags & Flags::DOT_IS_INVISIBLE) {
375 return Cell::Dot::INVISIBLE;
376 } else {
377 return Cell::Dot::BLACK;
378 }
379}
380
381int PuzzleSerializer::FindConnection(int i, const std::vector<int>& connections_a, const std::vector<int>& connections_b) const {
382 for (int j=0; j<connections_a.size(); j++) {
383 if (connections_a[j] == i) return connections_b[j];
384 if (connections_b[j] == i) return connections_a[j];
385 }
386 return -1;
387}