commit b42f2c6eafbfa3a3e253be5027733fa0d4dea9e7 Author: NaiJi Date: Sun Dec 13 21:45:52 2020 +0300 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a9d35c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.user diff --git a/application.cpp b/application.cpp new file mode 100644 index 0000000..f5fbbf1 --- /dev/null +++ b/application.cpp @@ -0,0 +1,87 @@ +#include +#include "application.h" + +constexpr int SCREEN_WIDTH = 1280; +constexpr int SCREEN_HEIGHT = 720; +const sf::Time TIME_PER_SECOND = sf::seconds(1.f / 60.f); + +Application::Application() : + render_window({SCREEN_WIDTH, SCREEN_HEIGHT}, "Sliding Puzzle") +{} + +void Application::run() +{ + render_window.setFramerateLimit(60); + render_window.display(); + + while (render_window.isOpen()) + { + processInput(); + draw(); + } +} + +void Application::draw() +{ + render_window.clear(); + board.draw(render_window); + render_window.setView(render_window.getDefaultView()); + render_window.display(); +} + +bool Application::processInput() +{ + sf::Event event; + while (render_window.pollEvent(event)) + { + switch (event.type) + { + case sf::Event::Closed: + render_window.close(); + break; + + case sf::Event::KeyPressed: + if (board.isWinCondition()) + return true; + if (event.key.code == sf::Keyboard::Z) + board.onSelectionMode(); + else + board.moveSelection(getDirection(event.key.code)); + break; + + default: + break; + } + } + + return true; +} + +DIRECTION Application::getDirection(sf::Keyboard::Key &key) const +{ + switch (key) + { + case sf::Keyboard::A: + case sf::Keyboard::Left: + case sf::Keyboard::Num4: + return DIRECTION::LEFT; + + case sf::Keyboard::W: + case sf::Keyboard::Up: + case sf::Keyboard::Num8: + return DIRECTION::UP; + + case sf::Keyboard::D: + case sf::Keyboard::Right: + case sf::Keyboard::Num6: + return DIRECTION::RIGHT; + + case sf::Keyboard::S: + case sf::Keyboard::Down: + case sf::Keyboard::Num2: + return DIRECTION::DOWN; + + default: + return DIRECTION::NONE; + } +} diff --git a/application.h b/application.h new file mode 100644 index 0000000..275c2c8 --- /dev/null +++ b/application.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "board.h" + +class Application +{ +public: + explicit Application(); + + void run(); + void draw(); + bool processInput(); + +private: + DIRECTION getDirection(sf::Keyboard::Key &key) const; + + Board board; + sf::RenderWindow render_window; +}; diff --git a/board.cpp b/board.cpp new file mode 100644 index 0000000..909fb32 --- /dev/null +++ b/board.cpp @@ -0,0 +1,216 @@ +#include "board.h" +#include +#include + +const std::string picture_dest = "art.png"; + +Board::Board(int splitting) : + selection_index(0), + on_selection(false) +{ + // PREPARING INITIAL BOARD STATE // + + if (!global_texture.loadFromFile(picture_dest) ) + return; + + const float width = global_texture.getSize().x; + const float 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); + vec_ref.reserve(vec_field.size()); + + /* 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) + { + if ((height - x) >= Cell::side_length) + { + for (int y = 0; y < width; y += Cell::side_length) + { + if ((width - y) >= Cell::side_length) + { + sf::Sprite* sp = new sf::Sprite(global_texture, sf::IntRect(y, x, Cell::side_length, Cell::side_length)); + sp->setPosition(y, x); + + vec_field.push_back({index, index, sp}); + vec_ref.push_back(index); + ++index; + } + } + } + } + + // SHUFFLING // + + srand(static_cast(time(nullptr))); + for (Cells::iterator curr_it = vec_field.begin(); curr_it != vec_field.end(); ++curr_it) + { + bool success = false; + while (!success) + { + Cells::iterator swap_it = cellByCurrentIndex(rand() & (vec_field.size() - 1)); + if (curr_it->current_index == swap_it->current_index) + continue; + + swapCells(curr_it, swap_it); + success = true; + } + } + + setSelectionVertex(selection_index); +} + +Board::~Board() +{ + for (Cell& cell : vec_field) + delete cell.sprite; +} + +void Board::draw(sf::RenderWindow& window) +{ + for (const Cell& cell : vec_field) + window.draw(*cell.sprite); + + window.draw(rect_selection); +} + +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: + return false; + break; + } + + return true; + } + + switch (direction) { + case DIRECTION::UP: + if (selection_index < cells_on_width) // if upper row + return false; + swapCells(cellByCurrentIndex(selection_index), cellByCurrentIndex(selection_index - cells_on_width)); + 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; + swapCells(cellByCurrentIndex(selection_index), cellByCurrentIndex(selection_index + cells_on_width)); + selection_index += cells_on_width; + setSelectionVertex(selection_index); + break; + + case DIRECTION::RIGHT: + if ((selection_index + 1) % cells_on_width == 0) + return false; + swapCells(cellByCurrentIndex(selection_index), cellByCurrentIndex(selection_index + 1)); + ++selection_index; + setSelectionVertex(selection_index); + break; + + case DIRECTION::LEFT: + if (((selection_index % cells_on_width == 0) && (selection_index > cells_on_width)) || selection_index == 0) + return false; + swapCells(cellByCurrentIndex(selection_index), cellByCurrentIndex(selection_index - 1)); + --selection_index; + setSelectionVertex(selection_index); + break; + + default: + return false; + break; + } + + on_selection = false; + return true; +} + +void Board::onSelectionMode() +{ + on_selection = !on_selection; +} + +void Board::setSelectionVertex(Cells::size_type index) +{ + const auto& cell = cellByCurrentIndex(index); + const auto& pos = cell->sprite->getPosition(); + const auto& x = pos.x; + const auto& y = pos.y; + const float length = static_cast(Cell::side_length); + + rect_selection = sf::VertexArray(sf::LinesStrip, 5); + rect_selection[0].position = pos; + rect_selection[0].color = sf::Color::Red; + rect_selection[1].position = sf::Vector2f(x + length, y); + rect_selection[1].color = sf::Color::Red; + rect_selection[2].position = sf::Vector2f(x + length, y + length); + rect_selection[2].color = sf::Color::Red; + rect_selection[3].position = sf::Vector2f(x, y + length); + rect_selection[3].color = sf::Color::Red; + rect_selection[4].position = pos; + rect_selection[4].color = sf::Color::Red; +} + +void Board::swapCells(Cells::size_type curr_cell_ind, Cells::size_type swap_cell_ind) +{ + auto& curr_cell = vec_field[curr_cell_ind]; + auto& swap_cell = vec_field[swap_cell_ind]; + const sf::Vector2f temp_pos = curr_cell.sprite->getPosition(); + const Cells::size_type temp_cell_index = curr_cell.current_index; + + curr_cell.sprite->setPosition(vec_field[swap_cell_ind].sprite->getPosition()); + curr_cell.current_index = vec_field[swap_cell_ind].current_index; + + swap_cell.sprite->setPosition(temp_pos); + swap_cell.current_index = temp_cell_index; +} + +Board::Cells::iterator Board::cellByCurrentIndex(Cells::size_type index) +{ + return std::find_if(vec_field.begin(), vec_field.end(), [&](const Cell& cell) { return cell.current_index == index; }); +} + +bool Board::isWinCondition() const +{ + return std::all_of(vec_field.begin(), vec_field.end(), [](const Cell& cell){ return cell.current_index == cell.inital_index; }); +} + +int Board::Cell::side_length = 0; diff --git a/board.h b/board.h new file mode 100644 index 0000000..c74dc32 --- /dev/null +++ b/board.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include +#include + +using pos = std::pair; + +enum class DIRECTION +{ + UP, + DOWN, + RIGHT, + LEFT, + + NONE +}; + +class Board +{ +public: + explicit Board(int splitting = 6); + ~Board(); + + void draw(sf::RenderWindow& window); + bool moveSelection(const DIRECTION& direction); + void onSelectionMode(); + bool isWinCondition() const; + +private: + + struct Cell + { + std::vector::size_type inital_index; + std::vector::size_type current_index; + sf::Sprite *sprite; + static float side_length; + }; + + using Cells = std::vector; + using RefCells = std::vector; + + sf::VertexArray rect_selection; + Cells::size_type selection_index; + + Cells::size_type cells_on_width; // amount of cells on horizontal side of board + Cells::size_type cells_on_height; // amount of cells on vertical side of board + Cells vec_field; + RefCells vec_ref; // map to actual index + sf::Texture global_texture; + bool on_selection; + + void setSelectionVertex(Cells::size_type index); + void swapCells(Cells::size_type curr_cell_ind, Cells::size_type swap_cell_ind); + Cells::iterator cellByCurrentIndex(Cells::size_type index); +}; diff --git a/filepath_util.h b/filepath_util.h new file mode 100644 index 0000000..91f3df6 --- /dev/null +++ b/filepath_util.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +static std::tuple file_info(const std::filesystem::directory_entry& entry) +{ + const auto fs (std::filesystem::status(entry)); + return {entry.path(), + fs, + std::filesystem::is_regular_file(fs) ? std::filesystem::file_size(entry.path()) : 0u}; +} diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0ca2175 --- /dev/null +++ b/main.cpp @@ -0,0 +1,7 @@ +#include "gamestate.h" + +int main() +{ + GameState game; + game.run(); +} diff --git a/sliding-puzzle.pro b/sliding-puzzle.pro new file mode 100644 index 0000000..e153abb --- /dev/null +++ b/sliding-puzzle.pro @@ -0,0 +1,17 @@ +TEMPLATE = app +QMAKE_CXXFLAGS = -Wall -Werror -Wextra -Wpedantic -Wconversion -std=c++17 -O2 -g +CONFIG += c++17 +CONFIG -= app_bundle +CONFIG -= qt + +SOURCES += \ + board.cpp \ + application.cpp \ + main.cpp + +HEADERS += \ + board.h \ + filepath_util.h \ + application.h + +unix:LIBS += -lsfml-graphics -lsfml-audio -lsfml-window -lsfml-system