Compare commits

...

10 Commits

Author SHA1 Message Date
NaiJi ✨ bb0c4d9fc4 Add 'LICENSE' 2022-03-30 14:35:42 +00:00
NaiJi ✨ 9c9fd95ea9 Add multifolder support 2021-04-24 23:46:31 +03:00
NaiJi ✨ 93449bd39a Set defaults for arguments 2021-03-30 20:32:25 +03:00
NaiJi ✨ 291c23124f Clean arguments parsing 2021-03-25 15:27:50 +03:00
NaiJi ✨ 5b8b0f8fb8 Clean code 2021-03-24 17:19:55 +03:00
NaiJi ✨ 3257e534c3 Fix README 2021-01-09 12:07:36 +03:00
NaiJi ✨ 0d2143925f Fix CMake 2021-01-09 11:50:26 +03:00
NaiJi ✨ a766708147 Fix runtime crash 2021-01-09 00:53:24 +03:00
NaiJi ✨ 81bf4c7e8a Update 'README.md' 2021-01-08 22:29:47 +01:00
NaiJi ✨ 90ed819f17 Update --help and add README 2021-01-09 00:27:07 +03:00
11 changed files with 451 additions and 253 deletions

View File

@ -4,31 +4,21 @@ project(sliding-puzzle LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(SOURCES application.cpp board.cpp main.cpp)
set(HEADER_FILES application.h board.h filepath_util.h output_util.h)
set(SFML_STATIC_LIBRARIES TRUE) #set to FALSE if you have sfml installed from package manager and you don't want to link it statically
set(SOURCES application.cpp board.cpp main.cpp argsprocessor.cpp)
set(HEADER_FILES application.h board.h filepath_util.h output_util.h argsprocessor.h)
# STATIC #
# You need to build SFML from sources with cmake
if (SFML_STATIC_LIBRARIES)
set(SFML_LIB_DIR
${CMAKE_SOURCE_DIR}/SFML-2.5.1/lib/libsfml-graphics.so.2.5
${CMAKE_SOURCE_DIR}/SFML-2.5.1/lib/libsfml-system.so.2.5
${CMAKE_SOURCE_DIR}/SFML-2.5.1/lib/libsfml-window.so.2.5)
set(SFML_INCL_DIR ${CMAKE_SOURCE_DIR}/SFML-2.5.1/include)
include_directories(${SFML_INCL_DIR})
add_executable(sliding-puzzle ${SOURCES} ${HEADER_FILES} )
target_link_libraries(sliding-puzzle ${SFML_LIB_DIR})
endif()
set(SFML_LIB_DIR
${CMAKE_SOURCE_DIR}/SFML-2.5.1/lib/libsfml-graphics.so.2.5
${CMAKE_SOURCE_DIR}/SFML-2.5.1/lib/libsfml-system.so.2.5
${CMAKE_SOURCE_DIR}/SFML-2.5.1/lib/libsfml-window.so.2.5)
#set(SFML_INCL_DIR ${CMAKE_SOURCE_DIR}/SFML-2.5.1/include)
#include_directories(${SFML_INCL_DIR})
#target_link_libraries(sliding-puzzle ${SFML_LIB_DIR})
# DYNAMIC #
# You only need to install SFML from your package manager
if (NOT SFML_STATIC_LIBRARIES)
find_package(SFML REQUIRED graphics window system)
include_directories(${SFML_INCLUDE_DIR})
add_executable(sliding-puzzle ${SOURCES} ${HEADER_FILES} )
target_link_libraries(sliding-puzzle ${SFML_LIBRARIES} ${SFML_DEPENDENCIES})
endif()
find_package(SFML REQUIRED graphics window system)
add_executable(sliding-puzzle ${SOURCES} ${HEADER_FILES} )
target_link_libraries(sliding-puzzle sfml-system sfml-graphics sfml-network)

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>

39
README.md Normal file
View File

@ -0,0 +1,39 @@
# sliding-puzzle
A simple game which takes an image, then slices it into tiles and shuffles. You need to restore the initial image by swapping neighbor tiles! It's a command line application and does support --help.
### How to build
You only need a compiler supporting c++17 and sfml 2.5.1 (or latter versions).
First of all, clone the project and go to the project folder
```
git clone https://dev.udongein.xyz/NaiJi/sliding-puzzle
cd sliding-puzzle/
```
Then simply build it with CMake
```
cmake CMakeLists.txt -B ../sliding-puzzle-build
cd ../sliding-puzzle-build
make
```
### Usage
Run it from terminal, providing optional flags and filepath. Move cursor by either WASD or arrows. Press Z to select a tile and then select a direction to swap the tile with a neighbor from that direction. Repeat until the inital image gets completely assembled!
```
usage: sliding-puzzle [-OPTIONS...] FILE-OR-DIRECTORY
Provide FILE-OR-DIRECTORY variable as a path to either a directory
which contains images (one will be picked randomly) or a specific
image file of .bmp, .jpg, of .png format.
If no path was provided, it will be assumed as '.'!
Options:
[-r NUMxNUM] Provide it if you want to explicitly define
window resolution.
[-s NUM] Provide it if you want to explicitly define
qualifier for image slicing, it's counted
by the smallest side of given source texture.
Hence, if your image is square, the amount of tiles
will be num * num.
```

View File

@ -61,14 +61,13 @@ void Application::onGameState(sf::Event& event)
break;
case sf::Event::KeyPressed:
// Go to selection mode
if (event.key.code == sf::Keyboard::Z)
board.onSelectionMode();
else // if it wasn't selection mode, then try to handle direction
board.moveSelection(getDirection(event.key.code));
else
board.tryProcessDirection(getDirection(event.key.code));
break;
default: // otherwise - do nothing
default:
break;
}

