#include "Randomizer2.h" #include "Puzzle.h" #include "Random.h" #include "Solver.h" #include "Memory.h" #include "PuzzlerSerializer.h" #include #include void FloodFillInternal(const Puzzle& p, std::vector>& reached, int x, int y) { if (x%2 == 1 && y%2 == 1) return; auto cell = p.GetCell(x, y); if (cell.undefined) return; if (cell.gap != Cell::Gap::NONE) return; if (reached[x][y]) return; reached[x][y] = true; FloodFillInternal(p, reached, x-1, y); FloodFillInternal(p, reached, x+1, y); FloodFillInternal(p, reached, x, y-1); FloodFillInternal(p, reached, x, y+1); } // Returns true: All nodes reachable / false: Some node disconnected bool FloodFill(const Puzzle& p) { std::vector> reached; reached.resize(p.width); for (int x=0; x& memory) : _memory(memory) {} void Randomizer2::Randomize() { // 4x4 // 14 gaps // start (x=0, y=8) // end (x=8, y=0) Up // 1 solution Puzzle p; while (true) { p.NewGrid(4, 4); std::vector corners; std::vector cells; std::vector edges; for (int x=0; x(edges.size() - 1)); Pos pos = edges[edge]; edges.erase(edges.begin() + edge); if (FloodFill(p)) { p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; break; } else { p.grid[pos.x][pos.y].gap = Cell::Gap::NONE; } } } p.grid[0][8].start = true; p.grid[8][0].end = Cell::Dir::UP; auto solutions = Solver::Solve(p); if (solutions.size() > 0) break; } PuzzleSerializer(_memory).WritePuzzle(p, 0x293); // 7x7 // 35 gaps // start (x=8, y=8) // end (x=4, y=0) Up // 2 solutions, 37 & 39 while (true) { p.NewGrid(7, 7); std::vector corners; std::vector cells; std::vector edges; for (int x=0; x(edges.size() - 1)); Pos pos = edges[edge]; edges.erase(edges.begin() + edge); if (FloodFill(p)) { p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; break; } else { p.grid[pos.x][pos.y].gap = Cell::Gap::NONE; } } } switch (Random::RandInt(1, 4)) { case 1: p.grid[Random::RandInt(0, p.width-1)][0].end = Cell::Dir::UP; break; case 2: p.grid[Random::RandInt(0, p.width-1)][p.height-1].end = Cell::Dir::DOWN; break; case 3: p.grid[0][Random::RandInt(0, p.height-1)].end = Cell::Dir::LEFT; break; case 4: p.grid[p.width-1][Random::RandInt(0, p.height-1)].end = Cell::Dir::RIGHT; break; } switch (Random::RandInt(1, 3)) { case 1: // Horiz (6) [5/7][4/6/8] p.grid[Random::RandInt(0, 1)*2 + 5][Random::RandInt(0, 2)*2 + 4].start = true; break; case 2: // Verti (6) [4/6/8][5/7] p.grid[Random::RandInt(0, 2)*2 + 4][Random::RandInt(0, 1)*2 + 5].start = true; break; case 3: // Inter (9) [4/6/8][4/6/8] p.grid[Random::RandInt(0, 2)*2 + 4][Random::RandInt(0, 2)*2 + 4].start = true; break; } auto solutions = Solver::Solve(p); if (solutions.size() > 0) break; } PuzzleSerializer(_memory).WritePuzzle(p, 0x295); } void DebugColorGrid(const std::vector>& colorGrid) { for (int y=0; y>& colorGrid, int color, int x, int y) { if (!p.SafeCell(x, y)) return; if (colorGrid[x][y] != 0) return; // Already processed. colorGrid[x][y] = color; FloodFill(p, colorGrid, color, x, y+1); FloodFill(p, colorGrid, color, x, y-1); FloodFill(p, colorGrid, color, x+1, y); FloodFill(p, colorGrid, color, x-1, y); } void FloodFillOutside(const Puzzle&p, std::vector>& colorGrid, int x, int y) { if (!p.SafeCell(x, y)) return; if (colorGrid[x][y] != 0) return; // Already processed. if (x%2 != y%2 && p.grid[x][y].gap != Cell::Gap::FULL) return; // Only flood-fill through full gaps colorGrid[x][y] = 1; // Outside color FloodFillOutside(p, colorGrid, x, y+1); FloodFillOutside(p, colorGrid, x, y-1); FloodFillOutside(p, colorGrid, x+1, y); FloodFillOutside(p, colorGrid, x-1, y); } /* undefined -> 1 (color of outside) or * (any colored cell) or -1 (edge/intersection not part of any region) 0 -> {} (this is a special edge case, which I don't need right now) 1 -> 0 (uncolored / ready to color) 2 -> */ std::vector> CreateColorGrid(const Puzzle& p) { std::vector> colorGrid; colorGrid.resize(p.width); for (int x=0; x edges; for (int x=0; x gates = {0x00344, 0x00488, 0x00489, 0x00495, 0x00496}; for (int i=0; i<5; i++) { for (int j=0; j(edges.size() - 1)); Pos pos = edges[edge]; edges.erase(edges.begin() + edge); int color1 = 0; int color2 = 0; if (pos.x%2 == 0 && pos.y%2 == 1) { // Vertical if (pos.x > 0) color1 = colorGrid[pos.x-1][pos.y]; else color1 = 1; if (pos.x < p.width - 1) color2 = colorGrid[pos.x+1][pos.y]; else color2 = 1; } else { // Horizontal assert(pos.x%2 == 1 && pos.y%2 == 0); if (pos.y > 0) color1 = colorGrid[pos.x][pos.y-1]; else color1 = 1; if (pos.y < p.height - 1) color2 = colorGrid[pos.x][pos.y+1]; else color2 = 1; } // Enforce color1 < color2 if (color1 > color2) std::swap(color1, color2); // Colors mismatch, valid cut if (color1 != color2) { // @Performance... have a lookup table instead? for (int x=0; xWritePanelData(panel, ORIENTATION, {0.0f, 0.0f, z, w}); } void Randomizer2::SetPos(int panel, float x, float y, float z) { _memory->WritePanelData(panel, POSITION, {x, y, z}); }