diff options
Diffstat (limited to 'Source/PuzzleSerializer.cpp')
-rw-r--r-- | Source/PuzzleSerializer.cpp | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/Source/PuzzleSerializer.cpp b/Source/PuzzleSerializer.cpp new file mode 100644 index 0000000..c1e93a5 --- /dev/null +++ b/Source/PuzzleSerializer.cpp | |||
@@ -0,0 +1,461 @@ | |||
1 | #include "PuzzleSerializer.h" | ||
2 | #include "Memory.h" | ||
3 | |||
4 | #pragma warning (disable:26451) | ||
5 | #pragma warning (disable:26812) | ||
6 | |||
7 | PuzzleSerializer::PuzzleSerializer(const std::shared_ptr<Memory>& memory) : _memory(memory) {} | ||
8 | |||
9 | Puzzle PuzzleSerializer::ReadPuzzle(int id) { | ||
10 | int width = 2 * _memory->ReadEntityData<int>(id, GRID_SIZE_X, 1)[0] - 1; | ||
11 | int height = 2 * _memory->ReadEntityData<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. | ||
13 | |||
14 | int numIntersections = _memory->ReadEntityData<int>(id, NUM_DOTS, 1)[0]; | ||
15 | _intersectionFlags = _memory->ReadArray<int>(id, DOT_FLAGS, numIntersections); | ||
16 | int numConnections = _memory->ReadEntityData<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 | |||
21 | Puzzle p; | ||
22 | p.NewGrid(width, height); | ||
23 | ReadIntersections(p); | ||
24 | ReadExtras(p); | ||
25 | ReadDecorations(p, id); | ||
26 | ReadSequence(p, id); | ||
27 | return p; | ||
28 | } | ||
29 | |||
30 | void PuzzleSerializer::WritePuzzle(const Puzzle& p, int id) { | ||
31 | _intersectionFlags.clear(); | ||
32 | _connectionsA.clear(); | ||
33 | _connectionsB.clear(); | ||
34 | _intersectionLocations.clear(); | ||
35 | |||
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 | WriteDots(p); | ||
45 | WriteGaps(p); | ||
46 | WriteEndpoints(p); | ||
47 | WriteDecorations(p, id); | ||
48 | WriteSequence(p, id); | ||
49 | |||
50 | _memory->WriteEntityData<int>(id, GRID_SIZE_X, {(p.width + 1)/2}); | ||
51 | _memory->WriteEntityData<int>(id, GRID_SIZE_Y, {(p.height + 1)/2}); | ||
52 | _memory->WriteEntityData<int>(id, NUM_DOTS, {static_cast<int>(_intersectionFlags.size())}); | ||
53 | _memory->WriteArray<float>(id, DOT_POSITIONS, _intersectionLocations); | ||
54 | _memory->WriteArray<int>(id, DOT_FLAGS, _intersectionFlags); | ||
55 | _memory->WriteEntityData<int>(id, NUM_CONNECTIONS, {static_cast<int>(_connectionsA.size())}); | ||
56 | _memory->WriteArray<int>(id, DOT_CONNECTION_A, _connectionsA); | ||
57 | _memory->WriteArray<int>(id, DOT_CONNECTION_B, _connectionsB); | ||
58 | _memory->WriteEntityData<int>(id, NEEDS_REDRAW, {1}); | ||
59 | } | ||
60 | |||
61 | void PuzzleSerializer::ReadIntersections(Puzzle& p) { | ||
62 | // @Cleanup: Change defaults? | ||
63 | for (int x=0; x<p.width; x++) { | ||
64 | for (int y=0; y<p.height; y++) { | ||
65 | if (x%2 == y%2) continue; | ||
66 | p.grid[x][y].gap = Cell::Gap::FULL; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | for (int j=0; j<_intersectionFlags.size(); j++) { | ||
71 | if (_intersectionFlags[_connectionsA[j]] & Flags::IS_ENDPOINT) break; | ||
72 | if (_intersectionFlags[_connectionsB[j]] & Flags::IS_ENDPOINT) break; | ||
73 | float x1 = _intersectionLocations[2*_connectionsA[j]]; | ||
74 | float y1 = _intersectionLocations[2*_connectionsA[j]+1]; | ||
75 | float x2 = _intersectionLocations[2*_connectionsB[j]]; | ||
76 | float y2 = _intersectionLocations[2*_connectionsB[j]+1]; | ||
77 | auto [x, y] = loc_to_xy(p, _connectionsA[j]); | ||
78 | |||
79 | if (x1 < x2) x++; | ||
80 | else if (x1 > x2) x--; | ||
81 | else if (y1 < y2) y--; | ||
82 | else if (y1 > y2) y++; | ||
83 | p.grid[x][y].gap = Cell::Gap::NONE; | ||
84 | } | ||
85 | } | ||
86 | |||
87 | void PuzzleSerializer::ReadExtras(Puzzle& p) { | ||
88 | // This iterates bottom-top, left-right | ||
89 | int i = 0; | ||
90 | for (; i < _intersectionFlags.size(); i++) { | ||
91 | int flags = _intersectionFlags[i]; | ||
92 | auto [x, y] = loc_to_xy(p, i); | ||
93 | if (y < 0) break; // This is the expected exit point | ||
94 | if (flags & Flags::IS_STARTPOINT) { | ||
95 | p.grid[x][y].start = true; | ||
96 | } | ||
97 | p.grid[x][y].dot = FlagsToDot(flags); | ||
98 | if (flags & Flags::HAS_NO_CONN) { | ||
99 | p.grid[x][y].gap = Cell::Gap::FULL; | ||
100 | } | ||
101 | } | ||
102 | |||
103 | // Iterate the remaining intersections (endpoints, dots, gaps) | ||
104 | for (; i < _intersectionFlags.size(); i++) { | ||
105 | int location = FindConnection(i); | ||
106 | if (location == -1) continue; // @Error: Unable to find connection point | ||
107 | // (x1, y1) location of this intersection | ||
108 | // (x2, y2) location of the connected intersection | ||
109 | float x1 = _intersectionLocations[2*i]; | ||
110 | float y1 = _intersectionLocations[2*i+1]; | ||
111 | float x2 = _intersectionLocations[2*location]; | ||
112 | float y2 = _intersectionLocations[2*location+1]; | ||
113 | auto [x, y] = loc_to_xy(p, location); | ||
114 | |||
115 | if (_intersectionFlags[i] & Flags::IS_ENDPOINT) { | ||
116 | // Our x coordinate is less than the target's | ||
117 | if (x1 < x2) p.grid[x][y].end = Cell::Dir::LEFT; | ||
118 | else if (x1 > x2) p.grid[x][y].end = Cell::Dir::RIGHT; | ||
119 | // Note that Y coordinates are reversed: 0.0 (bottom) 1.0 (top) | ||
120 | else if (y1 < y2) p.grid[x][y].end = Cell::Dir::DOWN; | ||
121 | else if (y1 > y2) p.grid[x][y].end = Cell::Dir::UP; | ||
122 | } else if (_intersectionFlags[i] & Flags::HAS_DOT) { | ||
123 | if (x1 < x2) x--; | ||
124 | else if (x1 > x2) x++; | ||
125 | else if (y1 < y2) y++; | ||
126 | else if (y1 > y2) y--; | ||
127 | p.grid[x][y].dot = FlagsToDot(_intersectionFlags[i]); | ||
128 | } else if (_intersectionFlags[i] & Flags::HAS_ONE_CONN) { | ||
129 | if (x1 < x2) x--; | ||
130 | else if (x1 > x2) x++; | ||
131 | else if (y1 < y2) y++; | ||
132 | else if (y1 > y2) y--; | ||
133 | p.grid[x][y].gap = Cell::Gap::BREAK; | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | |||
138 | void PuzzleSerializer::ReadDecorations(Puzzle& p, int id) { | ||
139 | int numDecorations = _memory->ReadEntityData<int>(id, NUM_DECORATIONS, 1)[0]; | ||
140 | std::vector<int> decorations = _memory->ReadArray<int>(id, DECORATIONS, numDecorations); | ||
141 | if (numDecorations > 0) p.hasDecorations = true; | ||
142 | |||
143 | for (int i=0; i<numDecorations; i++) { | ||
144 | auto [x, y] = dloc_to_xy(p, i); | ||
145 | auto d = std::make_shared<Decoration>(); | ||
146 | p.grid[x][y].decoration = d; | ||
147 | d->type = static_cast<Type>(decorations[i] & 0xFF00); | ||
148 | switch(d->type) { | ||
149 | case Type::Poly: | ||
150 | case Type::RPoly: | ||
151 | case Type::Ylop: | ||
152 | d->polyshape = decorations[i] & 0xFFFF0000; | ||
153 | break; | ||
154 | case Type::Triangle: | ||
155 | d->count = decorations[i] & 0x000F0000; | ||
156 | break; | ||
157 | } | ||
158 | d->color = static_cast<Color>(decorations[i] & 0xF); | ||
159 | } | ||
160 | } | ||
161 | |||
162 | void PuzzleSerializer::ReadSequence(Puzzle& p, int id) { | ||
163 | int sequenceLength = _memory->ReadEntityData<int>(id, SEQUENCE_LEN, 1)[0]; | ||
164 | std::vector<int> sequence = _memory->ReadArray<int>(id, SEQUENCE, sequenceLength); | ||
165 | |||
166 | for (int location : sequence) { | ||
167 | auto [x, y] = loc_to_xy(p, location); | ||
168 | p.sequence.emplace_back(Pos{x, y}); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | void PuzzleSerializer::WriteIntersections(const Puzzle& p) { | ||
173 | // @Cleanup: If I write directly to locations, then I can simplify this gross loop iterator. | ||
174 | // int numIntersections = (p.width / 2 + 1) * (p.height / 2 + 1); | ||
175 | // Grided intersections | ||
176 | for (int y=p.height-1; y>=0; y-=2) { | ||
177 | for (int x=0; x<p.width; x+=2) { | ||
178 | auto [xPos, yPos] = xy_to_pos(p, x, y); | ||
179 | _intersectionLocations.push_back(xPos); | ||
180 | _intersectionLocations.push_back(yPos); | ||
181 | int flags = 0; | ||
182 | if (p.grid[x][y].start) { | ||
183 | flags |= Flags::IS_STARTPOINT; | ||
184 | } | ||
185 | switch (p.grid[x][y].dot) { | ||
186 | case Cell::Dot::BLACK: | ||
187 | flags |= Flags::HAS_DOT; | ||
188 | break; | ||
189 | case Cell::Dot::BLUE: | ||
190 | flags |= Flags::HAS_DOT | Flags::DOT_IS_BLUE; | ||
191 | break; | ||
192 | case Cell::Dot::YELLOW: | ||
193 | flags |= Flags::HAS_DOT | Flags::DOT_IS_ORANGE; | ||
194 | break; | ||
195 | case Cell::Dot::INVISIBLE: | ||
196 | flags |= Flags::HAS_DOT | Flags::DOT_IS_INVISIBLE; | ||
197 | break; | ||
198 | } | ||
199 | |||
200 | int numConnections = 0; | ||
201 | if (p.grid[x][y].end != Cell::Dir::NONE) numConnections++; | ||
202 | // Create connections for this intersection for top/left only. | ||
203 | // Top connection | ||
204 | if (y > 0 && p.grid[x][y-1].gap != Cell::Gap::FULL) { | ||
205 | _connectionsA.push_back(xy_to_loc(p, x, y-2)); | ||
206 | _connectionsB.push_back(xy_to_loc(p, x, y)); | ||
207 | flags |= Flags::HAS_VERTI_CONN; | ||
208 | numConnections++; | ||
209 | } | ||
210 | // Bottom connection | ||
211 | if (y < p.height - 1 && p.grid[x][y+1].gap != Cell::Gap::FULL) { | ||
212 | flags |= Flags::HAS_VERTI_CONN; | ||
213 | numConnections++; | ||
214 | } | ||
215 | // Left connection | ||
216 | if (x > 0 && p.grid[x-1][y].gap != Cell::Gap::FULL) { | ||
217 | _connectionsA.push_back(xy_to_loc(p, x-2, y)); | ||
218 | _connectionsB.push_back(xy_to_loc(p, x, y)); | ||
219 | flags |= Flags::HAS_HORIZ_CONN; | ||
220 | numConnections++; | ||
221 | } | ||
222 | // Right connection | ||
223 | if (x < p.width - 1 && p.grid[x+1][y].gap != Cell::Gap::FULL) { | ||
224 | flags |= Flags::HAS_HORIZ_CONN; | ||
225 | numConnections++; | ||
226 | } | ||
227 | if (numConnections == 0) flags |= HAS_NO_CONN; | ||
228 | if (numConnections == 1) flags |= HAS_ONE_CONN; | ||
229 | |||
230 | _intersectionFlags.push_back(flags); | ||
231 | } | ||
232 | } | ||
233 | } | ||
234 | |||
235 | void PuzzleSerializer::WriteEndpoints(const Puzzle& p) { | ||
236 | for (int x=0; x<p.width; x++) { | ||
237 | for (int y=0; y<p.height; y++) { | ||
238 | if (p.grid[x][y].end == Cell::Dir::NONE) continue; | ||
239 | _connectionsA.push_back(xy_to_loc(p, x, y)); | ||
240 | _connectionsB.push_back(static_cast<int>(_intersectionFlags.size())); | ||
241 | |||
242 | auto [xPos, yPos] = xy_to_pos(p, x, y); | ||
243 | switch (p.grid[x][y].end) { | ||
244 | case Cell::Dir::LEFT: | ||
245 | xPos -= .05f; | ||
246 | break; | ||
247 | case Cell::Dir::RIGHT: | ||
248 | xPos += .05f; | ||
249 | break; | ||
250 | case Cell::Dir::UP: | ||
251 | yPos += .05f; // Y position goes from 0 (bottom) to 1 (top), so this is reversed. | ||
252 | break; | ||
253 | case Cell::Dir::DOWN: | ||
254 | yPos -= .05f; | ||
255 | break; | ||
256 | } | ||
257 | _endpointLocations.emplace_back(x, y, static_cast<int>(_intersectionFlags.size())); | ||
258 | _intersectionLocations.push_back(xPos); | ||
259 | _intersectionLocations.push_back(yPos); | ||
260 | _intersectionFlags.push_back(Flags::IS_ENDPOINT); | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | |||
265 | void PuzzleSerializer::WriteDots(const Puzzle& p) { | ||
266 | for (int x=0; x<p.width; x++) { | ||
267 | for (int y=0; y<p.height; y++) { | ||
268 | if (x%2 == y%2) continue; // Cells are invalid, intersections are already handled. | ||
269 | if (p.grid[x][y].dot == Cell::Dot::NONE) continue; | ||
270 | |||
271 | // We need to introduce a new segment which contains this dot. Break the existing segment, and add one. | ||
272 | int connectionLocation = -1; | ||
273 | for (int i=0; i<_connectionsA.size(); i++) { | ||
274 | auto [x1, y1] = loc_to_xy(p, _connectionsA[i]); | ||
275 | auto [x2, y2] = loc_to_xy(p, _connectionsB[i]); | ||
276 | if ((x1+1 == x && x2-1 == x && y1 == y && y2 == y) || | ||
277 | (y1+1 == y && y2-1 == y && x1 == x && x2 == x)) { | ||
278 | connectionLocation = i; | ||
279 | break; | ||
280 | } | ||
281 | } | ||
282 | if (connectionLocation == -1) continue; // @Error | ||
283 | |||
284 | // @Assume: B > A for connections. To remove, add the horiz/verti check, see gaps. | ||
285 | int other_connection = _connectionsB[connectionLocation]; | ||
286 | _connectionsB[connectionLocation] = static_cast<int>(_intersectionFlags.size()); | ||
287 | _connectionsA.push_back(other_connection); | ||
288 | _connectionsB.push_back(static_cast<int>(_intersectionFlags.size())); | ||
289 | |||
290 | // Add this dot to the end | ||
291 | auto [xPos, yPos] = xy_to_pos(p, x, y); | ||
292 | _intersectionLocations.push_back(xPos); | ||
293 | _intersectionLocations.push_back(yPos); | ||
294 | |||
295 | int flags = Flags::HAS_DOT; | ||
296 | switch (p.grid[x][y].dot) { | ||
297 | case Cell::Dot::BLACK: | ||
298 | break; | ||
299 | case Cell::Dot::BLUE: | ||
300 | flags |= DOT_IS_BLUE; | ||
301 | break; | ||
302 | case Cell::Dot::YELLOW: | ||
303 | flags |= DOT_IS_ORANGE; | ||
304 | break; | ||
305 | case Cell::Dot::INVISIBLE: | ||
306 | flags |= DOT_IS_INVISIBLE; | ||
307 | break; | ||
308 | } | ||
309 | _intersectionFlags.push_back(flags); | ||
310 | } | ||
311 | } | ||
312 | } | ||
313 | |||
314 | void PuzzleSerializer::WriteGaps(const Puzzle& p) { | ||
315 | for (int x=0; x<p.width; x++) { | ||
316 | for (int y=0; y<p.height; y++) { | ||
317 | if (x%2 == y%2) continue; // Cells are invalid, intersections are already handled. | ||
318 | if (p.grid[x][y].gap != Cell::Gap::BREAK) continue; | ||
319 | |||
320 | // We need to introduce a new segment which contains this dot. Break the existing segment, and add one. | ||
321 | int connectionLocation = -1; | ||
322 | for (int i=0; i<_connectionsA.size(); i++) { | ||
323 | auto [x1, y1] = loc_to_xy(p, _connectionsA[i]); | ||
324 | auto [x2, y2] = loc_to_xy(p, _connectionsB[i]); | ||
325 | if ((x1+1 == x && x2-1 == x && y1 == y && y2 == y) || | ||
326 | (y1+1 == y && y2-1 == y && x1 == x && x2 == x)) { | ||
327 | connectionLocation = i; | ||
328 | break; | ||
329 | } | ||
330 | } | ||
331 | if (connectionLocation == -1) continue; // @Error | ||
332 | |||
333 | auto [xPos, yPos] = xy_to_pos(p, x, y); | ||
334 | // Reminder: Y goes from 0.0 (bottom) to 1.0 (top) | ||
335 | if (x%2 == 0) { // Vertical gap | ||
336 | _connectionsA[connectionLocation] = xy_to_loc(p, x, y-1); | ||
337 | _connectionsB[connectionLocation] = static_cast<int>(_intersectionFlags.size()); | ||
338 | _intersectionLocations.push_back(xPos); | ||
339 | _intersectionLocations.push_back(yPos + VERTI_GAP_SIZE / 2); | ||
340 | _intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN); | ||
341 | |||
342 | _connectionsA.push_back(xy_to_loc(p, x, y+1)); | ||
343 | _connectionsB.push_back(static_cast<int>(_intersectionFlags.size())); | ||
344 | _intersectionLocations.push_back(xPos); | ||
345 | _intersectionLocations.push_back(yPos - VERTI_GAP_SIZE / 2); | ||
346 | _intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN); | ||
347 | } else if (y%2 == 0) { // Horizontal gap | ||
348 | _connectionsA[connectionLocation] = xy_to_loc(p, x-1, y); | ||
349 | _connectionsB[connectionLocation] = static_cast<int>(_intersectionFlags.size()); | ||
350 | _intersectionLocations.push_back(xPos - HORIZ_GAP_SIZE / 2); | ||
351 | _intersectionLocations.push_back(yPos); | ||
352 | _intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN); | ||
353 | |||
354 | _connectionsA.push_back(xy_to_loc(p, x+1, y)); | ||
355 | _connectionsB.push_back(static_cast<int>(_intersectionFlags.size())); | ||
356 | _intersectionLocations.push_back(xPos + HORIZ_GAP_SIZE / 2); | ||
357 | _intersectionLocations.push_back(yPos); | ||
358 | _intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN); | ||
359 | } | ||
360 | } | ||
361 | } | ||
362 | } | ||
363 | |||
364 | void PuzzleSerializer::WriteDecorations(const Puzzle& p, int id) { | ||
365 | if (!p.hasDecorations) return; | ||
366 | |||
367 | std::vector<int> decorations; | ||
368 | for (int y=p.height-2; y>0; y-=2) { | ||
369 | for (int x=1; x<p.width-1; x+=2) { | ||
370 | auto d = p.grid[x][y].decoration; | ||
371 | if (d) { | ||
372 | decorations.push_back(d->color | d->type | d->count | d->polyshape); | ||
373 | } else { | ||
374 | decorations.push_back(0); | ||
375 | } | ||
376 | } | ||
377 | } | ||
378 | |||
379 | _memory->WriteEntityData<int>(id, NUM_DECORATIONS, {static_cast<int>(decorations.size())}); | ||
380 | _memory->WriteArray<int>(id, DECORATIONS, decorations); | ||
381 | } | ||
382 | |||
383 | void PuzzleSerializer::WriteSequence(const Puzzle& p, int id) { | ||
384 | if (p.sequence.size() == 0) return; | ||
385 | |||
386 | std::vector<int> sequence; | ||
387 | for (Pos pos : p.sequence) { | ||
388 | // Only include intersections, the game does not treat segments as real objects | ||
389 | if (pos.x%2 == 0 && pos.y%2 == 0) { | ||
390 | sequence.emplace_back(xy_to_loc(p, pos.x, pos.y)); | ||
391 | } | ||
392 | } | ||
393 | |||
394 | Pos endpoint = p.sequence[p.sequence.size() - 1]; | ||
395 | for (auto [x, y, location] : _endpointLocations) { | ||
396 | if (x == endpoint.x && y == endpoint.y) { | ||
397 | sequence.emplace_back(location); | ||
398 | break; | ||
399 | } | ||
400 | } | ||
401 | |||
402 | _memory->WriteEntityData<int>(id, SEQUENCE_LEN, {static_cast<int>(sequence.size())}); | ||
403 | _memory->WriteNewArray<int>(id, SEQUENCE, sequence); | ||
404 | } | ||
405 | |||
406 | std::tuple<int, int> PuzzleSerializer::loc_to_xy(const Puzzle& p, int location) const { | ||
407 | int height2 = (p.height - 1) / 2; | ||
408 | int width2 = (p.width + 1) / 2; | ||
409 | |||
410 | int x = 2 * (location % width2); | ||
411 | int y = 2 * (height2 - location / width2); | ||
412 | return {x, y}; | ||
413 | } | ||
414 | |||
415 | int PuzzleSerializer::xy_to_loc(const Puzzle& p, int x, int y) const { | ||
416 | int height2 = (p.height - 1) / 2; | ||
417 | int width2 = (p.width + 1) / 2; | ||
418 | |||
419 | int rowsFromBottom = height2 - y/2; | ||
420 | return rowsFromBottom * width2 + x/2; | ||
421 | } | ||
422 | |||
423 | std::tuple<int, int> PuzzleSerializer::dloc_to_xy(const Puzzle& p, int location) const { | ||
424 | int height2 = (p.height - 3) / 2; | ||
425 | int width2 = (p.width - 1) / 2; | ||
426 | |||
427 | int x = 2 * (location % width2) + 1; | ||
428 | int y = 2 * (height2 - location / width2) + 1; | ||
429 | return {x, y}; | ||
430 | } | ||
431 | |||
432 | int PuzzleSerializer::xy_to_dloc(const Puzzle& p, int x, int y) const { | ||
433 | int height2 = (p.height - 3) / 2; | ||
434 | int width2 = (p.width - 1) / 2; | ||
435 | |||
436 | int rowsFromBottom = height2 - (y - 1)/2; | ||
437 | return rowsFromBottom * width2 + (x - 1)/2; | ||
438 | } | ||
439 | |||
440 | std::tuple<float, float> PuzzleSerializer::xy_to_pos(const Puzzle& p, int x, int y) const { | ||
441 | return { | ||
442 | MIN + (x/2.0f) * WIDTH_INTERVAL, | ||
443 | MAX - (y/2.0f) * HEIGHT_INTERVAL | ||
444 | }; | ||
445 | } | ||
446 | |||
447 | Cell::Dot PuzzleSerializer::FlagsToDot(int flags) const { | ||
448 | if (!(flags & Flags::HAS_DOT)) return Cell::Dot::NONE; | ||
449 | if (flags & Flags::DOT_IS_BLUE) return Cell::Dot::BLUE; | ||
450 | else if (flags & Flags::DOT_IS_ORANGE) return Cell::Dot::YELLOW; | ||
451 | else if (flags & Flags::DOT_IS_INVISIBLE) return Cell::Dot::INVISIBLE; | ||
452 | else return Cell::Dot::BLACK; | ||
453 | } | ||
454 | |||
455 | int PuzzleSerializer::FindConnection(int location) const { | ||
456 | for (int j=0; j<_connectionsA.size(); j++) { | ||
457 | if (_connectionsA[j] == location) return _connectionsB[j]; | ||
458 | if (_connectionsB[j] == location) return _connectionsA[j]; | ||
459 | } | ||
460 | return -1; | ||
461 | } | ||