141
argsprocessor.cpp Normal file
View File

@ -0,0 +1,141 @@
#include "argsprocessor.h"
#include "output_util.h"
#include "filepath_util.h"
#include <cstring>
#include <cctype>
/////////////////////////////////////////////////////////////////////////
static constexpr int DEFAULT_SCREEN_WIDTH = 1280;
static constexpr int DEFAULT_SCREEN_HEIGHT = 720;
static constexpr int DEFAULT_SPLITTING = 4;
static const char * const DEFAULT_PATH = ".";
/////////////////////////////////////////////////////////////////////////
ArgsProcessor::ArgsProcessor(int argc, char **argv) :
image_splitting(DEFAULT_SPLITTING),
game_resolution({DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT}),
image_path(DEFAULT_PATH)
{
parse_result = tryConvertInput(argc, argv);
}
int ArgsProcessor::broken() const
{
return parse_result;
}
std::tuple<int, sf::Vector2i, std::string> ArgsProcessor::unpack() const
{
return {image_splitting, game_resolution, image_path};
}
int ArgsProcessor::tryConvertInput(int argc, char **argv)
{
int error = iterateArgc(argc, argv);
if (error)
return error;
if (image_path == DEFAULT_PATH && loaded_image_pathes.empty())
{
// no path was given, loading random image from '.'
const auto &[error, ret_path] = filepath::parsePath(image_path);
if (error)
return makeError(output::IMG_FAIL_MSG);
loaded_image_pathes.insert(loaded_image_pathes.end(), ret_path.begin(), ret_path.end());
}
image_path = filepath::randomChoice(loaded_image_pathes);
return EXIT_SUCCESS;
}
bool ArgsProcessor::isFlag(const char *arg, const char *flag) const
{
return (strcmp(arg, flag) == 0);
}
int ArgsProcessor::iterateArgc(int argc, char **argv)
{
for (int current_arg = 1; current_arg < argc; ++current_arg) // current_arg = 0 is executable name
{
if (isFlag(argv[current_arg], output::HELP_FLAG))
return makeError(output::HELP_MSG);
if (isFlag(argv[current_arg], output::SPLITTING_FLAG))
{
int error = parseSplitting(current_arg, argc, argv);
if (error)
return error;
++current_arg;
continue;
}
if (isFlag(argv[current_arg], output::RESOLUTION_FLAG))
{
int error = parseResolution(current_arg, argc, argv);
if (error)
return error;
++current_arg;
continue;
}
const auto &[error, ret_path] = filepath::parsePath(argv[current_arg]);
if (error)
continue;
loaded_image_pathes.insert(loaded_image_pathes.end(), ret_path.begin(), ret_path.end());
}
return EXIT_SUCCESS;
}
int ArgsProcessor::parseSplitting(int curr_arg, int argc, char **argv)
{
const int value_rg = curr_arg + 1;
if (value_rg == argc)
return makeError(output::SPLITTING_MSG);
image_splitting = -1;
if (std::isdigit(*argv[value_rg]))
image_splitting = std::stoi(argv[value_rg]);
if (image_splitting < 2)
return makeError(output::SPLITTING_MSG);
return EXIT_SUCCESS;
}
int ArgsProcessor::parseResolution(int curr_arg, int argc, char **argv)
{
const int value_rg = curr_arg + 1;
if (value_rg == argc)
return makeError(output::RESOLUTION_MSG);
std::vector<std::string> res = filepath::split(argv[value_rg], 'x'); // splitting argument by 'x' as in 600x900
if (res.size() < 2)
return makeError(output::RESOLUTION_MSG);
game_resolution = {-1, -1};
if (std::isdigit(*res[0].c_str()) && std::isdigit(*res[1].c_str()))
game_resolution = {std::stoi(res[0].c_str()), std::stoi(res[1].c_str())};
if (game_resolution.x < 1 || game_resolution.y < 1)
return makeError(output::RESOLUTION_MSG);
return EXIT_SUCCESS;
}
int ArgsProcessor::makeError(const char* msg) const
{
std::cout << msg;
return EXIT_FAILURE;
}

