From a581945aa7c77251420489f4b22222ec15abcd7a Mon Sep 17 00:00:00 2001 From: roy Date: Sun, 5 Jul 2026 01:11:04 +0300 Subject: Basic movement finished. --- main.cpp | 395 +++++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 259 insertions(+), 136 deletions(-) (limited to 'main.cpp') diff --git a/main.cpp b/main.cpp index ba00d88..7f6a1f3 100644 --- a/main.cpp +++ b/main.cpp @@ -1,152 +1,275 @@ #include "terminal-game-utils.hpp" -#include +#include +#include #include #include +#include #include #include #include #include +#include +#include #include struct termios orig_termios; class Snake { -private: - struct mSnakeUnit { - int x; - int y; - std::string direction; - std::string appearance; - }; - - const std::string HEAD_ARROWS = "⇐⇓⇑⇒"; - const std::string SNAKE_BODY = "═║╔╗╚╝"; - - bool mSnakeAteApple = false; - char mSnakeDirection = 'r'; - bool running = true; - int mWidth, mHeight, mStartingSpeed; - double mAcceleration; - std::queue mSnake; - std::queue mKeyQueue; - - std::string mRepeat(int times, std::string str) { - std::string returned; - for (int i = 0; i < times; i++) - returned += str; - return returned; - } - - std::string mGetTurningCharacter(std::string ldur) { - if (ldur.length() != 2) - return ""; - - if (ldur == "lr") - return "═"; - if (ldur == "du") - return "║"; - if (ldur == "dr") - return "╔"; - if (ldur == "ld") - return "╗"; - if (ldur == "ur") - return "╚"; - if (ldur == "lu") - return "╝"; - - return ""; - } - - void mPrintBoardOutlines() { - TGU::MoveCursor(0, 0); - - std::string horizontalLine = mRepeat(mWidth * 2 - 1, "═"); - std::string horizontalSpaces = std::string(mWidth * 2 - 1, ' '); - - std::cout << "╔" << horizontalLine + "╗" << std::endl; - - for (int i = 0; i < mHeight; i++) { - std::cout << "║" + horizontalSpaces << "║" << std::endl; - } - - std::cout << "╚" << horizontalLine + "╝" << std::endl; - } - - void mSetSnakeDirection() { - TGU::GameKey popped = mKeyQueue.front(); - if (popped == TGU::GameKey::W || popped == TGU::GameKey::UPARROW) - mSnakeDirection = 'u'; - if (popped == TGU::GameKey::A || popped == TGU::GameKey::LEFTARROW) - mSnakeDirection = 'l'; - if (popped == TGU::GameKey::S || popped == TGU::GameKey::DOWNARROW) - mSnakeDirection = 'd'; - if (popped == TGU::GameKey::D || popped == TGU::GameKey::RIGHTARROW) - mSnakeDirection = 'r'; - } - - void mSnakeStep() { - - if (!mSnakeAteApple) - mSnake.pop(); - mSnake.push({1, 2, mSnakeDirection, ""}) - } - - void mGameTick() { - if (mKeyQueue.size() != 0) { - mSetSnakeDirection(); - - mSnakeStep(); - } - } - - void mInputLoop() { - TGU::GameKey key; - while (running) { - key = TGU::GetKeyPress(); - } - } - - void mGameLoop() { - while (running) { - mGameTick(); - } - } - -public: - struct GameSettings { - int width = 14; - int height = 14; - int startingSpeed = 3; - double acceleration = 0.3; - }; - - Snake(const GameSettings &settings) - : mWidth(settings.width), mHeight(settings.height), - mStartingSpeed(settings.startingSpeed), - mAcceleration(settings.acceleration) { - if (mWidth < 5) - mWidth = 5; - if (mHeight < 5) - mHeight = 5; - } - - void StartGame() { - mPrintBoardOutlines(); - - mSnake.push({0, 2, "lr", "═"}); - mSnake.push({1, 2, "lr", "═"}); - mSnake.push({2, 2, "lr", "═"}); - mSnake.push({3, 2, "lr", "⇒"}); - - auto InputLoopResponse = - std::async(std::launch::async, [this] { mInputLoop(); }); - - mInputLoop(); - } + private: + struct mSnakeUnit { + int x; + int y; + std::string appearance; + }; + + const std::map HEAD_ARROWS = { + {'l', "◁"}, {'d', "▽"}, {'u', "△"}, {'r', "▷"}}; + + const std::string SNAKE_BODY = "═║╔╗╚╝"; + bool mSnakeAteApple = false; + char mSnakeDirection = 'r'; + bool mRunning = true; + int mDelay = 500; + int mWidth, mHeight, mStartingSpeed; + double mAcceleration; + mSnakeUnit mLastDequeued; + std::deque mSnake; + std::queue mKeyQueue; + + std::string mRepeat(int times, std::string str) { + std::string returned; + for (int i = 0; i < times; i++) + returned += str; + return returned; + } + + std::uint8_t mGetDirectionByte(mSnakeUnit pos, mSnakeUnit goal) { + if (pos.x > goal.x) + return 0b0001; + if (pos.x < goal.x) + return 0b1000; + if (pos.y > goal.y) + return 0b0100; + if (pos.y < goal.y) + return 0b0010; + return 0b0000; + } + + std::string mGetTurningCharacter(mSnakeUnit Before, mSnakeUnit Current, + mSnakeUnit After) { + + std::uint8_t BeforeDirection = mGetDirectionByte(Current, Before); + std::uint8_t AfterDirection = mGetDirectionByte(Current, After); + + std::uint8_t CharacterBytes = BeforeDirection + AfterDirection; + + if (CharacterBytes == 0b1001) + return "═"; + if (CharacterBytes == 0b0110) + return "║"; + if (CharacterBytes == 0b1010) + return "╔"; + if (CharacterBytes == 0b0011) + return "╗"; + if (CharacterBytes == 0b1100) + return "╚"; + if (CharacterBytes == 0b0101) + return "╝"; + + return ""; + } + + void mPrintBoardOutlines() { + tgu::MoveCursor(0, 0); + + std::string horizontalLine = mRepeat(mWidth * 2 - 1, "═"); + std::string horizontalSpaces = std::string(mWidth * 2 - 1, ' '); + + std::cout << "╔" << horizontalLine + "╗\n\r"; + + for (int i = 0; i < mHeight; i++) { + std::cout << "║" + horizontalSpaces << "║\n\r"; + } + + std::cout << "╚" << horizontalLine + "╝\n\r"; + } + + char mFlipDirection(char c) { + if (c == 'l') + return 'r'; + if (c == 'r') + return 'l'; + if (c == 'd') + return 'u'; + if (c == 'u') + return 'd'; + return ' '; + } + + void mSetSnakeDirection() { + tgu::GameKey popped = mKeyQueue.front(); + mKeyQueue.pop(); + + char PotentialSnakeDirection; + + if (popped == tgu::GameKey::W || popped == tgu::GameKey::UPARROW) + PotentialSnakeDirection = 'u'; + else if (popped == tgu::GameKey::A || popped == tgu::GameKey::LEFTARROW) + PotentialSnakeDirection = 'l'; + else if (popped == tgu::GameKey::S || popped == tgu::GameKey::DOWNARROW) + PotentialSnakeDirection = 'd'; + else if (popped == tgu::GameKey::D || + popped == tgu::GameKey::RIGHTARROW) { + PotentialSnakeDirection = 'r'; + } else + return; + + if (PotentialSnakeDirection == mFlipDirection(mSnakeDirection)) + return; + else + mSnakeDirection = PotentialSnakeDirection; + } + + std::tuple mGetDirectionOffset(char c) { + tgu::MoveCursor(30, 30); + std::cout << c; + if (c == 'l') + return std::tuple(-1, 0); + if (c == 'd') + return std::tuple(0, 1); + if (c == 'u') + return std::tuple(0, -1); + if (c == 'r') { + return std::tuple(1, 0); + } + + std::cout << "Some ting wong. Direction is " << +c; + return std::tuple(0, 0); + } + + void mSnakeStep() { + if (!mSnakeAteApple) { + mLastDequeued = mSnake.front(); + mSnake.pop_front(); + } + + std::tuple Offset = mGetDirectionOffset(mSnakeDirection); + + int PotentialX = mSnake.back().x + std::get<0>(Offset); + int PotentialY = mSnake.back().y + std::get<1>(Offset); + + std::cout << "Potentials: " << PotentialX << ", " << PotentialY; + std::cout << "Actual: " << mSnake.back().x << ", " << mSnake.back().y; + + if (mSnake[mSnake.size() - 2].x != PotentialX || + mSnake[mSnake.size() - 2].y != PotentialY) { + + mSnake.push_back( + {PotentialX, PotentialY, HEAD_ARROWS.at(mSnakeDirection)}); + + mSnake[mSnake.size() - 2].appearance = mGetTurningCharacter( + mSnake[mSnake.size() - 3], mSnake[mSnake.size() - 2], + mSnake[mSnake.size() - 1]); + } else { + mRunning = false; + std::cout << "Stop the game. Snake somehow ate itself????"; + } + + tgu::MoveCursor(mHeight + 2, 0); + std::cout << "Brother. " << mSnake.back().x << ", " << mSnake.back().y; + } + + void mGameTick() { + if (mKeyQueue.size() != 0) { + mSetSnakeDirection(); + } + + mSnakeStep(); + } + + void mInputLoop() { + tgu::GameKey key; + while (mRunning) { + key = tgu::GetKeyPress(); + if (mKeyQueue.size() <= 2) + mKeyQueue.push(key); + } + } + + bool mHorizontalConnectors(mSnakeUnit, mSnakeUnit) { return false; } + + void mDrawSnake() { + for (int i = 0; i < mSnake.size(); i++) { + mSnakeUnit &s = mSnake[i]; + + if (s.appearance == "═") { + tgu::MoveCursor(s.y + 2, (s.x * 2) + 1); + std::cout << mRepeat(3, s.appearance); + } else if (i != 0 && mHorizontalConnectors(s, mSnake[i - 1])) { + std::cout << ""; + } else { + tgu::MoveCursor(s.y + 2, (s.x * 2) + 2); + std::cout << s.appearance; + } + } + + tgu::MoveCursor(mHeight + 2, 0); + std::cout << "Brother. " << mSnake.back().x << ", " << mSnake.back().y; + + tgu::MoveCursor(mLastDequeued.y + 2, (mLastDequeued.x * 2) + 1); + std::cout << " "; + + std::cout << std::flush; + } + + void mGameLoop() { + while (mRunning) { + std::this_thread::sleep_for(std::chrono::milliseconds(mDelay)); + mGameTick(); + + mDrawSnake(); + } + } + + public: + struct GameSettings { + int width = 14; + int height = 14; + int startingSpeed = 3; + double acceleration = 0.3; + }; + + Snake(const GameSettings &settings) + : mWidth(settings.width), mHeight(settings.height), + mStartingSpeed(settings.startingSpeed), + mAcceleration(settings.acceleration) { + if (mWidth < 5) + mWidth = 5; + if (mHeight < 5) + mHeight = 5; + } + + void StartGame() { + tgu::EnableRawMode(); + tgu::MakeCursorInvisible(); + tgu::EnterGameBuffer(); + mPrintBoardOutlines(); + + mSnake.push_back({0, 2, "═"}); + mSnake.push_back({1, 2, "═"}); + mSnake.push_back({2, 2, "═"}); + mSnake.push_back({3, 2, "⇒"}); + mSnakeDirection = 'r'; + + auto InputLoopResponse = + std::async(std::launch::async, [this] { mInputLoop(); }); + + mGameLoop(); + tgu::LeaveGameBuffer(); + } }; int main() { - TGU::EnableRawMode(); - Snake hi({}); - hi.StartGame(); + Snake hi({}); + hi.StartGame(); } -- cgit v1.3.1