From c60df0e75e63f488d94fd744ad70df8124dc7724 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Sat, 21 Aug 2021 17:12:29 -0400 Subject: Souped up the UI --- App/Main.cpp | 180 ++----------- App/Version.h | 8 +- Source/Randomizer2.cpp | 644 --------------------------------------------- Source/Randomizer2.h | 19 -- Source/Randomizer2Core.cpp | 203 -------------- Source/Randomizer2Core.h | 17 -- Source/Source.vcxproj | 4 - 7 files changed, 23 insertions(+), 1052 deletions(-) delete mode 100644 Source/Randomizer2.cpp delete mode 100644 Source/Randomizer2.h delete mode 100644 Source/Randomizer2Core.cpp delete mode 100644 Source/Randomizer2Core.h diff --git a/App/Main.cpp b/App/Main.cpp index 00bf29a..ad3d127 100644 --- a/App/Main.cpp +++ b/App/Main.cpp @@ -5,17 +5,12 @@ #include "Memory.h" #include "Random.h" #include "Randomizer.h" -#include "Randomizer2.h" #include "Panels_.h" #define HEARTBEAT 0x401 #define RANDOMIZE_READY 0x402 #define RANDOMIZING 0403 #define RANDOMIZE_DONE 0x404 -#define RANDOMIZE_CHALLENGE_DONE 0x405 -#define CHALLENGE_ONLY 0x406 -#define DISABLE_SNIPES 0x407 -#define SPEED_UP_AUTOSCROLLERS 0x408 /* ------- Temp ------- */ #include "Solver.h" @@ -23,10 +18,6 @@ #include #include -#define TMP1 0x501 -#define TMP2 0x502 -#define TMP3 0x503 -#define TMP4 0x504 HWND g_panelId; Puzzle g_puzzle; @@ -37,13 +28,11 @@ HWND g_rngDebug; // Globals HWND g_hwnd; -HWND g_seed; +//HWND g_seed; HWND g_randomizerStatus; HINSTANCE g_hInstance; auto g_witnessProc = std::make_shared(L"witness64_d3d11.exe"); std::shared_ptr g_randomizer; -std::shared_ptr g_randomizer2; -void SetRandomSeed(); LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message == WM_DESTROY) { @@ -64,171 +53,57 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) // Shut down randomizer, wait for startup if (g_randomizer) { g_randomizer = nullptr; - g_randomizer2 = nullptr; EnableWindow(g_randomizerStatus, FALSE); } break; case ProcStatus::Running: if (!g_randomizer) { g_randomizer = std::make_shared(g_witnessProc); - g_randomizer2 = std::make_shared(g_witnessProc); PostMessage(g_hwnd, WM_COMMAND, RANDOMIZE_READY, NULL); } break; case ProcStatus::NewGame: // This status will fire only once per new game - SetWindowText(g_seed, L""); + //SetWindowText(g_seed, L""); PostMessage(g_hwnd, WM_COMMAND, RANDOMIZE_READY, NULL); break; } break; case RANDOMIZE_READY: EnableWindow(g_randomizerStatus, TRUE); - if (IsDlgButtonChecked(hwnd, CHALLENGE_ONLY)) { - SetWindowText(g_randomizerStatus, L"Randomize Challenge"); - } else { - SetWindowText(g_randomizerStatus, L"Randomize"); - } + SetWindowText(g_randomizerStatus, L"Tutorialise"); break; case RANDOMIZING: if (!g_randomizer) break; // E.g. an enter press at the wrong time EnableWindow(g_randomizerStatus, FALSE); - SetRandomSeed(); std::thread([]{ - if (IsDlgButtonChecked(g_hwnd, DISABLE_SNIPES)) { - MEMORY_CATCH(g_randomizer->PreventSnipes()); - } - if (IsDlgButtonChecked(g_hwnd, SPEED_UP_AUTOSCROLLERS)) { - MEMORY_CATCH(g_randomizer->AdjustSpeed()); - } - if (IsDlgButtonChecked(g_hwnd, CHALLENGE_ONLY)) { - SetWindowText(g_randomizerStatus, L"Randomizing Challenge..."); - MEMORY_CATCH(g_randomizer->RandomizeChallenge()); - PostMessage(g_hwnd, WM_COMMAND, RANDOMIZE_CHALLENGE_DONE, NULL); - } else { - SetWindowText(g_randomizerStatus, L"Randomizing..."); - g_randomizer->Randomize(); - PostMessage(g_hwnd, WM_COMMAND, RANDOMIZE_DONE, NULL); - } + SetWindowText(g_randomizerStatus, L"Tutorialising..."); + g_randomizer->Randomize(); + PostMessage(g_hwnd, WM_COMMAND, RANDOMIZE_DONE, NULL); }).detach(); break; case RANDOMIZE_DONE: EnableWindow(g_randomizerStatus, FALSE); - SetWindowText(g_randomizerStatus, L"Randomized!"); - break; - case RANDOMIZE_CHALLENGE_DONE: - EnableWindow(g_randomizerStatus, FALSE); - SetWindowText(g_randomizerStatus, L"Randomized Challenge!"); - std::thread([]{ - // Allow re-randomization for challenge -- it doesn't break the rest of the game. - std::this_thread::sleep_for(std::chrono::seconds(10)); - PostMessage(g_hwnd, WM_COMMAND, RANDOMIZE_READY, NULL); - }).detach(); - break; - case CHALLENGE_ONLY: - CheckDlgButton(hwnd, CHALLENGE_ONLY, !IsDlgButtonChecked(hwnd, CHALLENGE_ONLY)); - if (IsWindowEnabled(g_randomizerStatus)) { - PostMessage(g_hwnd, WM_COMMAND, RANDOMIZE_READY, NULL); - } - break; - case DISABLE_SNIPES: - CheckDlgButton(hwnd, DISABLE_SNIPES, !IsDlgButtonChecked(hwnd, DISABLE_SNIPES)); - break; - case SPEED_UP_AUTOSCROLLERS: - CheckDlgButton(hwnd, SPEED_UP_AUTOSCROLLERS, !IsDlgButtonChecked(hwnd, SPEED_UP_AUTOSCROLLERS)); - break; - case TMP1: - { - std::wstring text(128, L'\0'); - int length = GetWindowText(g_panelId, text.data(), static_cast(text.size())); - text.resize(length); - std::wstringstream s; - int panelId; - s << text; - s >> std::hex >> panelId; - g_puzzle = PuzzleSerializer(g_witnessProc).ReadPuzzle(panelId); - } - break; - case TMP2: - { - std::wstring text(128, L'\0'); - int length = GetWindowText(g_panelId, text.data(), static_cast(text.size())); - text.resize(length); - std::wstringstream s; - int panelId; - s << text; - s >> std::hex >> panelId; - PuzzleSerializer(g_witnessProc).WritePuzzle(g_puzzle, panelId); - } + SetWindowText(g_randomizerStatus, L"Tutorialised!"); break; - case TMP3: - { - for (auto [key, value] : PANELS) { - std::stringstream out; - std::string name(value); - out << " {'id': 0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(5) << key << ", 'area':'"; - int k; - for (k=0; name[k] != ' '; k++) out << name[k]; - if (name[k+2] == ' ') { - out << name[k] << name[k+1]; - k += 2; - } - out << "', 'name':'"; - k++; - for (k; k < name.size(); k++) out << name[k]; - out << "', 'data':'"; - auto puzzle = PuzzleSerializer(g_witnessProc).ReadPuzzle(key); - out << puzzle.Serialize(); - out << "'},\r\n"; - DebugPrint(out.str()); - } - } - // Solver::Solve(g_puzzle); - break; - case TMP4: - SetRandomSeed(); - g_randomizer2->Randomize(); - case TMP5: - { - std::wstring text; - for (int i=0; i<10; i++) { - Random::SetSeed(i); - int rng = Random::RandInt(0, 999999); - text += std::to_wstring(rng) + L"\n"; - } - SetWindowText(g_rngDebug, text.c_str()); - } } } return DefWindowProc(hwnd, message, wParam, lParam); } -void SetRandomSeed() { - std::wstring text(128, L'\0'); - int length = GetWindowText(g_seed, text.data(), static_cast(text.size())); - if (length > 0) { // Set seed - text.resize(length); - Random::SetSeed(_wtoi(text.c_str())); - } else { // Random seed - int seed = Random::RandInt(0, 999999); - - text = std::to_wstring(seed); - SetWindowText(g_seed, text.c_str()); - CHARRANGE range = {0, static_cast(text.length())}; - PostMessage(g_seed, EM_EXSETSEL, NULL, (LPARAM)&range); - SetFocus(g_seed); - - Random::SetSeed(seed); - } -} - HWND CreateLabel(int x, int y, int width, LPCWSTR text) { return CreateWindow(L"STATIC", text, WS_TABSTOP | WS_VISIBLE | WS_CHILD | SS_LEFT, x, y, width, 16, g_hwnd, NULL, g_hInstance, NULL); } +HWND CreateMultiLabel(int x, int y, int width, int height, LPCWSTR text) { + return CreateWindow(L"STATIC", text, + WS_TABSTOP | WS_VISIBLE | WS_CHILD | SS_LEFT, + x, y, width, height, g_hwnd, NULL, g_hInstance, NULL); +} + HWND CreateText(int x, int y, int width, LPCWSTR defaultText = L"") { return CreateWindow(MSFTEDIT_CLASS, defaultText, WS_TABSTOP | WS_VISIBLE | WS_CHILD | WS_BORDER, @@ -271,32 +146,15 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance RECT rect; GetClientRect(GetDesktopWindow(), &rect); g_hwnd = CreateWindow(WINDOW_CLASS, PRODUCT_NAME, WS_OVERLAPPEDWINDOW, - rect.right - 550, 200, 500, 500, nullptr, nullptr, hInstance, nullptr); + rect.right - 550, 200, 500, 180, nullptr, nullptr, hInstance, nullptr); - CreateLabel(390, 15, 90, L"Version: " VERSION_STR); - g_seed = CreateText(10, 10, 100); - PostMessage(g_seed, EM_SETEVENTMASK, 0, ENM_KEYEVENTS); - g_randomizerStatus = CreateButton(120, 10, 180, L"Randomize", RANDOMIZING); + CreateMultiLabel(10, 10, 460, 86, L"This mod replaces most puzzles in the game with Tutorial Straight (the first puzzle in the tunnel where you start the game). Certain special panels are unaffected. Additionally, some panels (e.g. the tutorial gate, and every puzzle in Bunker) behave a little strangely now, and can be solved by simply double clicking in the middle of the panel."); + CreateLabel(390, 110, 90, L"Version: " VERSION_STR); + //g_seed = CreateText(10, 10, 100); + //PostMessage(g_seed, EM_SETEVENTMASK, 0, ENM_KEYEVENTS); + g_randomizerStatus = CreateButton(120, 105, 180, L"Tutorialise", RANDOMIZING); EnableWindow(g_randomizerStatus, FALSE); - CreateCheckbox(10, 300, CHALLENGE_ONLY); - CreateLabel(30, 300, 200, L"Randomize the challenge only"); - CreateCheckbox(10, 320, DISABLE_SNIPES); - CheckDlgButton(g_hwnd, DISABLE_SNIPES, TRUE); - CreateLabel(30, 320, 240, L"Disable Swamp and Shadows snipes"); - CreateCheckbox(10, 340, SPEED_UP_AUTOSCROLLERS); - CreateLabel(30, 340, 205, L"Speed up various autoscrollers"); - // CreateButton(200, 50, 200, L"Test RNG", TMP5); - // g_rngDebug = CreateWindow(L"STATIC", L"", - // WS_TABSTOP | WS_VISIBLE | WS_CHILD | SS_LEFT, - // 200, 80, 200, 200, g_hwnd, NULL, g_hInstance, NULL); -#ifndef NDEBUG - g_panelId = CreateText(200, 100, 100, L"59"); - CreateButton(200, 130, 100, L"Read", TMP1); - CreateButton(200, 160, 100, L"Write", TMP2); - CreateButton(200, 190, 100, L"Solve", TMP3); - CreateButton(200, 220, 100, L"Randomize2", TMP4); -#endif g_witnessProc->StartHeartbeat(g_hwnd, HEARTBEAT); diff --git a/App/Version.h b/App/Version.h index 1541697..204782f 100644 --- a/App/Version.h +++ b/App/Version.h @@ -3,12 +3,12 @@ #define TO_STRING2(s) L#s #define TO_STRING(s) TO_STRING2(s) -#define MAJOR 6 -#define MINOR 0 +#define MAJOR 0 +#define MINOR 1 #define PATCH 0 #define VERSION_STR TO_STRING(MAJOR) L"." TO_STRING(MINOR) L"." TO_STRING(PATCH) #define VERSION MAJOR, MINOR, PATCH -#define PRODUCT_NAME L"Witness Randomizer" -#define WINDOW_CLASS L"WitnessRandomizer" +#define PRODUCT_NAME L"Witness Tutorialiser Mod" +#define WINDOW_CLASS L"WitnessTutorialiser" diff --git a/Source/Randomizer2.cpp b/Source/Randomizer2.cpp deleted file mode 100644 index 421ce69..0000000 --- a/Source/Randomizer2.cpp +++ /dev/null @@ -1,644 +0,0 @@ -#include "pch.h" -#include "Randomizer2.h" -#include "PuzzleSerializer.h" -#include "Randomizer2Core.h" -#include "Random.h" -#include "Solver.h" -#include "Windows.h" - -Randomizer2::Randomizer2(const PuzzleSerializer& serializer) : _serializer(serializer) { -} - -void Randomizer2::Randomize() { - // RandomizeTutorial(); - // RandomizeGlassFactory(); - RandomizeSymmetryIsland(); - // RandomizeKeep(); -} - -void Randomizer2::RandomizeTutorial() { - { // Far center - Puzzle p; - p.NewGrid(4, 4); - p.grid[0][8].start = true; - p.grid[8][0].end = Cell::Dir::UP; - - for (Pos pos : Randomizer2Core::CutEdges(p, 14)) { - p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; - } - _serializer.WritePuzzle(p, 0x293); - } - - { // Center left - Puzzle p; - p.NewGrid(6, 6); - - int x = Random::RandInt(0, (p.width-1)/2)*2; - int y = Random::RandInt(0, (p.height-1)/2)*2; - int rng = Random::RandInt(1, 4); - if (rng == 1) p.grid[x][0].end = Cell::Dir::UP; - else if (rng == 2) p.grid[x][p.height-1].end = Cell::Dir::DOWN; - else if (rng == 3) p.grid[0][y].end = Cell::Dir::LEFT; - else if (rng == 4) p.grid[p.width-1][y].end = Cell::Dir::RIGHT; - - // [4/6/8][4/6/8] - p.grid[Random::RandInt(0, 2)*2 + 4][Random::RandInt(0, 2)*2 + 4].start = true; - - for (Pos pos : Randomizer2Core::CutEdges(p, 35)) { - p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; - } - - _serializer.WritePuzzle(p, 0x295); - } - - { // Far left - Puzzle p; - p.NewGrid(10, 10); - - p.grid[0][20].start = true; - p.grid[20][0].end = Cell::Dir::RIGHT; - - for (Pos pos : Randomizer2Core::CutEdges(p, 96)) { - p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; - } - _serializer.WritePuzzle(p, 0x2C2); - } - - { // Back left - Puzzle p; - p.NewGrid(6, 6); - - p.grid[0][12].start = true; - p.grid[12][0].end = Cell::Dir::RIGHT; - p.grid[12][12].end = Cell::Dir::RIGHT; - - for (Pos pos : Randomizer2Core::CutEdges(p, 27)) { - p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; - } - _serializer.WritePuzzle(p, 0xA3B5); - } - - { // Back right - Puzzle p; - p.NewGrid(6, 6); - - p.grid[0][12].start = true; - p.grid[12][12].start = true; - p.grid[6][0].end = Cell::Dir::UP; - - // @Cleanup - std::vector cuts; - bool toTheRight; - // Start by generating a cut line, to ensure one of the two startpoints is inaccessible - int x, y; - switch (Random::RandInt(1, 4)) { - case 1: - x = 1; y = 1; - toTheRight = true; - cuts.emplace_back(0, 1); - break; - case 2: - x = 1; y = 1; - toTheRight = true; - cuts.emplace_back(1, 0); - break; - case 3: - x = 11; y = 1; - toTheRight = false; - cuts.emplace_back(12, 1); - break; - case 4: - x = 11; y = 1; - toTheRight = false; - cuts.emplace_back(11, 0); - break; - } - while (y < p.height) { // The final cut will push y below the bottom of the puzzle, which means we're done. - switch (Random::RandInt(1, 4)) { - case 1: // Go right - if (x < p.width-2) { - cuts.emplace_back(x+1, y); - x += 2; - } - break; - case 2: // Go left - if (x > 1) { - cuts.emplace_back(x-1, y); - x -= 2; - } - break; - case 3: - case 4: // Go down (biased x2) - cuts.emplace_back(x, y+1); - y += 2; - break; - } - } - - for (Pos pos : cuts) { - p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; - } - - for (Pos pos : Randomizer2Core::CutEdges(p, 30 - cuts.size())) { - p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; - } - _serializer.WritePuzzle(p, 0xA3B2); - } -} - -void Randomizer2::RandomizeGlassFactory() { - { // Back wall 1 - Puzzle p; - p.NewGrid(3, 3); - p.symmetry = Puzzle::Symmetry::X; - p.grid[0][6].start = true; - p.grid[6][6].start = true; - p.grid[2][0].end = Cell::Dir::UP; - p.grid[4][0].end = Cell::Dir::UP; - - std::vector cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 2); - for (Pos pos : cutEdges) { - Pos sym = p.GetSymmetricalPos(pos.x, pos.y); - p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; - p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK; - } - _serializer.WritePuzzle(p, 0x86); - } - { // Back wall 2 - Puzzle p; - p.NewGrid(4, 4); - p.symmetry = Puzzle::Symmetry::X; - p.grid[0][8].start = true; - p.grid[8][8].start = true; - p.grid[2][0].end = Cell::Dir::UP; - p.grid[6][0].end = Cell::Dir::UP; - std::vector cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 4); - for (int i=0; i cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 10); - for (int i=0; i cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 15); - for (int i=0; i cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 3); - for (int i=0; i cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 7); - for (int i=0; i cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 15); - for (int i=0; i corners; - std::vector cells; - std::vector edges; - for (int x=0; x dots = Random::SelectFromSet(edges, 4); - for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::BLACK; - - auto solutions = Solver::Solve(p); - if (solutions.size() > 0 && solutions.size() < 10) break; - - for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::NONE; - } - - _serializer.WritePuzzle(p, 0xB0); - } - - { // Dots 1 - Puzzle p; - p.NewGrid(3, 3); - p.symmetry = Puzzle::Symmetry::Y; - p.grid[0][2].start = true; - p.grid[0][4].start = true; - p.grid[6][2].end = Cell::Dir::RIGHT; - p.grid[6][4].end = Cell::Dir::RIGHT; - - std::vector corners; - std::vector cells; - std::vector edges; - for (int x=0; x dots; - for (int j=0;; j++) { - dots = Random::SelectFromSet(edges, 3); - for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::BLACK; - - auto solutions = Solver::Solve(p); - if (solutions.size() == 2) break; - - for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::NONE; - } - - for (Pos pos : dots) { - Pos sym = p.GetSymmetricalPos(pos.x, pos.y); - p.grid[sym.x][sym.y].dot = Cell::Dot::BLACK; - } - - _serializer.WritePuzzle(p, 0x22); - } - { // Dots 2 - Puzzle p; - p.NewGrid(3, 3); - p.symmetry = Puzzle::Symmetry::Y; - p.grid[0][2].start = true; - p.grid[0][4].start = true; - p.grid[6][2].end = Cell::Dir::RIGHT; - p.grid[6][4].end = Cell::Dir::RIGHT; - - std::vector corners; - std::vector cells; - std::vector edges; - for (int x=0; x dots; - for (int j=0;; j++) { - dots = Random::SelectFromSet(edges, 3); - for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::BLACK; - - auto solutions = Solver::Solve(p); - if (solutions.size() == 2) break; - - for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::NONE; - } - - Pos pos = dots[1]; - Pos sym = p.GetSymmetricalPos(pos.x, pos.y); - p.grid[pos.x][pos.y].dot = Cell::Dot::NONE; - p.grid[sym.x][sym.y].dot = Cell::Dot::BLACK; - - _serializer.WritePuzzle(p, 0x23); - } -} - -void Randomizer2::RandomizeKeep() { - { // Hedges 1 - Puzzle p; - p.NewGrid(4, 4); - - p.grid[2][1].gap = Cell::Gap::FULL; - p.grid[4][1].gap = Cell::Gap::FULL; - p.grid[6][1].gap = Cell::Gap::FULL; - p.grid[3][2].gap = Cell::Gap::FULL; - p.grid[5][2].gap = Cell::Gap::FULL; - p.grid[8][3].gap = Cell::Gap::FULL; - p.grid[2][5].gap = Cell::Gap::FULL; - p.grid[6][5].gap = Cell::Gap::FULL; - p.grid[7][6].gap = Cell::Gap::FULL; - p.grid[2][7].gap = Cell::Gap::FULL; - p.grid[4][7].gap = Cell::Gap::FULL; - - p.grid[4][8].start = true; - p.grid[6][0].end = Cell::Dir::UP; - - std::vector cutEdges = Randomizer2Core::CutInsideEdges(p, 5); - Puzzle copy = p; - std::vector gates = {0x00344, 0x00488, 0x00489, 0x00495, 0x00496}; - for (int i=0; i cutEdges = Randomizer2Core::CutInsideEdges(p, 7); - for (Pos pos : cutEdges) { - p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; - } - auto solution = GetUniqueSolution(p); - - Puzzle q; - q.NewGrid(4, 4); - q.grid[0][8].start = true; - q.grid[8][0].end = Cell::Dir::RIGHT; - q.sequence = solution.sequence; - for (Pos pos : cutEdges) { - q.grid[pos.x][pos.y].gap = Cell::Gap::FULL; - } - // Cut to 6 of 9 additional edges - for (Pos pos : Randomizer2Core::CutInsideEdges(q, 6)) { - q.grid[pos.x][pos.y].gap = Cell::Gap::FULL; - } - _serializer.WritePuzzle(q, 0x19DC); - } - - { // Hedges 3 [WIP] - Puzzle p; - p.NewGrid(4, 4); - - p.grid[2][1].gap = Cell::Gap::FULL; - p.grid[5][2].gap = Cell::Gap::FULL; - p.grid[7][2].gap = Cell::Gap::FULL; - p.grid[4][3].gap = Cell::Gap::FULL; - p.grid[1][4].gap = Cell::Gap::FULL; - p.grid[6][5].gap = Cell::Gap::FULL; - p.grid[1][6].gap = Cell::Gap::FULL; - p.grid[3][6].gap = Cell::Gap::FULL; - p.grid[6][7].gap = Cell::Gap::FULL; - - p.grid[0][8].start = true; - p.grid[8][2].end = Cell::Dir::RIGHT; - - std::vector cutEdges = Randomizer2Core::CutInsideEdges(p, 7); - for (Pos pos : cutEdges) { - p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; - } - - std::vector pebbleMarkers = {0x034a9, 0x034b1, 0x034be, 0x034c4}; - - // _serializer.WritePuzzle(p, 0x19E7); - } - - { // Hedges 4 - Puzzle p; - p.NewGrid(4, 4); - - p.grid[3][0].gap = Cell::Gap::FULL; - p.grid[4][1].gap = Cell::Gap::FULL; - p.grid[8][1].gap = Cell::Gap::FULL; - p.grid[1][2].gap = Cell::Gap::FULL; - p.grid[4][3].gap = Cell::Gap::FULL; - p.grid[8][3].gap = Cell::Gap::FULL; - p.grid[1][4].gap = Cell::Gap::FULL; - p.grid[5][4].gap = Cell::Gap::FULL; - p.grid[2][5].gap = Cell::Gap::FULL; - p.grid[6][5].gap = Cell::Gap::FULL; - p.grid[3][6].gap = Cell::Gap::FULL; - p.grid[0][7].gap = Cell::Gap::FULL; - p.grid[8][7].gap = Cell::Gap::FULL; - p.grid[5][8].gap = Cell::Gap::FULL; - - p.grid[0][8].start = true; - p.grid[4][0].end = Cell::Dir::UP; - - std::vector cutEdges = Randomizer2Core::CutInsideEdges(p, 2); - for (Pos pos : cutEdges) { - p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; - } - auto solution = GetUniqueSolution(p); - - Puzzle q; - q.NewGrid(4, 4); - q.grid[0][8].start = true; - q.grid[4][0].end = Cell::Dir::UP; - q.sequence = solution.sequence; - for (Pos pos : cutEdges) { - q.grid[pos.x][pos.y].gap = Cell::Gap::FULL; - } - for (Pos pos : Randomizer2Core::CutInsideEdges(q, 7)) { - q.grid[pos.x][pos.y].gap = Cell::Gap::FULL; - } - _serializer.WritePuzzle(q, 0x1A0F); - } -} - -Puzzle Randomizer2::GetUniqueSolution(Puzzle& p) { - auto solutions = Solver::Solve(p); - assert(solutions.size() == 1); - return solutions[0]; -} - -void Randomizer2::SetGate(int panel, int X, int Y) { - float x, y, z, w; - if (X%2 == 0 && Y%2 == 1) { // Horizontal - x = -1.49f * X + 0.22f * Y + 66.58f; - y = 0.275f * X + 1.6f * Y + 108.4f; - z = -.77f; - w = .63f; - } else { // Vertical - assert(X%2 == 1 && Y%2 == 0); - x = -1.6f * X + 0.35f * Y + 66.5f; - y = 0.25f * X + 1.6f * Y + 108.55f; - z = -0.1f; - w = 1.0f; - } - - SetPos(panel, x, y, 19.2f); - // _memory->WriteEntityData(panel, ORIENTATION, {0.0f, 0.0f, z, w}); -} - -void Randomizer2::SetPos(int panel, float x, float y, float z) { - // _memory->WriteEntityData(panel, POSITION, {x, y, z}); -} \ No newline at end of file diff --git a/Source/Randomizer2.h b/Source/Randomizer2.h deleted file mode 100644 index a2b5ebd..0000000 --- a/Source/Randomizer2.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include "PuzzleSerializer.h" - -class Randomizer2 { -public: - Randomizer2(const PuzzleSerializer& serializer); - void Randomize(); - void RandomizeTutorial(); - void RandomizeGlassFactory(); - void RandomizeSymmetryIsland(); - void RandomizeKeep(); - -private: - Puzzle GetUniqueSolution(Puzzle& p); - void SetGate(int panel, int X, int Y); - void SetPos(int panel, float x, float y, float z); - - PuzzleSerializer _serializer; -}; diff --git a/Source/Randomizer2Core.cpp b/Source/Randomizer2Core.cpp deleted file mode 100644 index 867fa5a..0000000 --- a/Source/Randomizer2Core.cpp +++ /dev/null @@ -1,203 +0,0 @@ -#include "pch.h" -#include "Randomizer2Core.h" -#include "Random.h" - -std::vector Randomizer2Core::CutEdges(const Puzzle& p, size_t numEdges) { - return CutEdgesInternal(p, 0, p.width, 0, p.height, numEdges); -} - -std::vector Randomizer2Core::CutInsideEdges(const Puzzle& p, size_t numEdges) { - return CutEdgesInternal(p, 1, p.width-1, 1, p.height-1, numEdges); -} - -std::vector Randomizer2Core::CutSymmetricalEdgePairs(const Puzzle& p, size_t numEdges) { - Puzzle copy = p; - // Prevent cuts from landing on the midline - if (p.symmetry == Puzzle::Symmetry::X) { - for (int y=0; y Randomizer2Core::CutEdgesInternal(const Puzzle& p, int xMin, int xMax, int yMin, int yMax, size_t numEdges) { - std::vector edges; - for (int x=xMin; x y) continue; // Only allow cuts bottom-left of the diagonal - } - - // If the puzzle already has a sequence, don't cut along it. - bool inSequence = false; - for (Pos pos : p.sequence) inSequence |= (pos.x == x && pos.y == y); - if (inSequence) continue; - edges.emplace_back(x, y); - } - } - assert(numEdges <= edges.size()); - - auto [colorGrid, numColors] = CreateColorGrid(p); - assert(numEdges <= numColors); - - // @Hack... sort of. I couldn't think of a better way to do this. - if (p.symmetry == Puzzle::Symmetry::XY) { - // Recolor the diagonal so that opposite cells share a color. This is because we're only cutting along half their edges, - // so they are in fact two sides of the same cell. - for (int x=1; x cutEdges; - for (int i=0; i 0) { - int edge = Random::RandInt(0, static_cast(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 -#endif - -void Randomizer2Core::DebugColorGrid(const std::vector>& colorGrid) { -#ifndef NDEBUG - static std::string colors = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - 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::NONE) return; // Only flood-fill through 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); -} - -// Color key: -// 0 (default): Uncolored -// 1: Outside color and separator color -// 2+: Flood-filled region color -std::tuple>, int> Randomizer2Core::CreateColorGrid(const Puzzle& p) { - std::vector> colorGrid; - colorGrid.resize(p.width); - - for (int x=0; x CutEdges(const Puzzle& p, size_t numEdges); - static std::vector CutInsideEdges(const Puzzle& p, size_t numEdges); - static std::vector CutSymmetricalEdgePairs(const Puzzle& p, size_t numEdges); - -private: - static std::vector CutEdgesInternal(const Puzzle& p, int xMin, int xMax, int yMin, int yMax, 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 1cfb484..8d6104c 100644 --- a/Source/Source.vcxproj +++ b/Source/Source.vcxproj @@ -172,8 +172,6 @@ - - @@ -190,8 +188,6 @@ - - -- cgit 1.4.1