diff options
Diffstat (limited to 'Source/Panel.cpp')
-rw-r--r-- | Source/Panel.cpp | 337 |
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 | ||
9 | template <class T> | 7 | PuzzleSerializer::PuzzleSerializer(const std::shared_ptr<Memory>& memory) : _memory(memory) {} |
10 | int 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 | |||
17 | Panel::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 | |||
27 | void 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 | |||
35 | nlohmann::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 | ||
73 | void Panel::ReadDecorations(int id) { | 9 | Puzzle 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 | |||
83 | void 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 | ||
95 | void Panel::ReadIntersections(int id) { | 22 | void 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 | ||
167 | void Panel::WriteIntersections(int id) { | 93 | void 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 | |||
117 | void 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 | |||
127 | void 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 | ||
248 | void 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 | |||
265 | std::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 | |||
274 | int 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 | |||
282 | std::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 | |||
291 | int 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 | |||
299 | Cell::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 | } | ||