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