aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xmainbin0 -> 305160 bytes
-rw-r--r--main.cpp152
-rw-r--r--terminal-game-utils.hpp178
3 files changed, 330 insertions, 0 deletions
diff --git a/main b/main
new file mode 100755
index 0000000..701c417
--- /dev/null
+++ b/main
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