From 96eefbe181dc3afdb616e4d9302eb3ec1a82db72 Mon Sep 17 00:00:00 2001 From: German Mikheev Date: Sat, 23 May 2026 11:50:32 +0300 Subject: [PATCH] Initial commit, some logic added --- .gitignore | 1 + CMakeLists.txt | 32 ++++++++ include/field.h | 45 +++++++++++ include/line.h | 13 +++ include/tilePosition.h | 9 +++ include/types.h | 4 + main.cpp | 39 +++++++++ source/field.cpp | 180 +++++++++++++++++++++++++++++++++++++++++ source/line.cpp | 47 +++++++++++ 9 files changed, 370 insertions(+) create mode 100755 .gitignore create mode 100755 CMakeLists.txt create mode 100755 include/field.h create mode 100644 include/line.h create mode 100755 include/tilePosition.h create mode 100644 include/types.h create mode 100755 main.cpp create mode 100755 source/field.cpp create mode 100644 source/line.cpp diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..d163863 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..3a57d19 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.25.1) +project(game) + +include(FetchContent) + +FetchContent_Declare(SFML + GIT_REPOSITORY https://github.com/SFML/SFML.git + GIT_TAG 3.0.x) +FetchContent_MakeAvailable(SFML) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_FLAGS "-g -Wall -Wextra -Werror -pedantic -fsanitize=address") +# set(CMAKE_CXX_FLAGS "-g -Wall -pedantic") + +file(GLOB SRC source/*.cpp) +# file(GLOB THIRD thirdpart/*.hpp) + +add_executable(game + ${SRC} +# ${THIRD} + main.cpp +) + +include_directories( + ./include/ +# ./thirdpart/ +) + +target_link_libraries(game PRIVATE + sfml-graphics +) \ No newline at end of file diff --git a/include/field.h b/include/field.h new file mode 100755 index 0000000..a109b14 --- /dev/null +++ b/include/field.h @@ -0,0 +1,45 @@ +#pragma once + +#include "tilePosition.h" +#include "line.h" +#include "types.h" + +#include +#include +#include + +#include + +class Field { +private: + std::mt19937 mt; + + Score score = 0; + + size_t FieldSize = 4; + std::vector field; + + std::vector processLine(const std::vector &sourceLine, Score &score); + + Line getRow(size_t rowIndex) const; + std::vector getColumn(size_t columnIndex) const; + void setRow(size_t rowIndex, const std::vector &line); + void setColumn(size_t columnIndex, const std::vector &line); + + void reset(); + void setSize(size_t size); + +public: + Field(size_t fieldSize); + + bool canMove() const; + bool moveUp(); + bool moveDown(); + bool moveLeft(); + bool moveRight(); + + std::vector getEmptyTiles() const; + bool spawnTile(const std::vector &empty); + + void debug() const; +}; \ No newline at end of file diff --git a/include/line.h b/include/line.h new file mode 100644 index 0000000..98f18c8 --- /dev/null +++ b/include/line.h @@ -0,0 +1,13 @@ +#include "types.h" + +#include + +struct Line { + std::vector line; + Score score; + + explicit Line(); + Line(size_t length); + + void process(); +}; \ No newline at end of file diff --git a/include/tilePosition.h b/include/tilePosition.h new file mode 100755 index 0000000..991d03f --- /dev/null +++ b/include/tilePosition.h @@ -0,0 +1,9 @@ +#pragma once + +struct Position { + int x; + int y; + + Position() : x(0), y(0) {} + Position(int x, int y) : x(x), y(y) {} +}; \ No newline at end of file diff --git a/include/types.h b/include/types.h new file mode 100644 index 0000000..0362b9e --- /dev/null +++ b/include/types.h @@ -0,0 +1,4 @@ +#pragma once + +using Tile = unsigned long long int; +using Score = unsigned long long int; \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100755 index 0000000..e378372 --- /dev/null +++ b/main.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include "field.h" + +using Tile = unsigned long long int; + +int main() { + Field field(4); + char action; + + while (true) { + field.debug(); + std::cin >> action; + switch (action) + { + case 'w': + field.moveUp(); + field.spawnTile(); + break; + case 's': + field.moveDown(); + field.spawnTile(); + break; + case 'a': + field.moveLeft(); + field.spawnTile(); + break; + case 'd': + field.moveRight(); + field.spawnTile(); + break; + default: + return 0; + } + } + + return 0; +} \ No newline at end of file diff --git a/source/field.cpp b/source/field.cpp new file mode 100755 index 0000000..ca84ef1 --- /dev/null +++ b/source/field.cpp @@ -0,0 +1,180 @@ +#include "field.h" + +/* +Fields: + std::vector> field; + +Private methods: + std::vector processLine(const std::vector &sourceLine); + + std::vector getRow(size_t idx); + std::vector getColumn(size_t idx); + void setRow(size_t idx); + void setColumn(size_t idx); + +Public methods: + void moveUp(); + void moveDown(); + void moveLeft(); + void moveRight(); +*/ + +// PRIVATE +std::vector Field::processLine(const std::vector& sourceLine, Score &score) { + std::vector result; + Tile pending = 0; + bool hasPending = false; + + for (Tile element : sourceLine) { + if (element == 0) continue; + + if (!hasPending) { + pending = element; + hasPending = true; + continue; + } + + if (element == pending) { + result.push_back(pending * 2); + score += pending * 2; + hasPending = false; + pending = 0; + } else { + result.push_back(pending); + pending = element; + } + } + + if (hasPending) { + result.push_back(pending); + } + + while (result.size() < sourceLine.size()) { + result.push_back(0); + } + + return result; +} + +Line Field::getRow(size_t rowIndex) const { + return this->field[rowIndex]; +} + +std::vector Field::getColumn(size_t columnIndex) const { + // from top to bottom + std::vector column; + for (size_t i = 0; i < this->FieldSize; ++i) { + column.push_back(this->field[i][columnIndex]); + } + return column; +} + +void Field::setRow(size_t rowIndex, const std::vector &line) { + this->field[rowIndex] = line; +} + +void Field::setColumn(size_t columnIndex, const std::vector &line) { + // from top to bottom (same) + for (size_t i = 0; i < this->FieldSize; ++i) { + this->field[i][columnIndex] = line[i]; + } +} + +void Field::reset() { + this->field.resize(this->FieldSize, std::vector(this->FieldSize, 0)); +} + +// PUBLIC +Field::Field(size_t fieldSize) : mt(std::random_device{}()), FieldSize(fieldSize) { + this->reset(); +} + +void printVector(const std::vector &arr) { + for (size_t i = 0; i < arr.size(); ++i) { + printf("%llu ", arr[i]); + } + printf("\n"); +} + +bool Field::moveUp() { + auto oldField = this->field; + for (size_t i = 0; i < this->FieldSize; ++i) { + std::vector column; + column = getColumn(i); + column = processLine(column, this->score); + setColumn(i, column); + } + return this->field != oldField; +} + +bool Field::moveDown() { + auto oldField = this->field; + for (size_t i = 0; i < this->FieldSize; ++i) { + std::vector column; + column = getColumn(i); + std::reverse(column.begin(), column.end()); + column = processLine(column, this->score); + std::reverse(column.begin(), column.end()); + setColumn(i, column); + } + return this->field != oldField; +} + +bool Field::moveLeft() { + auto oldField = this->field; + std::vector row; + for (size_t i = 0; i < this->FieldSize; ++i) { + row = getRow(i); + row = processLine(row, this->score); + setRow(i, row); + } + return this->field != oldField; +} + +bool Field::moveRight() { + auto oldField = this->field; + std::vector row; + for (size_t i = 0; i < this->FieldSize; ++i) { + row = getRow(i); + std::reverse(row.begin(), row.end()); + row = processLine(row, this->score); + std::reverse(row.begin(), row.end()); + setRow(i, row); + } + return this->field != oldField; +} + + +std::vector Field::getEmptyTiles() const { + std::vector empty; + for (size_t i = 0; i < this->FieldSize; ++i) { + for (size_t j = 0; j < this->FieldSize; ++j) { + if (this->field[i][j] == 0) { + empty.push_back(Position(i, j)); + } + } + } + return empty; +} + +bool Field::spawnTile(const std::vector &empty) { + if (!empty.empty()) { + std::uniform_int_distribution<> dist(0, static_cast(empty.size() - 1)); + std::uniform_int_distribution<> chance(1, 10); + int choice = dist(this->mt); + Position spawnPoint = empty[choice]; + this->field[spawnPoint.x][spawnPoint.y] = ((chance(this->mt) == 10) ? 4 : 2); + return true; + } + return false; +} + + +void Field::debug() const { + for (size_t i = 0; i < this->FieldSize; ++i) { + for (size_t j = 0; j < this->FieldSize; ++j) { + printf("%5llu ", this->field[i][j]); + } + printf("\n"); + } +} \ No newline at end of file diff --git a/source/line.cpp b/source/line.cpp new file mode 100644 index 0000000..e713e9c --- /dev/null +++ b/source/line.cpp @@ -0,0 +1,47 @@ +#include "line.h" + +Line::Line() { + this->line.clear(); + this->line.reserve(4); +} + +Line::Line(size_t length) { + this->line.clear(); + this->line.reserve(length); +} + +void Line::processLine() { + std::vector result; + Tile pending = 0; + bool hasPending = false; + + for (Tile element : this->line) { + if (element == 0) continue; + + if (!hasPending) { + pending = element; + hasPending = true; + continue; + } + + if (element == pending) { + result.push_back(pending * 2); + this->score += pending * 2; + hasPending = false; + pending = 0; + } else { + result.push_back(pending); + pending = element; + } + } + + if (hasPending) { + result.push_back(pending); + } + + while (result.size() < sourceLine.size()) { + result.push_back(0); + } + + return result; +} \ No newline at end of file