diff --git a/visualizer/.DS_Store b/visualizer/.DS_Store new file mode 100644 index 0000000..5172429 Binary files /dev/null and b/visualizer/.DS_Store differ diff --git a/visualizer/.clang-format b/visualizer/.clang-format new file mode 100755 index 0000000..33bf2a3 --- /dev/null +++ b/visualizer/.clang-format @@ -0,0 +1,137 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + - Regex: '.*' + Priority: 1 + SortPriority: 0 +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentCaseLabels: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +Standard: Latest +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +... + diff --git a/visualizer/.gitignore b/visualizer/.gitignore new file mode 100755 index 0000000..04fd5e6 --- /dev/null +++ b/visualizer/.gitignore @@ -0,0 +1,3 @@ +__pycache__ +build/ +.vscode/ diff --git a/visualizer/CMakeLists.txt b/visualizer/CMakeLists.txt new file mode 100755 index 0000000..663fbb1 --- /dev/null +++ b/visualizer/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.16) +project(push_swap_visualizer + LANGUAGES CXX + VERSION 1.0 +) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +add_subdirectory(dependencies) +add_subdirectory(src) +add_subdirectory(tests) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/imgui.ini ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/imgui.ini COPYONLY) diff --git a/visualizer/README.md b/visualizer/README.md new file mode 100755 index 0000000..a3e99db --- /dev/null +++ b/visualizer/README.md @@ -0,0 +1,34 @@ +# Push Swap Visualizer +This project is a visualizer for the **"PUSH_SWAP"** 42 School Project. + +**Push Swap** is a program that takes as argument a space separated list of numbers and outputs a list of commands that can be used to sort them. + +You can read the subject [here](https://github.com/Binary-Hackers/42_Subjects/blob/master/00_Projects/02_Algorithmic/push_swap.pdf). + +## Usage +- In the **Values** window + - Choose the size of the push swap input with the slider [Optional] + - **Shuffle** the input [Optional] + - The space separated values should be filled automatically, you can also put your own values + - Set the Push Swap program path (Absolute or relative path). + - **Compute** the sort commands, it will display OK when done. +- In the **Controls** window + - **Load** the commands in the visualizer + - **Start** the animation + - Adjust the **Speed** as needed + - **Pause** and go **Step** by step to see the details of your algorithm. + - **Load** to restart the animation + +## Install +This project uses C++17, cmake, SFML and ImGui. +- Install a C++ compiler (gcc, clang,...) +- Install cmake +- Move push_swap_visualizer inside push_swap +- Inside push_swap_visualizer, mkdir : + - 'build' +- cd in the build folder and type : + - 'cmake ..' + - 'make' +- run the visualizer with ./bin/visualizer + +![](https://i.imgur.com/zqcsZfY.png) diff --git a/visualizer/dependencies/CMakeLists.txt b/visualizer/dependencies/CMakeLists.txt new file mode 100755 index 0000000..17c0ab5 --- /dev/null +++ b/visualizer/dependencies/CMakeLists.txt @@ -0,0 +1,31 @@ +include(FetchContent) + +FetchContent_Declare( + sfml + URL https://github.com/SFML/SFML/releases/download/2.5.1/SFML-2.5.1-sources.zip +) + +FetchContent_Declare( + imgui + GIT_REPOSITORY https://github.com/ocornut/imgui + GIT_TAG 35b1148efb839381b84de9290d9caf0b66ad7d03 +) + +FetchContent_MakeAvailable(imgui) + +FetchContent_Declare( + imgui-sfml + GIT_REPOSITORY https://github.com/eliasdaler/imgui-sfml + GIT_TAG 82dc2033e51b8323857c3ae1cf1f458b3a933c35 +) + +add_subdirectory(imgui-sfml) +add_subdirectory(sfml) + +FetchContent_Declare( + catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v2.13.7 +) + +add_subdirectory(catch2) diff --git a/visualizer/dependencies/catch2/CMakeLists.txt b/visualizer/dependencies/catch2/CMakeLists.txt new file mode 100755 index 0000000..9eddf24 --- /dev/null +++ b/visualizer/dependencies/catch2/CMakeLists.txt @@ -0,0 +1,4 @@ +message(STATUS "Fetching Catch2...") + +FetchContent_MakeAvailable(catch2) +list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/contrib) diff --git a/visualizer/dependencies/imgui-sfml/CMakeLists.txt b/visualizer/dependencies/imgui-sfml/CMakeLists.txt new file mode 100755 index 0000000..9b8f449 --- /dev/null +++ b/visualizer/dependencies/imgui-sfml/CMakeLists.txt @@ -0,0 +1,7 @@ +message(STATUS "Fetching ImGui-SFML...") + +set(IMGUI_DIR ${imgui_SOURCE_DIR}) +set(IMGUI_SFML_FIND_SFML OFF) +set(IMGUI_SFML_IMGUI_DEMO OFF) + +FetchContent_MakeAvailable(imgui-sfml) diff --git a/visualizer/dependencies/sfml/CMakeLists.txt b/visualizer/dependencies/sfml/CMakeLists.txt new file mode 100755 index 0000000..a09f298 --- /dev/null +++ b/visualizer/dependencies/sfml/CMakeLists.txt @@ -0,0 +1,7 @@ +message(STATUS "Fetching SFML...") + +# No need to build audio and network modules +set(SFML_BUILD_AUDIO FALSE) +set(SFML_BUILD_NETWORK FALSE) + +FetchContent_MakeAvailable(sfml) diff --git a/visualizer/imgui.ini b/visualizer/imgui.ini new file mode 100755 index 0000000..941895e --- /dev/null +++ b/visualizer/imgui.ini @@ -0,0 +1,19 @@ +[Window][Debug##Default] +Pos=60,60 +Size=400,400 +Collapsed=0 + +[Window][Controls] +Pos=975,47 +Size=205,101 +Collapsed=0 + +[Window][Commands] +Pos=1060,459 +Size=167,320 +Collapsed=0 + +[Window][Values] +Pos=1212,140 +Size=182,207 +Collapsed=0 diff --git a/visualizer/include/gui.h b/visualizer/include/gui.h new file mode 100755 index 0000000..6bb5b43 --- /dev/null +++ b/visualizer/include/gui.h @@ -0,0 +1,39 @@ +#pragma once + +#include "pushswap.h" +#include "queues.h" +#include +#include +#include +#include +#include + +class Gui { +private: + enum class STATE {Running, Reverse, Stopped}; + Queues queues; + PushSwap pushswap; + + std::vector barsA; + std::vector barsB; + int generateNumberSize; + std::string numbers; + int speed; + STATE state; + float scale; + + void _updateControls(); + void _drawBars(); + void _animateQueue(sf::Clock &clock); + std::list _generateValues(const unsigned int size); + void _updateBars(); + sf::Color _rgb(const double ratio); + +public: + sf::RenderWindow _window; + + Gui(); + ~Gui(); + + void loop(); +}; diff --git a/visualizer/include/pushswap.h b/visualizer/include/pushswap.h new file mode 100755 index 0000000..987f052 --- /dev/null +++ b/visualizer/include/pushswap.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +class PushSwap { +private: + std::list _split(const std::string &input, const char delimitor); + +public: + PushSwap(); + ~PushSwap(); + void run(const std::string &numbers); + + std::string path; + std::list commands; +}; diff --git a/visualizer/include/queues.h b/visualizer/include/queues.h new file mode 100755 index 0000000..c80016d --- /dev/null +++ b/visualizer/include/queues.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include + +class Queues { + enum COMMAND { NONE, SA, SB, SS, PA, PB, RA, RB, RR, RRA, RRB, RRR }; + std::map commandMap; + +public: + Queues(); + ~Queues(); + + void step(); + void stepBack(); + void start(const std::list &start); + + std::list commands; + std::list executedCommands; + std::list queueA; + std::list queueB; + +private: + void _executeCommand(const std::string &cmd); + void _executeReverseCommand(const std::string &cmd); + std::list _normalize(const std::list &numbers); + void _sa(); + void _sb(); + void _ss(); + void _pa(); + void _pb(); + void _ra(); + void _rb(); + void _rr(); + void _rra(); + void _rrb(); + void _rrr(); +}; diff --git a/visualizer/include/utils.h b/visualizer/include/utils.h new file mode 100755 index 0000000..e2194d3 --- /dev/null +++ b/visualizer/include/utils.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace Utils { +std::list SplitStringToString(const std::string &s, + const char delimitor); +std::list SplitStringToInt(const std::string &s, + const char delimitor); + +} // namespace Utils diff --git a/visualizer/src/CMakeLists.txt b/visualizer/src/CMakeLists.txt new file mode 100755 index 0000000..04d36b5 --- /dev/null +++ b/visualizer/src/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library(VisualizerLib STATIC utils.cpp queues.cpp pushswap.cpp gui.cpp) + +target_link_libraries(VisualizerLib PUBLIC ImGui-SFML::ImGui-SFML) +target_include_directories(VisualizerLib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include") +target_compile_features(VisualizerLib PRIVATE cxx_std_17) +target_compile_options(VisualizerLib PRIVATE +$<$:/W4 /WX> +$<$>:-Wall -Wextra -Wpedantic -Werror> +) + +add_executable(visualizer main.cpp) +target_compile_features(visualizer PRIVATE cxx_std_17) +target_link_libraries(visualizer PRIVATE VisualizerLib) diff --git a/visualizer/src/gui.cpp b/visualizer/src/gui.cpp new file mode 100755 index 0000000..b0df57a --- /dev/null +++ b/visualizer/src/gui.cpp @@ -0,0 +1,236 @@ +#include "gui.h" +#include "utils.h" +#include +#include +#include +#include +#include +#include + +Gui::Gui() + : generateNumberSize{0}, speed{1}, state{STATE::Stopped}, scale{1.0f}, + _window{sf::VideoMode::getDesktopMode(), "Push Swap Visualizer"} { + _window.setFramerateLimit(60); +} +Gui::~Gui() {} + +std::list Gui::_generateValues(const unsigned int size) { + std::vector values; + for (unsigned int v = 1; v <= size; ++v) { + values.push_back(v); + } + std::shuffle(values.begin(), values.end(), + std::mt19937{std::random_device{}()}); + std::list shuffledList; + std::move(values.begin(), values.end(), back_inserter(shuffledList)); + return shuffledList; +} + +void Gui::_updateBars() { + this->barsA.clear(); + this->barsB.clear(); + const sf::Vector2f windowSize = this->_window.getDefaultView().getSize(); + const uint64_t queuesSize{this->queues.queueA.size() + + this->queues.queueB.size()}; + if (queuesSize == 0) { + return; + } + const float barWidth = static_cast(windowSize.y) / queuesSize; + const float barUnit = static_cast(windowSize.x) / (2 * queuesSize + 2); + + const auto updateBar = [=](std::list queue, + std::vector &bar, + int deltaX = 0) { + int index{0}; + for (const int val : queue) { + bar.push_back( + sf::RectangleShape(sf::Vector2f((1 + val) * barUnit, barWidth))); + bar.back().setPosition(deltaX, index * barWidth); + bar.back().setFillColor(this->_rgb(static_cast(val) / queuesSize)); + index++; + } + }; + + updateBar(this->queues.queueA, this->barsA); + updateBar(this->queues.queueB, this->barsB, windowSize.x / 2); +} + +void Gui::_updateControls() { + ImGui::Begin("Controls"); + ImGui::SliderInt("Speed", &this->speed, 1, 500, "%i/s"); + + if (ImGui::Button("Start")) { + this->state = STATE::Running; + } + + ImGui::SameLine(); + if (ImGui::Button("Reverse")) { + this->state = STATE::Reverse; + } + + ImGui::SameLine(); + if (ImGui::Button("Pause")) { + this->state = STATE::Stopped; + } + + ImGui::SameLine(); + if (ImGui::Button("Step")) { + this->state = STATE::Stopped; + this->queues.step(); + } + + ImGui::SameLine(); + if (ImGui::Button("Step Back")) { + this->state = STATE::Stopped; + this->queues.stepBack(); + } + + ImGui::SliderFloat("Scale UI", &this->scale, 0.5f, 3.0f, "%.2f"); + ImGui::End(); + + ImGui::Begin("Values"); + ImGui::Text("Values to generate"); + ImGui::InputInt("Count", &this->generateNumberSize); + if (this->generateNumberSize < 0) { + this->generateNumberSize = 0; + } + + if (ImGui::Button("Shuffle")) { + unsigned int size = static_cast(this->generateNumberSize); + std::list valueInts = this->_generateValues(size); + std::string values; + for (const int value : valueInts) { + values += std::to_string(value) + ' '; + } + this->numbers = values; + } + + ImGui::Text("Space separated values"); + ImGui::InputText("Values", &this->numbers); + + ImGui::Text("push_swap file path"); + ImGui::InputText("", &this->pushswap.path); + if (ImGui::Button("Compute")) { + this->pushswap.run(this->numbers); + this->state = STATE::Stopped; + this->queues.start(Utils::SplitStringToInt(this->numbers, ' ')); + this->queues.commands = this->pushswap.commands; + this->queues.executedCommands.clear(); + } + + std::string status{"..."}; + if (!this->pushswap.commands.empty()) { + status = "OK"; + } + ImGui::SameLine(); + ImGui::Text("%s", status.c_str()); + + ImGui::End(); + + ImGui::Begin("Commands"); + ImGui::Text("Count: %zu", this->pushswap.commands.size()); + ImGui::BeginChild("Scrolling"); + for (const auto &cmd : this->queues.commands) { + ImGui::Text("%s", cmd.c_str()); + } + ImGui::EndChild(); + ImGui::End(); +} + +void Gui::_drawBars() { + for (const auto &shape : this->barsA) { + this->_window.draw(shape); + } + for (const auto &shape : this->barsB) { + this->_window.draw(shape); + } +} + +void Gui::_animateQueue(sf::Clock &clock) { + float delta = clock.getElapsedTime().asSeconds(); + if (delta >= (1.0 / this->speed)) { + clock.restart(); + } + switch (this->state) { + case STATE::Running: { + int steps = static_cast(delta * this->speed); + for (int i = 0; i < steps; ++i) { + this->queues.step(); + } + break; + } + case STATE::Reverse: { + int steps = static_cast(delta * this->speed); + for (int i = 0; i < steps; ++i) { + this->queues.stepBack(); + } + break; + } + case STATE::Stopped: + break; + } +} + +void Gui::loop() { + ImGui::SFML::Init(_window); + + sf::Clock deltaClock; + sf::Clock stepClock; + while (_window.isOpen()) { + sf::Event event; + while (_window.pollEvent(event)) { + ImGui::SFML::ProcessEvent(event); + + if (event.type == sf::Event::Closed) { + _window.close(); + } + } + ImGui::GetIO().FontGlobalScale = this->scale; + this->_updateBars(); + ImGui::SFML::Update(this->_window, deltaClock.restart()); + + this->_updateControls(); + + this->_animateQueue(stepClock); + + _window.clear(); + + this->_drawBars(); + + ImGui::SFML::Render(_window); + _window.display(); + } + ImGui::SFML::Shutdown(); +} + +sf::Color Gui::_rgb(const double ratio) { + int normalized = static_cast(ratio * 256 * 4); + int region = normalized / 256; + int x = normalized % 256; + + sf::Color color; + color.a = 255; + switch (region) { + case 0: + color.r = 0; + color.g = x; + color.b = 255; + break; + case 1: + color.r = 0; + color.g = 255; + color.b = 255 - x; + break; + case 2: + color.r = x; + color.g = 255; + color.b = 0; + break; + case 3: + color.r = 255; + color.g = 255 - x; + color.b = 0; + break; + } + return color; +} diff --git a/visualizer/src/main.cpp b/visualizer/src/main.cpp new file mode 100755 index 0000000..08f5788 --- /dev/null +++ b/visualizer/src/main.cpp @@ -0,0 +1,7 @@ +#include "gui.h" + +int main() { + Gui gui; + gui.loop(); + return 0; +} diff --git a/visualizer/src/pushswap.cpp b/visualizer/src/pushswap.cpp new file mode 100755 index 0000000..6b10e1e --- /dev/null +++ b/visualizer/src/pushswap.cpp @@ -0,0 +1,27 @@ +#include "pushswap.h" +#include "utils.h" +#include +#include +#include +#include +#include + +PushSwap::PushSwap() : path{"./push_swap"} {} +PushSwap::~PushSwap() {} + +void PushSwap::run(const std::string &numbers) { + this->commands.clear(); + std::array buffer; + std::string result; + std::string command = this->path + " " + numbers; + std::unique_ptr pipe(popen(command.c_str(), "r"), + pclose); + + if (!pipe) { + throw std::runtime_error("popen() failed!"); + } + while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { + result += buffer.data(); + } + this->commands = Utils::SplitStringToString(result, '\n'); +} diff --git a/visualizer/src/queues.cpp b/visualizer/src/queues.cpp new file mode 100755 index 0000000..9da5b00 --- /dev/null +++ b/visualizer/src/queues.cpp @@ -0,0 +1,221 @@ +#include "queues.h" +#include +#include +#include + +Queues::Queues() + : commandMap{ + {"sa", COMMAND::SA}, {"sb", COMMAND::SB}, {"ss", COMMAND::SS}, + {"pa", COMMAND::PA}, {"pb", COMMAND::PB}, {"ra", COMMAND::RA}, + {"rb", COMMAND::RB}, {"rr", COMMAND::RR}, {"rra", COMMAND::RRA}, + {"rrb", COMMAND::RRB}, {"rrr", COMMAND::RRR}, + } {} +Queues::~Queues() {} + +void Queues::step() { + if (this->commands.empty()) { + return; + } + this->_executeCommand(this->commands.front()); + this->executedCommands.push_front(this->commands.front()); + this->commands.pop_front(); +} + +void Queues::stepBack() { + if (this->executedCommands.empty()) { + return; + } + this->_executeReverseCommand(this->executedCommands.front()); + this->commands.push_front(this->executedCommands.front()); + this->executedCommands.pop_front(); +} + +std::list Queues::_normalize(const std::list &numbers) { + std::list normalized{numbers}; + std::vector ordered(numbers.size()); + std::copy(numbers.begin(), numbers.end(), ordered.begin()); + std::sort(ordered.begin(), ordered.end()); + for (int &number : normalized) { + const auto order = std::find(ordered.begin(), ordered.end(), number); + number = order - ordered.begin(); + } + return normalized; +} + +void Queues::start(const std::list &start) { + this->queueA = this->_normalize(start); + this->queueB.clear(); +} + +void Queues::_executeCommand(const std::string &cmd) { + COMMAND command{COMMAND::NONE}; + try { + command = this->commandMap[cmd]; + } catch (...) { + } + switch (command) { + case COMMAND::SA: + this->_sa(); + break; + case COMMAND::SB: + this->_sb(); + break; + case COMMAND::SS: + this->_ss(); + break; + case COMMAND::PA: + this->_pa(); + break; + case COMMAND::PB: + this->_pb(); + break; + case COMMAND::RA: + this->_ra(); + break; + case COMMAND::RB: + this->_rb(); + break; + case COMMAND::RR: + this->_rr(); + break; + case COMMAND::RRA: + this->_rra(); + break; + case COMMAND::RRB: + this->_rrb(); + break; + case COMMAND::RRR: + this->_rrr(); + break; + default: + break; + } +} + +void Queues::_executeReverseCommand(const std::string &cmd) { + COMMAND command{COMMAND::NONE}; + try { + command = this->commandMap[cmd]; + } catch (...) { + } + switch (command) { + case COMMAND::SA: + this->_sa(); + break; + case COMMAND::SB: + this->_sb(); + break; + case COMMAND::SS: + this->_ss(); + break; + case COMMAND::PA: + this->_pb(); + break; + case COMMAND::PB: + this->_pa(); + break; + case COMMAND::RA: + this->_rra(); + break; + case COMMAND::RB: + this->_rrb(); + break; + case COMMAND::RR: + this->_rrr(); + break; + case COMMAND::RRA: + this->_ra(); + break; + case COMMAND::RRB: + this->_rb(); + break; + case COMMAND::RRR: + this->_rr(); + break; + default: + break; + } +} + +void Queues::_sa() { + if (this->queueA.size() < 2) { + return; + } + std::swap(*this->queueA.begin(), *(++this->queueA.begin())); +} + +void Queues::_sb() { + if (this->queueB.size() < 2) { + return; + } + std::swap(*this->queueB.begin(), *(++this->queueB.begin())); +} + +void Queues::_ss() { + this->_sa(); + this->_sb(); +} + +void Queues::_pa() { + if (this->queueB.empty()) { + return; + } + int firstElement = this->queueB.front(); + this->queueB.pop_front(); + this->queueA.push_front(firstElement); +} + +void Queues::_pb() { + if (this->queueA.empty()) { + return; + } + int firstElement = this->queueA.front(); + this->queueA.pop_front(); + this->queueB.push_front(firstElement); +} + +void Queues::_ra() { + if (this->queueA.size() < 2) { + return; + } + int firstElement = this->queueA.front(); + this->queueA.pop_front(); + this->queueA.push_back(firstElement); +} + +void Queues::_rb() { + if (this->queueB.size() < 2) { + return; + } + int firstElement = this->queueB.front(); + this->queueB.pop_front(); + this->queueB.push_back(firstElement); +} + +void Queues::_rr() { + this->_ra(); + this->_rb(); +} + +void Queues::_rra() { + if (this->queueA.size() < 2) { + return; + } + int lastElement = this->queueA.back(); + this->queueA.pop_back(); + this->queueA.push_front(lastElement); +} + +void Queues::_rrb() { + if (this->queueB.size() < 2) { + return; + } + int lastElement = this->queueB.back(); + this->queueB.pop_back(); + this->queueB.push_front(lastElement); +} + +void Queues::_rrr() { + this->_rra(); + this->_rrb(); +} diff --git a/visualizer/src/utils.cpp b/visualizer/src/utils.cpp new file mode 100755 index 0000000..55160ed --- /dev/null +++ b/visualizer/src/utils.cpp @@ -0,0 +1,27 @@ +#include "utils.h" +#include +#include +#include + +namespace Utils { +std::list SplitStringToString(const std::string &s, + const char delimitor) { + std::stringstream ss(s); + std::string item; + std::list elems; + while (std::getline(ss, item, delimitor)) { + elems.push_back(item); + } + return elems; +} + +std::list SplitStringToInt(const std::string &s, const char delimitor) { + std::stringstream ss(s); + std::string item; + std::list elems; + while (std::getline(ss, item, delimitor)) { + elems.push_back(std::stoi(item)); + } + return elems; +} +} // namespace Utils diff --git a/visualizer/tests/CMakeLists.txt b/visualizer/tests/CMakeLists.txt new file mode 100755 index 0000000..3ea33d3 --- /dev/null +++ b/visualizer/tests/CMakeLists.txt @@ -0,0 +1,4 @@ + +add_executable(tests test_queues.cpp test_pushswap.cpp) +target_link_libraries(tests PRIVATE Catch2::Catch2 VisualizerLib) +target_include_directories(tests PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") diff --git a/visualizer/tests/test_pushswap.cpp b/visualizer/tests/test_pushswap.cpp new file mode 100755 index 0000000..45bf00f --- /dev/null +++ b/visualizer/tests/test_pushswap.cpp @@ -0,0 +1,10 @@ +#include +#include "pushswap.h" + +TEST_CASE("run", "[PushSwap]") { + PushSwap ps; + ps.path = "../push_swap"; + ps.run("2 1 3"); + REQUIRE_FALSE(ps.commands.empty()); + REQUIRE(ps.commands.front() == "sa"); +} diff --git a/visualizer/tests/test_queues.cpp b/visualizer/tests/test_queues.cpp new file mode 100755 index 0000000..c65ea06 --- /dev/null +++ b/visualizer/tests/test_queues.cpp @@ -0,0 +1,84 @@ +#define CATCH_CONFIG_MAIN +#include +#include "queues.h" + +TEST_CASE("normalize", "[Queues]") { + Queues queues; + queues.start({2, 10, 4}); + auto it = queues.queueA.begin(); + REQUIRE(*it == 0); + REQUIRE(*(++it) == 2); + REQUIRE(*(++it) == 1); +} + +TEST_CASE("sa", "[Queues]") { + Queues queues; + queues.queueA = {1, 2, 3, 4}; + queues.commands = {"sa"}; + queues.step(); + REQUIRE(queues.queueA.front() == 2); + REQUIRE(*(++queues.queueA.begin()) == 1); +} + +TEST_CASE("sb", "[Queues]") { + Queues queues; + queues.queueB = {1, 2, 3, 4}; + queues.commands = {"sb"}; + queues.step(); + REQUIRE(queues.queueB.front() == 2); + REQUIRE(*(++queues.queueB.begin()) == 1); +} + +TEST_CASE("pa", "[Queues]") { + Queues queues; + queues.queueB = {1, 2, 3, 4}; + queues.commands = {"pa"}; + queues.step(); + REQUIRE(queues.queueB.front() == 2); + REQUIRE(queues.queueA.front() == 1); +} + +TEST_CASE("pb", "[Queues]") { + Queues queues; + queues.queueA = {1, 2, 3, 4}; + queues.commands = {"pb"}; + queues.step(); + REQUIRE(queues.queueA.front() == 2); + REQUIRE(queues.queueB.front() == 1); +} + +TEST_CASE("ra", "[Queues]") { + Queues queues; + queues.queueA = {1, 2, 3, 4}; + queues.commands = {"ra"}; + queues.step(); + REQUIRE(queues.queueA.front() == 2); + REQUIRE(queues.queueA.back() == 1); +} + +TEST_CASE("rb", "[Queues]") { + Queues queues; + queues.queueB = {1, 2, 3, 4}; + queues.commands = {"rb"}; + queues.step(); + REQUIRE(queues.queueB.front() == 2); + REQUIRE(queues.queueB.back() == 1); +} + +TEST_CASE("rra", "[Queues]") { + Queues queues; + queues.queueA = {1, 2, 3, 4}; + queues.commands = {"rra"}; + queues.step(); + REQUIRE(queues.queueA.front() == 4); + REQUIRE(queues.queueA.back() == 3); +} + +TEST_CASE("rrb", "[Queues]") { + Queues queues; + queues.queueB = {1, 2, 3, 4}; + queues.commands = {"rrb"}; + queues.step(); + REQUIRE(queues.queueB.front() == 4); + REQUIRE(queues.queueB.back() == 3); +}