diff options
| -rwxr-xr-x | main | bin | 0 -> 305160 bytes | |||
| -rw-r--r-- | main.cpp | 152 | ||||
| -rw-r--r-- | terminal-game-utils.hpp | 178 |
3 files changed, 330 insertions, 0 deletions
| Binary files differ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..ba00d88 --- /dev/null +++ b/main.cpp @@ -0,0 +1,152 @@ +#include "terminal-game-utils.hpp" +#include <format> +#include <future> +#include <iostream> +#include <poll.h> +#include <queue> +#include <string> +#include <termios.h> +#include <unistd.h> + +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<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 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(); + } +}; + +int main() { + TGU::EnableRawMode(); + Snake hi({}); + hi.StartGame(); +} diff --git a/terminal-game-utils.hpp b/terminal-game-utils.hpp new file mode 100644 index 0000000..0e41074 --- /dev/null +++ b/terminal-game-utils.hpp @@ -0,0 +1,178 @@ +#include <iostream> +#include <map> +#include <ostream> +#include <poll.h> +#include <string> +#include <termios.h> +#include <unistd.h> + +namespace TGU { + +inline struct termios orig_termios; + +inline std::string newline = "\n\r"; + +enum class GameKey { + INVALID, + LEFTARROW, + DOWNARROW, + UPARROW, + RIGHTARROW, + ESCAPE, + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + SPACE, +}; + +inline std::map<std::string, GameKey> KeyMap = {{"a", GameKey::A}, + {"b", GameKey::B}, + {"c", GameKey::C}, + {"d", GameKey::D}, + {"e", GameKey::E}, + {"f", GameKey::F}, + {"g", GameKey::G}, + {"h", GameKey::H}, + {"i", GameKey::I}, + {"j", GameKey::J}, + {"k", GameKey::K}, + {"l", GameKey::L}, + {"m", GameKey::M}, + {"n", GameKey::N}, + {"o", GameKey::O}, + {"p", GameKey::P}, + {"q", GameKey::Q}, + {"r", GameKey::R}, + {"s", GameKey::S}, + {"t", GameKey::T}, + {"u", GameKey::U}, + {"v", GameKey::V}, + {"w", GameKey::W}, + {"x", GameKey::X}, + {"y", GameKey::Y}, + {"z", GameKey::Z}, + {"A", GameKey::A}, + {"B", GameKey::B}, + {"C", GameKey::C}, + {"D", GameKey::D}, + {"E", GameKey::E}, + {"F", GameKey::F}, + {"G", GameKey::G}, + {"H", GameKey::H}, + {"I", GameKey::I}, + {"J", GameKey::J}, + {"K", GameKey::K}, + {"L", GameKey::L}, + {"M", GameKey::M}, + {"N", GameKey::N}, + {"O", GameKey::O}, + {"P", GameKey::P}, + {"Q", GameKey::Q}, + {"R", GameKey::R}, + {"S", GameKey::S}, + {"T", GameKey::T}, + {"U", GameKey::U}, + {"V", GameKey::V}, + {"W", GameKey::W}, + {"X", GameKey::X}, + {"Y", GameKey::Y}, + {"Z", GameKey::Z}, + {"\x1b", GameKey::ESCAPE}, + {"\x1b[D", GameKey::LEFTARROW}, + {"\x1b[B", GameKey::DOWNARROW}, + {"\x1b[A", GameKey::UPARROW}, + {"\x1b[C", GameKey::RIGHTARROW}, + {" ", GameKey::SPACE}}; + +inline void DisableRawMode() { tcsetattr(0, TCSAFLUSH, &orig_termios); } + +inline void EnableRawMode() { + + tcgetattr(0, &orig_termios); + + atexit(DisableRawMode); + struct termios raw = orig_termios; + raw.c_iflag &= ~(ICRNL | INPCK | ISTRIP | IXON); + raw.c_oflag &= ~(OPOST); + raw.c_cflag |= (CS8); + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN); + raw.c_cc[VMIN] = 1; + raw.c_cc[VTIME] = 0; + + tcsetattr(0, TCSAFLUSH, &raw); +} + +inline bool InputAvailable(int timeout_ms) { + pollfd pfd{}; + pfd.fd = STDIN_FILENO; + pfd.events = POLLIN; + + return poll(&pfd, 1, timeout_ms) > 0; +} + +inline GameKey GetKeyPress() { + std::string keypress_string; + char c; + + read(STDIN_FILENO, &c, 1); + keypress_string += c; + + if (c == 27) + while (InputAvailable(10)) { + read(STDIN_FILENO, &c, 1); + keypress_string += c; + } + + if (KeyMap.count(keypress_string)) + return KeyMap[keypress_string]; + + return KeyMap[keypress_string]; +} + +// Terminal color printers: + +inline void TextGreen() { std::cout << "\033[32m"; } + +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); +}; + +// Game buffer printers: + +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; +} +}; // namespace TGU |
