bla
This commit is contained in:
parent
898bcaf7b4
commit
d0783882b6
BIN
visualizer/.DS_Store
vendored
Normal file
BIN
visualizer/.DS_Store
vendored
Normal file
Binary file not shown.
137
visualizer/.clang-format
Executable file
137
visualizer/.clang-format
Executable file
@ -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
|
||||||
|
...
|
||||||
|
|
3
visualizer/.gitignore
vendored
Executable file
3
visualizer/.gitignore
vendored
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
__pycache__
|
||||||
|
build/
|
||||||
|
.vscode/
|
12
visualizer/CMakeLists.txt
Executable file
12
visualizer/CMakeLists.txt
Executable file
@ -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)
|
34
visualizer/README.md
Executable file
34
visualizer/README.md
Executable file
@ -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)
|
31
visualizer/dependencies/CMakeLists.txt
Executable file
31
visualizer/dependencies/CMakeLists.txt
Executable file
@ -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)
|
4
visualizer/dependencies/catch2/CMakeLists.txt
Executable file
4
visualizer/dependencies/catch2/CMakeLists.txt
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
message(STATUS "Fetching Catch2...")
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(catch2)
|
||||||
|
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/contrib)
|
7
visualizer/dependencies/imgui-sfml/CMakeLists.txt
Executable file
7
visualizer/dependencies/imgui-sfml/CMakeLists.txt
Executable file
@ -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)
|
7
visualizer/dependencies/sfml/CMakeLists.txt
Executable file
7
visualizer/dependencies/sfml/CMakeLists.txt
Executable file
@ -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)
|
19
visualizer/imgui.ini
Executable file
19
visualizer/imgui.ini
Executable file
@ -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
|
39
visualizer/include/gui.h
Executable file
39
visualizer/include/gui.h
Executable file
@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pushswap.h"
|
||||||
|
#include "queues.h"
|
||||||
|
#include <SFML/Graphics/RectangleShape.hpp>
|
||||||
|
#include <SFML/Graphics/RenderWindow.hpp>
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Gui {
|
||||||
|
private:
|
||||||
|
enum class STATE {Running, Reverse, Stopped};
|
||||||
|
Queues queues;
|
||||||
|
PushSwap pushswap;
|
||||||
|
|
||||||
|
std::vector<sf::RectangleShape> barsA;
|
||||||
|
std::vector<sf::RectangleShape> barsB;
|
||||||
|
int generateNumberSize;
|
||||||
|
std::string numbers;
|
||||||
|
int speed;
|
||||||
|
STATE state;
|
||||||
|
float scale;
|
||||||
|
|
||||||
|
void _updateControls();
|
||||||
|
void _drawBars();
|
||||||
|
void _animateQueue(sf::Clock &clock);
|
||||||
|
std::list<int> _generateValues(const unsigned int size);
|
||||||
|
void _updateBars();
|
||||||
|
sf::Color _rgb(const double ratio);
|
||||||
|
|
||||||
|
public:
|
||||||
|
sf::RenderWindow _window;
|
||||||
|
|
||||||
|
Gui();
|
||||||
|
~Gui();
|
||||||
|
|
||||||
|
void loop();
|
||||||
|
};
|
17
visualizer/include/pushswap.h
Executable file
17
visualizer/include/pushswap.h
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
class PushSwap {
|
||||||
|
private:
|
||||||
|
std::list<std::string> _split(const std::string &input, const char delimitor);
|
||||||
|
|
||||||
|
public:
|
||||||
|
PushSwap();
|
||||||
|
~PushSwap();
|
||||||
|
void run(const std::string &numbers);
|
||||||
|
|
||||||
|
std::string path;
|
||||||
|
std::list<std::string> commands;
|
||||||
|
};
|
39
visualizer/include/queues.h
Executable file
39
visualizer/include/queues.h
Executable file
@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Queues {
|
||||||
|
enum COMMAND { NONE, SA, SB, SS, PA, PB, RA, RB, RR, RRA, RRB, RRR };
|
||||||
|
std::map<const std::string, COMMAND> commandMap;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Queues();
|
||||||
|
~Queues();
|
||||||
|
|
||||||
|
void step();
|
||||||
|
void stepBack();
|
||||||
|
void start(const std::list<int> &start);
|
||||||
|
|
||||||
|
std::list<std::string> commands;
|
||||||
|
std::list<std::string> executedCommands;
|
||||||
|
std::list<int> queueA;
|
||||||
|
std::list<int> queueB;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _executeCommand(const std::string &cmd);
|
||||||
|
void _executeReverseCommand(const std::string &cmd);
|
||||||
|
std::list<int> _normalize(const std::list<int> &numbers);
|
||||||
|
void _sa();
|
||||||
|
void _sb();
|
||||||
|
void _ss();
|
||||||
|
void _pa();
|
||||||
|
void _pb();
|
||||||
|
void _ra();
|
||||||
|
void _rb();
|
||||||
|
void _rr();
|
||||||
|
void _rra();
|
||||||
|
void _rrb();
|
||||||
|
void _rrr();
|
||||||
|
};
|
12
visualizer/include/utils.h
Executable file
12
visualizer/include/utils.h
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Utils {
|
||||||
|
std::list<std::string> SplitStringToString(const std::string &s,
|
||||||
|
const char delimitor);
|
||||||
|
std::list<int> SplitStringToInt(const std::string &s,
|
||||||
|
const char delimitor);
|
||||||
|
|
||||||
|
} // namespace Utils
|
13
visualizer/src/CMakeLists.txt
Executable file
13
visualizer/src/CMakeLists.txt
Executable file
@ -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
|
||||||
|
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
|
||||||
|
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic -Werror>
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(visualizer main.cpp)
|
||||||
|
target_compile_features(visualizer PRIVATE cxx_std_17)
|
||||||
|
target_link_libraries(visualizer PRIVATE VisualizerLib)
|
236
visualizer/src/gui.cpp
Executable file
236
visualizer/src/gui.cpp
Executable file
@ -0,0 +1,236 @@
|
|||||||
|
#include "gui.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include <SFML/System/Clock.hpp>
|
||||||
|
#include <SFML/Window/Event.hpp>
|
||||||
|
#include <imgui-SFML.h>
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <misc/cpp/imgui_stdlib.h>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
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<int> Gui::_generateValues(const unsigned int size) {
|
||||||
|
std::vector<int> 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<int> 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<float>(windowSize.y) / queuesSize;
|
||||||
|
const float barUnit = static_cast<float>(windowSize.x) / (2 * queuesSize + 2);
|
||||||
|
|
||||||
|
const auto updateBar = [=](std::list<int> queue,
|
||||||
|
std::vector<sf::RectangleShape> &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<float>(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<unsigned int>(this->generateNumberSize);
|
||||||
|
std::list<int> 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<int>(delta * this->speed);
|
||||||
|
for (int i = 0; i < steps; ++i) {
|
||||||
|
this->queues.step();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case STATE::Reverse: {
|
||||||
|
int steps = static_cast<int>(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<int>(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;
|
||||||
|
}
|
7
visualizer/src/main.cpp
Executable file
7
visualizer/src/main.cpp
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#include "gui.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Gui gui;
|
||||||
|
gui.loop();
|
||||||
|
return 0;
|
||||||
|
}
|
27
visualizer/src/pushswap.cpp
Executable file
27
visualizer/src/pushswap.cpp
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#include "pushswap.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include <array>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
PushSwap::PushSwap() : path{"./push_swap"} {}
|
||||||
|
PushSwap::~PushSwap() {}
|
||||||
|
|
||||||
|
void PushSwap::run(const std::string &numbers) {
|
||||||
|
this->commands.clear();
|
||||||
|
std::array<char, 128> buffer;
|
||||||
|
std::string result;
|
||||||
|
std::string command = this->path + " " + numbers;
|
||||||
|
std::unique_ptr<FILE, decltype(&pclose)> 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');
|
||||||
|
}
|
221
visualizer/src/queues.cpp
Executable file
221
visualizer/src/queues.cpp
Executable file
@ -0,0 +1,221 @@
|
|||||||
|
#include "queues.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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<int> Queues::_normalize(const std::list<int> &numbers) {
|
||||||
|
std::list<int> normalized{numbers};
|
||||||
|
std::vector<int> 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<int> &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();
|
||||||
|
}
|
27
visualizer/src/utils.cpp
Executable file
27
visualizer/src/utils.cpp
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#include "utils.h"
|
||||||
|
#include <list>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Utils {
|
||||||
|
std::list<std::string> SplitStringToString(const std::string &s,
|
||||||
|
const char delimitor) {
|
||||||
|
std::stringstream ss(s);
|
||||||
|
std::string item;
|
||||||
|
std::list<std::string> elems;
|
||||||
|
while (std::getline(ss, item, delimitor)) {
|
||||||
|
elems.push_back(item);
|
||||||
|
}
|
||||||
|
return elems;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<int> SplitStringToInt(const std::string &s, const char delimitor) {
|
||||||
|
std::stringstream ss(s);
|
||||||
|
std::string item;
|
||||||
|
std::list<int> elems;
|
||||||
|
while (std::getline(ss, item, delimitor)) {
|
||||||
|
elems.push_back(std::stoi(item));
|
||||||
|
}
|
||||||
|
return elems;
|
||||||
|
}
|
||||||
|
} // namespace Utils
|
4
visualizer/tests/CMakeLists.txt
Executable file
4
visualizer/tests/CMakeLists.txt
Executable file
@ -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")
|
10
visualizer/tests/test_pushswap.cpp
Executable file
10
visualizer/tests/test_pushswap.cpp
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#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");
|
||||||
|
}
|
84
visualizer/tests/test_queues.cpp
Executable file
84
visualizer/tests/test_queues.cpp
Executable file
@ -0,0 +1,84 @@
|
|||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#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);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user