From fc8649b12fc1280d81e8dd4d35736ed69c0d9909 Mon Sep 17 00:00:00 2001 From: jbzdarkid Date: Sat, 16 Nov 2019 14:22:39 -0800 Subject: Keep WIP --- Source/Memory.cpp | 22 ++- Source/PuzzlerSerializer.cpp | 2 + Source/Randomizer2.cpp | 310 ++++++++++++++++++++++--------------------- Source/Randomizer2Core.cpp | 164 +++++++++++++++++++++++ Source/Randomizer2Core.h | 21 +++ Source/Source.vcxproj | 2 + 6 files changed, 371 insertions(+), 150 deletions(-) create mode 100644 Source/Randomizer2Core.cpp create mode 100644 Source/Randomizer2Core.h diff --git a/Source/Memory.cpp b/Source/Memory.cpp index bc0725b..e240b90 100644 --- a/Source/Memory.cpp +++ b/Source/Memory.cpp @@ -170,7 +170,10 @@ void* Memory::ComputeOffset(std::vector offsets) { cumulativeAddress += offset; const auto search = _computedAddresses.find(cumulativeAddress); - if (search == std::end(_computedAddresses)) { + // This is an issue with re-randomization. Always. Just disable it in debug mode! +#ifdef NDEBUG + if (search == std::end(_computedAddresses)) { +#endif // If the address is not yet computed, then compute it. uintptr_t computedAddress = 0; if (bool result = !ReadProcessMemory(_handle, reinterpret_cast(cumulativeAddress), &computedAddress, sizeof(uintptr_t), NULL)) { @@ -180,7 +183,9 @@ void* Memory::ComputeOffset(std::vector offsets) { ThrowError(); } _computedAddresses[cumulativeAddress] = computedAddress; +#ifdef NDEBUG } +#endif cumulativeAddress = _computedAddresses[cumulativeAddress]; } @@ -188,6 +193,21 @@ void* Memory::ComputeOffset(std::vector offsets) { } uintptr_t Memory::Allocate(size_t bytes) { +/* +uintptr_t ForeignProcessMemory::AllocateMemory(size_t Size, DWORD Flags) const { + if (!ProcessHandle) { + return 0; + } + return (uintptr_t)VirtualAllocEx(ProcessHandle, nullptr, Size, MEM_RESERVE | MEM_COMMIT, Flags); +} + +void ForeignProcessMemory::DeallocateMemory(uintptr_t Addr) const { + if (!ProcessHandle || Addr == 0) { + return; + } + VirtualFreeEx(ProcessHandle, (void*)Addr, 0, MEM_RELEASE); +} +*/ uintptr_t current = _freeMem; _freeMem += bytes; diff --git a/Source/PuzzlerSerializer.cpp b/Source/PuzzlerSerializer.cpp index cf013ca..8ab1649 100644 --- a/Source/PuzzlerSerializer.cpp +++ b/Source/PuzzlerSerializer.cpp @@ -41,6 +41,8 @@ void PuzzleSerializer::WritePuzzle(const Puzzle& p, int id) { VERTI_GAP_SIZE = HEIGHT_INTERVAL / 2; WriteIntersections(p); + WriteDots(p); + WriteGaps(p); WriteEndpoints(p); WriteDecorations(p, id); WriteSequence(p, id); diff --git a/Source/Randomizer2.cpp b/Source/Randomizer2.cpp index b5218bf..81b6874 100644 --- a/Source/Randomizer2.cpp +++ b/Source/Randomizer2.cpp @@ -3,6 +3,7 @@ #include "Random.h" #include "Solver.h" #include "Memory.h" +#include "Randomizer2Core.h" #include "PuzzlerSerializer.h" #include #include @@ -155,173 +156,184 @@ void Randomizer2::Randomize() { } -void DebugColorGrid(const std::vector>& colorGrid) { - for (int y=0; y cutEdges = Randomizer2Core::CutEdgesToBeUnique(p); + assert(cutEdges.size() == 5); + Puzzle copy = p; + std::vector gates = {0x00344, 0x00488, 0x00489, 0x00495, 0x00496}; + for (int i=0; i>& 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); -} + // *** Hedges 2 *** + { + Puzzle p; + p.NewGrid(4, 4); -/* - undefined -> 1 (color of outside) or * (any colored cell) or -1 (edge/intersection not part of any region) + p.grid[2][1].gap = Cell::Gap::FULL; + p.grid[1][2].gap = Cell::Gap::FULL; + p.grid[5][2].gap = Cell::Gap::FULL; + p.grid[7][4].gap = Cell::Gap::FULL; + p.grid[4][5].gap = Cell::Gap::FULL; + p.grid[6][5].gap = Cell::Gap::FULL; + p.grid[1][6].gap = Cell::Gap::FULL; + p.grid[2][7].gap = Cell::Gap::FULL; + p.grid[5][8].gap = Cell::Gap::FULL; - 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); + p.grid[0][8].start = true; + p.grid[8][0].end = Cell::Dir::RIGHT; - for (int x=0; x cutEdges = Randomizer2Core::CutEdgesToBeUnique(p); + assert(cutEdges.size() == 7); + for (Pos pos : cutEdges) { + p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; + } + auto solutions = Solver::Solve(p); + assert(solutions.size() == 1); + + Puzzle q; + q.NewGrid(4, 4); + q.grid[0][8].start = true; + q.grid[8][0].end = Cell::Dir::RIGHT; + q.sequence = solutions[0].sequence; + for (Pos pos : cutEdges) { + q.grid[pos.x][pos.y].gap = Cell::Gap::FULL; } + // Cut to 4 of 9 additional edges (total: 11) + Randomizer2Core::CutEdgesNotOutsideNotBreakingSequence(q, 4); + PuzzleSerializer(_memory).WritePuzzle(q, 0x19DC); } + + // *** Hedges 3 ** + { + std::vector audioMarkers = { + 0x000034a9, + 0x000034b1, + 0x000034be, + 0x000034c4, + 0x000034cb, + 0x000034cc, + 0x000034cd, + 0x000034ce, + 0x000034df, + 0x000034e0, + 0x000034e1, + 0x000034e2, + 0x000034f3, + 0x000131cb, + 0x00017e34, + 0x00017e6f, + 0x00017e76, + 0x00017e77, + 0x00017e7a, + 0x00017e7e, + 0x00017e8b, + 0x00017e8d, + 0x00017eb5, + 0x000394a4, + 0x0003b54e, + }; + std::vector good; + for (int marker : audioMarkers) { + // std::vector assetName = _memory->ReadArray(marker, 0xD8, 100); + std::vector name = {'m', 'a', 'z', 'e', '_', 'p', 'e', 'b', 'b', 'l', 'e', '\0'}; + _memory->WriteNewArray(marker, 0xD8, name); + } - // @Future: Skip this loop if pillar = true; - for (int y=1; y cutEdges = Randomizer2Core::CutEdgesToBeUnique(p); + assert(cutEdges.size() == 7); + for (Pos pos : cutEdges) { + p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; } + PuzzleSerializer(_memory).WritePuzzle(p, 0x19E7); } - return colorGrid; -} + // *** Hedges 4 *** + { + Puzzle p; + p.NewGrid(4, 4); -void Randomizer2::RandomizeKeep() { - Puzzle p; - p.NewGrid(4, 4); - // p.width = 9; - // p.height = 10; - // p.grid.clear(); - // p.grid.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; x cutEdges = Randomizer2Core::CutEdgesToBeUnique(p); + assert(cutEdges.size() == 2); + for (Pos pos : cutEdges) { + p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; } + auto solutions = Solver::Solve(p); + assert(solutions.size() == 1); + + Puzzle q; + q.NewGrid(4, 4); + q.grid[0][8].start = true; + q.grid[4][0].end = Cell::Dir::UP; + q.sequence = solutions[0].sequence; + for (Pos pos : cutEdges) { + q.grid[pos.x][pos.y].gap = Cell::Gap::FULL; + } + // 9 cuts, -2 from existing cuts + Randomizer2Core::CutEdgesNotOutsideNotBreakingSequence(q, 7); + PuzzleSerializer(_memory).WritePuzzle(q, 0x1A0F); } - - auto solutions = Solver::Solve(copy); - assert(solutions.size() == 1); - p.sequence = solutions[0].sequence; - PuzzleSerializer(_memory).WritePuzzle(solutions[0], 0x139); } void Randomizer2::SetGate(int panel, int X, int Y) { diff --git a/Source/Randomizer2Core.cpp b/Source/Randomizer2Core.cpp new file mode 100644 index 0000000..2659076 --- /dev/null +++ b/Source/Randomizer2Core.cpp @@ -0,0 +1,164 @@ +#include "Randomizer2Core.h" +#include "Puzzle.h" +#include "Random.h" + +#include +#include +#include + +std::vector Randomizer2Core::CutEdgesToBeUnique(const Puzzle& p) { + auto [colorGrid, numColors] = CreateColorGrid(p); + std::vector edges; + for (int x=0; x edges; + for (int x=1; x cutEdges = CutEdgesInternal(p, colorGrid, edges, numEdges); + for (Pos pos : cutEdges) { + p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; + } +} + +std::vector Randomizer2Core::CutEdgesInternal(const Puzzle& p, std::vector>& colorGrid, std::vector& edges, size_t numEdges) { + std::vector cutEdges; + for (int i=0; i(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; x>& 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 Randomizer2Core::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::tuple>, int> Randomizer2Core::CreateColorGrid(const Puzzle& p) { + std::vector> colorGrid; + colorGrid.resize(p.width); + + for (int x=0; x + +struct Pos; +class Puzzle; + +class Randomizer2Core { +public: + // CAUTION: Does not actually cut edges, just returns a list of suggested cuts. + // Cuts a number of edges equal to the number of colors in the color grid. + static std::vector CutEdgesToBeUnique(const Puzzle& p); + static void CutEdgesNotOutsideNotBreakingSequence(Puzzle& p, size_t numEdges); + +private: + static std::vector CutEdgesInternal(const Puzzle& p, std::vector>& colorGrid, std::vector& edges, size_t numEdges); + static void DebugColorGrid(const std::vector>& colorGrid); + static void FloodFill(const Puzzle& p, std::vector>& colorGrid, int color, int x, int y); + static void FloodFillOutside(const Puzzle& p, std::vector>& colorGrid, int x, int y); + static std::tuple>, int> CreateColorGrid(const Puzzle& p); +}; + diff --git a/Source/Source.vcxproj b/Source/Source.vcxproj index 08e44a1..05f9ffa 100644 --- a/Source/Source.vcxproj +++ b/Source/Source.vcxproj @@ -165,6 +165,7 @@ + @@ -176,6 +177,7 @@ + -- cgit 1.4.1