diff options
| -rw-r--r-- | README.md | 0 | ||||
| -rwxr-xr-x | main | bin | 305160 -> 270200 bytes | |||
| -rw-r--r-- | main.cpp | 347 | ||||
| -rw-r--r-- | terminal-game-utils.hpp | 38 |
4 files changed, 264 insertions, 121 deletions
diff --git a/README.md b/README.md Binary files differnew file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/README.md @@ -1,152 +1,275 @@ #include "terminal-game-utils.hpp" -#include <format> +#include <chrono> +#include <cstdint> #include <future> #include <iostream> +#include <ostream> #include <poll.h> #include <queue> #include <string> #include <termios.h> +#include <thread> +#include <tuple> #include <unistd.h> struct termios orig_termios; class Snake { -private: - struct mSnakeUnit { - int x; - int y; - std::string direction; - std::string appearance; - }; + private: + struct mSnakeUnit { + int x; + int y; + std::string appearance; + }; - const std::string HEAD_ARROWS = "⇐⇓⇑⇒"; - const std::string SNAKE_BODY = "═║╔╗╚╝"; + const std::map<char, std::string> HEAD_ARROWS = { + {'l', "◁"}, {'d', "▽"}, {'u', "△"}, {'r', "▷"}}; - bool mSnakeAteApple = false; - char mSnakeDirection = 'r'; - bool running = true; - int mWidth, mHeight, mStartingSpeed; - double mAcceleration; - std::queue<mSnakeUnit> mSnake; - std::queue<TGU::GameKey> mKeyQueue; + 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<mSnakeUnit> mSnake; + std::queue<tgu::GameKey> 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 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 ""; + 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; + } - if (ldur == "lr") - return "═"; - if (ldur == "du") - return "║"; - if (ldur == "dr") - return "╔"; - if (ldur == "ld") - return "╗"; - if (ldur == "ur") - return "╚"; - if (ldur == "lu") - return "╝"; + std::string mGetTurningCharacter(mSnakeUnit Before, mSnakeUnit Current, + mSnakeUnit After) { - return ""; - } + std::uint8_t BeforeDirection = mGetDirectionByte(Current, Before); + std::uint8_t AfterDirection = mGetDirectionByte(Current, After); - void mPrintBoardOutlines() { - TGU::MoveCursor(0, 0); + std::uint8_t CharacterBytes = BeforeDirection + AfterDirection; - std::string horizontalLine = mRepeat(mWidth * 2 - 1, "═"); - std::string horizontalSpaces = std::string(mWidth * 2 - 1, ' '); + if (CharacterBytes == 0b1001) + return "═"; + if (CharacterBytes == 0b0110) + return "║"; + if (CharacterBytes == 0b1010) + return "╔"; + if (CharacterBytes == 0b0011) + return "╗"; + if (CharacterBytes == 0b1100) + return "╚"; + if (CharacterBytes == 0b0101) + return "╝"; - std::cout << "╔" << horizontalLine + "╗" << std::endl; + return ""; + } - for (int i = 0; i < mHeight; i++) { - std::cout << "║" + horizontalSpaces << "║" << std::endl; - } + void mPrintBoardOutlines() { + tgu::MoveCursor(0, 0); - std::cout << "╚" << horizontalLine + "╝" << std::endl; - } + std::string horizontalLine = mRepeat(mWidth * 2 - 1, "═"); + std::string horizontalSpaces = std::string(mWidth * 2 - 1, ' '); - 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'; - } + std::cout << "╔" << horizontalLine + "╗\n\r"; - void mSnakeStep() { + for (int i = 0; i < mHeight; i++) { + std::cout << "║" + horizontalSpaces << "║\n\r"; + } - if (!mSnakeAteApple) - mSnake.pop(); - mSnake.push({1, 2, mSnakeDirection, ""}) - } + std::cout << "╚" << horizontalLine + "╝\n\r"; + } - void mGameTick() { - if (mKeyQueue.size() != 0) { - mSetSnakeDirection(); + 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 ' '; + } - mSnakeStep(); - } - } + void mSetSnakeDirection() { + tgu::GameKey popped = mKeyQueue.front(); + mKeyQueue.pop(); - void mInputLoop() { - TGU::GameKey key; - while (running) { - key = TGU::GetKeyPress(); - } - } + char PotentialSnakeDirection; - void mGameLoop() { - while (running) { - mGameTick(); - } - } + 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; -public: - struct GameSettings { - int width = 14; - int height = 14; - int startingSpeed = 3; - double acceleration = 0.3; - }; + if (PotentialSnakeDirection == mFlipDirection(mSnakeDirection)) + return; + else + mSnakeDirection = PotentialSnakeDirection; + } - 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; - } + std::tuple<int, int> mGetDirectionOffset(char c) { + tgu::MoveCursor(30, 30); + std::cout << c; + if (c == 'l') + return std::tuple<int, int>(-1, 0); + if (c == 'd') + return std::tuple<int, int>(0, 1); + if (c == 'u') + return std::tuple<int, int>(0, -1); + if (c == 'r') { + return std::tuple<int, int>(1, 0); + } - void StartGame() { - mPrintBoardOutlines(); + std::cout << "Some ting wong. Direction is " << +c; + return std::tuple<int, int>(0, 0); + } - mSnake.push({0, 2, "lr", "═"}); - mSnake.push({1, 2, "lr", "═"}); - mSnake.push({2, 2, "lr", "═"}); - mSnake.push({3, 2, "lr", "⇒"}); + void mSnakeStep() { + if (!mSnakeAteApple) { + mLastDequeued = mSnake.front(); + mSnake.pop_front(); + } - auto InputLoopResponse = - std::async(std::launch::async, [this] { mInputLoop(); }); + std::tuple<int, int> Offset = mGetDirectionOffset(mSnakeDirection); - mInputLoop(); - } + 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(); } diff --git a/terminal-game-utils.hpp b/terminal-game-utils.hpp index 0e41074..d7d9522 100644 --- a/terminal-game-utils.hpp +++ b/terminal-game-utils.hpp @@ -1,3 +1,4 @@ +#include <format> #include <iostream> #include <map> #include <ostream> @@ -6,7 +7,7 @@ #include <termios.h> #include <unistd.h> -namespace TGU { +namespace tgu { inline struct termios orig_termios; @@ -146,13 +147,12 @@ inline GameKey GetKeyPress() { keypress_string += c; } - if (KeyMap.count(keypress_string)) + if (KeyMap.find(keypress_string) != KeyMap.end()) return KeyMap[keypress_string]; - - return KeyMap[keypress_string]; + return GameKey::INVALID; } -// Terminal color printers: +// Text printers: inline void TextGreen() { std::cout << "\033[32m"; } @@ -160,12 +160,24 @@ inline void TextRed() { std::cout << "\033[31m"; } inline void TextBlue() { std::cout << "\033[34m"; } -inline void TextReset() { std::cout << "\033[0m"; } - inline void TextRGB(int r, int g, int b) { std::cout << std::format("\033[38;2;{};{};{}m", r, g, b); }; +// Background printers: + +inline void BGGreen() { std::cout << "\033[42m"; } + +inline void BGRed() { std::cout << "\033[41m"; } + +inline void BGBlue() { std::cout << "\033[44m"; } + +inline void BGRGB(int r, int g, int b) { + std::cout << std::format("\033[48;2;{};{};{}m", r, g, b); +} + +inline void TerminalColorReset() { std::cout << "\033[0m"; } + // Game buffer printers: inline void EnterGameBuffer() { std::cout << "\033[?1049h"; } @@ -173,6 +185,14 @@ inline void EnterGameBuffer() { std::cout << "\033[?1049h"; } inline void LeaveGameBuffer() { std::cout << "\033[?1049l"; } inline void MoveCursor(int row, int col) { - std::cout << "\033[" << row << ";" << col << "H" << std::flush; + std::cout << "\033[" << (row + 1) << ";" << (col + 1) << "H" << std::flush; +} + +inline void MakeCursorVisible() { std::cout << "\033[?25h"; } + +inline void MakeCursorInvisible() { + std::cout << "\033[?25l"; + atexit(MakeCursorVisible); } -}; // namespace TGU + +}; // namespace tgu |