30
argsprocessor.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef ARGSPROCESSOR_H
#define ARGSPROCESSOR_H
#include <SFML/Graphics/VertexArray.hpp>
#include <string>
class ArgsProcessor
{
public:
ArgsProcessor(int argc, char **argv);
int broken() const;
std::tuple<int, sf::Vector2i, std::string> unpack() const;
private:
bool isFlag(const char* arg, const char* flag) const;
int tryConvertInput(int argc, char **argv);
int iterateArgc(int argc, char **argv);
int makeError(const char* msg) const;
int parseSplitting(int curr_arg, int argc, char **argv);
int parseResolution(int curr_arg, int argc, char **argv);
int parse_result;
int image_splitting;
sf::Vector2i game_resolution;
std::string image_path;
std::vector<std::string> loaded_image_pathes;
};
#endif // ARGSPROCESSOR_H

193
board.cpp
View File

@ -28,32 +28,44 @@ Board::~Board()
bool Board::init(const std::string& path, int splitting, const sf::RenderWindow &window)
{
// PREPARING INITIAL BOARD STATE //
std::cout << path << '\n';
if (!global_texture.loadFromFile(path) )
return false;
calculateBoardProperties(splitting);
splitImageIntoTiles(Cell::side_length);
scaleImageToWindow(window);
shuffleTiles();
setSelectionVertex(selection_index);
return true;
}
void Board::calculateBoardProperties(int splitting)
{
const int width = global_texture.getSize().x;
const int height = global_texture.getSize().y;
Cell::side_length = (width < height) ? width / splitting : height / splitting;
cells_on_height = height / Cell::side_length;
cells_on_width = width / Cell::side_length;
}
vec_field.reserve(cells_on_height * cells_on_width);
void Board::splitImageIntoTiles(int tile_length)
{
const int width = global_texture.getSize().x;
const int height = global_texture.getSize().y;
/* Iterating board cells' screen positions.
* The initial image after this would look exactly like the loaded picture, not shuffled yet. */
Cells::size_type index = 0;
for (int x = 0; x < height; x += Cell::side_length)
for (int x = 0; x < height; x += tile_length)
{
if ((height - x) >= Cell::side_length)
if ((height - x) >= tile_length)
{
for (int y = 0; y < width; y += Cell::side_length)
for (int y = 0; y < width; y += tile_length)
{
if ((width - y) >= Cell::side_length)
if ((width - y) >= tile_length)
{
sf::Sprite* sp = new sf::Sprite(global_texture, sf::IntRect(y, x, Cell::side_length, Cell::side_length));
sf::Sprite* sp = new sf::Sprite(global_texture, sf::IntRect(y, x, tile_length, tile_length));
sp->setPosition(static_cast<float>(y), static_cast<float>(x));
vec_field.push_back(new Cell({index, index, sp}));
@ -62,43 +74,63 @@ bool Board::init(const std::string& path, int splitting, const sf::RenderWindow
}
}
}
}
// SCALING //
void Board::scaleImageToWindow(const sf::RenderWindow &window)
{
float scaling = calculateScalingToWindow(window);
scaleTiles(scaling);
}
float Board::calculateScalingToWindow(const sf::RenderWindow &window) const
{
int texture_width = global_texture.getSize().x;
int texture_height = global_texture.getSize().y;
float scaling = 0.;
if (width >= height && width > static_cast<int>(window.getSize().x))
scaling = static_cast<float>(window.getSize().x) / static_cast<float>(width);
if (height >= width && height > static_cast<int>(window.getSize().y))
scaling = static_cast<float>(window.getSize().y) / static_cast<float>(height);
if (texture_width >= texture_height && texture_width > static_cast<int>(window.getSize().x))
scaling = static_cast<float>(window.getSize().x) / static_cast<float>(texture_width);
if (texture_height >= texture_width && texture_height > static_cast<int>(window.getSize().y))
scaling = static_cast<float>(window.getSize().y) / static_cast<float>(texture_height);
if (scaling != 0.)
return scaling;
}
void Board::scaleTiles(float scaling)
{
if (scaling == 0.)
return;
int old_side_length = Cell::side_length;
Cell::side_length = static_cast<int>(static_cast<float>(Cell::side_length) * scaling);
int shift = Cell::side_length - old_side_length;
for (Cells::size_type i = 0; i < vec_field.size(); ++i)
{
// Calculating new size of each tile
int old_side_length = Cell::side_length;
Cell::side_length = static_cast<int>(static_cast<float>(Cell::side_length) * scaling);
int shift = Cell::side_length - old_side_length;
float move_x, move_y;
// Moving all scaled tiles up and left, to remove spacing
for (Cells::size_type i = 0; i < vec_field.size(); ++i)
{
move_x = 0.f;
move_y = 0.f;
// The first column isn't allowed to move by x
if (!(((i % cells_on_width == 0) && (i >= cells_on_width))))
move_x = static_cast<float>(shift) * static_cast<float>((i < cells_on_width) ? i : i % cells_on_width);
// The first row isn't allowed to move by y
if (i >= cells_on_width)
move_y = static_cast<float>(shift) * static_cast<float>(i / cells_on_width);
vec_field[i]->sprite->scale(scaling, scaling);
vec_field[i]->sprite->move(move_x, move_y);
}
vec_field[i]->sprite->scale(scaling, scaling);
const auto shift_vector = calculateTileShiftVector(i, shift);
vec_field[i]->sprite->move(shift_vector.first, shift_vector.second);
}
}
std::pair<float, float> Board::calculateTileShiftVector(Cells::size_type tile_index, int shift) const
{
float move_x = 0.f, move_y = 0.f;
// SHUFFLING //
// The first column isn't allowed to move by x
if (!(((tile_index % cells_on_width == 0) && (tile_index >= cells_on_width))))
move_x = static_cast<float>(shift) * static_cast<float>((tile_index < cells_on_width) ? tile_index : tile_index % cells_on_width);
// The first row isn't allowed to move by y
if (tile_index >= cells_on_width)
move_y = static_cast<float>(shift) * static_cast<float>(tile_index / cells_on_width);
return {move_x, move_y};
}
void Board::shuffleTiles()
{
solved_tiles = vec_field.size(); // all tiles are solved for now
srand(static_cast<unsigned int>(time(nullptr)));
@ -112,11 +144,6 @@ bool Board::init(const std::string& path, int splitting, const sf::RenderWindow
swapCells(curr_i, swap_i);
}
// Set initial position of cursor
setSelectionVertex(selection_index);
return true;
}
void Board::draw(sf::RenderWindow& window)
@ -131,48 +158,56 @@ void Board::draw(sf::RenderWindow& window)
window.draw(rect_selection);
}
bool Board::tryProcessDirection(const DIRECTION &direction)
{
if (on_selection)
return swapOnSelection(direction);
return moveSelection(direction);
}
bool Board::moveSelection(const DIRECTION &direction)
{
if (!on_selection)
{
switch (direction) {
case DIRECTION::UP:
if (selection_index < cells_on_width) // if upper row
return false;
selection_index -= cells_on_width;
setSelectionVertex(selection_index);
break;
case DIRECTION::DOWN:
if (selection_index > (cells_on_width * (cells_on_height - 1))) // if bottom row
return false;
selection_index += cells_on_width;
setSelectionVertex(selection_index);
break;
case DIRECTION::RIGHT:
++selection_index;
if (selection_index == vec_field.size()) // if the last cell of right bottom corner
selection_index = 0; // move to the first cell of upper left corner
setSelectionVertex(selection_index);
break;
case DIRECTION::LEFT:
if (selection_index == 0) // if the first cell of of upper left corner
selection_index = vec_field.size() - 1; // move to the last cell of right bottom corner
else
--selection_index;
setSelectionVertex(selection_index);
break;
default:
switch (direction) {
case DIRECTION::UP:
if (selection_index < cells_on_width) // if upper row
return false;
break;
}
selection_index -= cells_on_width;
setSelectionVertex(selection_index);
break;
return true;
case DIRECTION::DOWN:
if (selection_index >= (cells_on_width * (cells_on_height - 1))) // if bottom row
return false;
selection_index += cells_on_width;
setSelectionVertex(selection_index);
break;
case DIRECTION::RIGHT:
++selection_index;
if (selection_index == vec_field.size()) // if the last cell of right bottom corner
selection_index = 0; // move to the first cell of upper left corner
setSelectionVertex(selection_index);
break;
case DIRECTION::LEFT:
if (selection_index == 0) // if the first cell of of upper left corner
selection_index = vec_field.size() - 1; // move to the last cell of right bottom corner
else
--selection_index;
setSelectionVertex(selection_index);
break;
default:
return false;
break;
}
return true;
}
bool Board::swapOnSelection(const DIRECTION &direction)
{
switch (direction) {
case DIRECTION::UP:
if (selection_index < cells_on_width) // if upper row
@ -291,4 +326,4 @@ void Board::setCursorVisibility(bool visible)
is_cursor_visible = visible;
}
int Board::Cell::side_length = 0;
int Board::Cell::side_length = 1;

26
board.h
View File

@ -27,27 +27,20 @@ public:
explicit Board();
~Board();
// Set play image
bool init(const std::string& path, int splitting, const sf::RenderWindow& window);
// Output current graphical state on application window
void draw(sf::RenderWindow& window);
bool isWinCondition() const;
// Move cursor to next tile by given direction
void setCursorVisibility(bool visible);
bool tryProcessDirection(const DIRECTION& direction);
bool moveSelection(const DIRECTION& direction);
bool swapOnSelection(const DIRECTION& direction);
// Go to or leave from selection mode
void onSelectionMode();
// Did player win the game
bool isWinCondition() const;
// Show or hide selection cursos
void setCursorVisibility(bool visible = false);
private:
// Game tile
struct Cell
{
std::vector<Cell*>::size_type inital_index;
@ -76,4 +69,15 @@ private:
// Draw selection cursor on given tile
void setSelectionVertex(Cells::size_type index);
void calculateBoardProperties(int splitting);
void splitImageIntoTiles(int tile_length);
void scaleImageToWindow(const sf::RenderWindow &window);
float calculateScalingToWindow(const sf::RenderWindow &window) const;
void scaleTiles(float scaling);
std::pair<float, float> calculateTileShiftVector(Cells::size_type tile_index, int shift) const;
void shuffleTiles();
};

View File

@ -25,7 +25,56 @@ namespace filepath
return std::equal(ending.rbegin(), ending.rend(), string.rbegin());
}
std::tuple<int, std::string> parsePath(const std::string &argv)
std::string randomChoice(const std::vector<std::string>& poll)
{
std::cout << "Loading random image!\n";
const auto range = poll.size() - 1;
return poll.at(rand() & range);
}
std::vector<std::tuple<std::filesystem::path, std::filesystem::file_status>> extractFilesFrom(std::filesystem::path&& path)
{
std::vector<std::tuple<std::filesystem::path, std::filesystem::file_status>> dir_items;
std::transform(std::filesystem::directory_iterator(path), {}, std::back_inserter(dir_items), filepath::getFileInfo);
return dir_items;
}
bool isImage(const std::filesystem::path& filepath)
{
static std::set<std::string> allowed_ext = {".bmp", ".jpg", "*.jpeg", ".png"};
const std::string path_string = filepath.string();
return std::filesystem::is_regular_file(filepath) &&
std::any_of(allowed_ext.begin(), allowed_ext.end(),
[&](const std::string& e)
{
return filepath::endsWith(path_string, e);
});
}
std::tuple<int, std::vector<std::string>> parseFolder(std::filesystem::path&& path)
{
auto directory_items = extractFilesFrom(std::move(path));
std::vector<std::string> image_pathes;
for (const auto &[local_path, status] : directory_items)
{
if (isImage(local_path))
image_pathes.emplace_back(local_path.string());
}
if (image_pathes.empty())
return {EXIT_FAILURE, {}};
const std::tuple<int, std::vector<std::string>> empty_folder = {EXIT_FAILURE, {}};
const std::tuple<int, std::vector<std::string>> found_images = {EXIT_SUCCESS, image_pathes};
return image_pathes.empty()
? empty_folder
: found_images;
}
std::tuple<int, std::vector<std::string>> parsePath(const std::string &argv)
{
std::filesystem::path path(argv);
if (!std::filesystem::exists(path))
@ -34,38 +83,12 @@ namespace filepath
return {EXIT_FAILURE, {}};
}
// Maybe user chose a specific image, not a folder
if (std::filesystem::is_regular_file(path))
return {EXIT_SUCCESS, path.string()};
// TO DO : I KNOW THIS PART IS BAD! I have never worked with ::filesystem before,
// So... it is a folder i will rewrite it when the prject works and is done
// Creating a vector of everything in the given directory
std::vector<std::tuple<std::filesystem::path, std::filesystem::file_status>> dir_items;
std::transform(std::filesystem::directory_iterator(path), {}, std::back_inserter(dir_items), filepath::getFileInfo);
std::set<std::string> allowed_ext = {".bmp", ".dds", ".jpg", ".png", ".tga", ".psd"};
// Now getting images
std::vector<std::string> dir_image_items;
for (const auto &[local_path, status] : dir_items)
{
const std::string str_path = local_path.string();
if (std::filesystem::is_regular_file(local_path) &&
std::any_of(allowed_ext.begin(), allowed_ext.end(), [&](const std::string& e) { return filepath::endsWith(str_path, e); }))
{
dir_image_items.emplace_back(str_path);
}
return {EXIT_SUCCESS, {path.string()}};
}
if (dir_image_items.empty())
{
std::cout << "No images found at " << path << "\n--help for more information.\n";
return {EXIT_FAILURE, {}};
}
std::cout << "Loading random image file from " << path << "\n";
srand(static_cast<unsigned int>(time(nullptr)));
return {EXIT_SUCCESS, dir_image_items[rand() & (dir_image_items.size() - 1)]};
return parseFolder(std::move(path));
}
std::vector<std::string> split(const std::string &s, char delim)

110
main.cpp
View File

@ -1,106 +1,16 @@
#include "application.h"
#include "output_util.h"
#include "filepath_util.h"
#include <cstring>
#include <cctype>
/////////////////////////////////////////////////////////////////////////
static constexpr int DEFAULT_SCREEN_WIDTH = 1280;
static constexpr int DEFAULT_SCREEN_HEIGHT = 720;
static constexpr int DEFAULT_SPLITTING = 4;
static const std::string DEFAULT_PATH = "."; // current folder, I guess
/////////////////////////////////////////////////////////////////////////
std::tuple<int, int, sf::Vector2i, std::string> error(const char* msg)
{
std::cout << msg;
return {EXIT_FAILURE, -1, {}, {}};
}
std::tuple<int, int, sf::Vector2i, std::string> parseInput(int argc, char **argv)
{
int splitting = DEFAULT_SPLITTING;
sf::Vector2i resolution(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT);
std::string path = DEFAULT_PATH;
for (int current_arg = 1; current_arg < argc; ++current_arg) // current_arg = 0 is executable name
{
if (strcmp(argv[current_arg], output::HELP_FLAG) == 0) // --help
return error(output::HELP_MSG);
if (strcmp(argv[current_arg], output::SPLITTING_FLAG) == 0) // -s num
{
const int value_rg = current_arg + 1;
if (value_rg == argc) // is '-s' is the last argument
return error(output::SPLITTING_MSG);
splitting = -1; // here assuming user is providing it on their own
if (std::isdigit(*argv[value_rg]))
splitting = std::stoi(argv[value_rg]);
if (splitting < 2)
return error(output::SPLITTING_MSG);
++current_arg; // skipping value after flag to not check it once again
continue;
}
if (strcmp(argv[current_arg], output::RESOLUTION_FLAG) == 0) // -r numxnum
{
const int value_rg = current_arg + 1;
if (value_rg == argc) // is '-s' is the last argument
return error(output::RESOLUTION_MSG);
std::vector<std::string> res = filepath::split(argv[value_rg], 'x'); // splitting argument by 'x' as in 600x900
if (res.size() < 2)
return error(output::RESOLUTION_MSG);
resolution = {-1, -1};
if (std::isdigit(*res[0].c_str()) && std::isdigit(*res[1].c_str()))
resolution = {std::stoi(res[0].c_str()), std::stoi(res[1].c_str())};
if (resolution.x < 1 || resolution.y < 1)
return error(output::RESOLUTION_MSG);
++current_arg; // skipping value after flag to not check it once again
continue;
}
// nothing else, then assuming it's filepath or folderpath
const auto &[ret_code, ret_path] = filepath::parsePath(argv[current_arg]);
if (ret_code)
return error(output::IMG_FAIL_MSG);
path = ret_path;
}
if (path == DEFAULT_PATH)
{
// no path was give, loading random image from '.'
const auto &[ret_code, ret_path] = filepath::parsePath(path);
if (ret_code)
return error(output::IMG_FAIL_MSG);
path = ret_path;
}
return {EXIT_SUCCESS, splitting, resolution, path};
}
/////////////////////////////////////////////////////////////////////////
#include "argsprocessor.h"
int main(int argc, char **argv)
{
const auto&[ret_code, splitting, resolution, path] = parseInput(argc, argv);
srand(static_cast<unsigned int>(time(nullptr)));
if (ret_code) // Error code is EXIT_FAILURE
return ret_code;
ArgsProcessor args(argc, argv);
if (args.broken())
return EXIT_FAILURE;
const auto&[splitting, resolution, path] = args.unpack();
Application app(resolution.x, resolution.y);
if (app.init(path, splitting))
@ -108,6 +18,6 @@ int main(int argc, char **argv)
app.run();
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
else
return EXIT_FAILURE;
}

View File

@ -8,15 +8,18 @@ namespace output
const char* NO_ARG_MSG = "Please at least provide a path to a target image. --help for more information.\n";
const char* HELP_MSG = "usage: sliding-puzzle [-OPTIONS...] FILE-OR-DIRECTORY\n\n"
" Necessarily provide the latter variable as path to either a directory\n"
" which contains arts (one will be picked randomly) or a specific\n"
" image file of .bmp, .dds, .jpg, .png, .tga, or .psd format.\n\n"
" Provide FILE-OR-DIRECTORY variable as a path to either a directory\n"
" which contains images (one will be picked randomly) or a specific\n"
" image file of .bmp, .jpg, of .png format.\n\n"
" If no path was provided, it will be assumed as \'.\'!\n\n"
" Options:\n"
" [-s num] Provide it if you want to explicitly define\n"
" qualifier for image slicing, it's counted\n"
" by the smallest side of given source texture.\n"
" Hence, if your image is square, the amount of tiles\n"
" will be num * num.\n";
" [-r NUMxNUM] Provide it if you want to explicitly define\n"
" window resolution.\n"
" [-s NUM] Provide it if you want to explicitly define\n"
" qualifier for image slicing, it's counted\n"
" by the smallest side of given source texture.\n"
" Hence, if your image is square, the amount of tiles\n"
" will be num * num.\n";
const char* SPLITTING_MSG = "-s should be given with a positive num >= 2 as in [-s num].\n";
const char* IMG_FAIL_MSG = "Couldn't load image from given path.\n";
const char* RESOLUTION_MSG = "Couldn't recognize given screen resolution.\n"