Feature: only update screen when key event is detected
This commit is contained in:
208
maze.cxx
208
maze.cxx
@@ -1,12 +1,14 @@
|
|||||||
#include <chrono>
|
#include <array>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <mutex>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
#include <thread>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class Entity {
|
class Entity {
|
||||||
public:
|
public:
|
||||||
@@ -43,106 +45,98 @@ class Game {
|
|||||||
size_t termColSize;
|
size_t termColSize;
|
||||||
size_t termRowSize;
|
size_t termRowSize;
|
||||||
std::unique_ptr<Maze> maze;
|
std::unique_ptr<Maze> maze;
|
||||||
|
std::mutex mtx;
|
||||||
|
std::condition_variable cv;
|
||||||
|
bool updateNeeded;
|
||||||
bool quitGame;
|
bool quitGame;
|
||||||
bool winGame;
|
bool winGame;
|
||||||
|
|
||||||
|
static constexpr std::array<char, 5> sprites = {' ', 'H', '*', 'X', 'O'};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Game() : quitGame(false), winGame(false) {}
|
Game() : updateNeeded(false), quitGame(false), winGame(false) {}
|
||||||
|
|
||||||
void getTermSize() {
|
void getTermSize() {
|
||||||
struct winsize w;
|
struct winsize w;
|
||||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == -1) {
|
||||||
|
throw std::runtime_error("Failed to get terminal size");
|
||||||
|
}
|
||||||
termRowSize = w.ws_row;
|
termRowSize = w.ws_row;
|
||||||
termColSize = w.ws_col;
|
termColSize = w.ws_col;
|
||||||
}
|
}
|
||||||
|
|
||||||
void termTooSmallHandler() {
|
void termTooSmallHandler() const {
|
||||||
if (termColSize < 2 * maze->rowSize || termRowSize < maze->colSize) {
|
if (termColSize < 2 * maze->rowSize || termRowSize < maze->colSize) {
|
||||||
std::cout << "Terminal is too small to display the whole maze.\n"
|
|
||||||
"Either make the terminal window bigger, or use a smaller maze map.\n";
|
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void getMazeSize(const char *path) {
|
void getMazeSize(const std::string &path) {
|
||||||
FILE *file;
|
std::ifstream file(path);
|
||||||
if (!(file = fopen(path, "r"))) {
|
if (!file) {
|
||||||
std::cout << "Couldn't find map file. Check file path.\n";
|
throw std::runtime_error("Couldn't find map file. Check file path.");
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char ch;
|
std::string line;
|
||||||
size_t x_cnt = 0;
|
size_t x_cnt = 0;
|
||||||
size_t y_cnt = 0;
|
size_t y_cnt = 0;
|
||||||
bool cnt_x = true;
|
if (std::getline(file, line)) {
|
||||||
while ((ch = getc(file)) != EOF) {
|
x_cnt = line.length();
|
||||||
if (ch == '\n') {
|
y_cnt = 1;
|
||||||
++y_cnt;
|
|
||||||
cnt_x = false;
|
|
||||||
} else if (cnt_x) {
|
|
||||||
++x_cnt;
|
|
||||||
}
|
}
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
++y_cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
maze->rowSize = x_cnt;
|
maze->rowSize = x_cnt;
|
||||||
maze->colSize = y_cnt;
|
maze->colSize = y_cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void restoreCursor() { std::cout << "\033[u"; }
|
void move(Entity::Move move) {
|
||||||
|
auto &player = maze->player;
|
||||||
|
auto &pntr = maze->pntr;
|
||||||
|
|
||||||
void saveCursor() { std::cout << "\033[s"; }
|
auto updatePosition = [&](int dx, int dy) {
|
||||||
|
if (player->xIdx == maze->start->xIdx && player->yIdx == maze->start->yIdx) {
|
||||||
|
pntr[player->yIdx][player->xIdx] = 2;
|
||||||
|
} else {
|
||||||
|
pntr[player->yIdx][player->xIdx] = 0;
|
||||||
|
}
|
||||||
|
player->xIdx += dx;
|
||||||
|
player->yIdx += dy;
|
||||||
|
pntr[player->yIdx][player->xIdx] = 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (move) {
|
||||||
|
case Entity::Move::Left:
|
||||||
|
if (player->xIdx != 0 && pntr[player->yIdx][player->xIdx - 1] != 1) {
|
||||||
|
updatePosition(-1, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Entity::Move::Down:
|
||||||
|
if (player->yIdx != maze->colSize - 1 && pntr[player->yIdx + 1][player->xIdx] != 1) {
|
||||||
|
updatePosition(0, 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Entity::Move::Up:
|
||||||
|
if (player->yIdx != 0 && pntr[player->yIdx - 1][player->xIdx] != 1) {
|
||||||
|
updatePosition(0, -1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Entity::Move::Right:
|
||||||
|
if (player->xIdx != maze->rowSize - 1 && pntr[player->yIdx][player->xIdx + 1] != 1) {
|
||||||
|
updatePosition(1, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void restoreCursor() const { std::cout << "\033[u"; }
|
||||||
|
|
||||||
|
void saveCursor() const { std::cout << "\033[s"; }
|
||||||
|
|
||||||
void zeroCursor() { std::cout << "\033[0;0H" << std::flush; }
|
void zeroCursor() { std::cout << "\033[0;0H" << std::flush; }
|
||||||
|
|
||||||
void move(Entity::Move move) {
|
void allocMazeBuffer(const std::string &path) {
|
||||||
if (move == Entity::Move::Left && maze->player->xIdx != 0 &&
|
|
||||||
maze->pntr[maze->player->yIdx][maze->player->xIdx - 1] != 1) {
|
|
||||||
if (maze->player->xIdx == maze->start->xIdx && maze->player->yIdx == maze->start->yIdx) {
|
|
||||||
maze->pntr[maze->player->yIdx][maze->player->xIdx] = 2;
|
|
||||||
} else {
|
|
||||||
maze->pntr[maze->player->yIdx][maze->player->xIdx] = 0;
|
|
||||||
}
|
|
||||||
--maze->player->xIdx;
|
|
||||||
maze->pntr[maze->player->yIdx][maze->player->xIdx] = 4;
|
|
||||||
}
|
|
||||||
if (move == Entity::Move::Down && maze->player->yIdx != maze->colSize - 1 &&
|
|
||||||
maze->pntr[maze->player->yIdx + 1][maze->player->xIdx] != 1) {
|
|
||||||
if (maze->player->xIdx == maze->start->xIdx && maze->player->yIdx == maze->start->yIdx) {
|
|
||||||
maze->pntr[maze->player->yIdx][maze->player->xIdx] = 2;
|
|
||||||
} else {
|
|
||||||
maze->pntr[maze->player->yIdx][maze->player->xIdx] = 0;
|
|
||||||
}
|
|
||||||
++maze->player->yIdx;
|
|
||||||
maze->pntr[maze->player->yIdx][maze->player->xIdx] = 4;
|
|
||||||
}
|
|
||||||
if (move == Entity::Move::Up && maze->player->yIdx != 0 &&
|
|
||||||
maze->pntr[maze->player->yIdx - 1][maze->player->xIdx] != 1) {
|
|
||||||
if (maze->player->xIdx == maze->start->xIdx && maze->player->yIdx == maze->start->yIdx) {
|
|
||||||
maze->pntr[maze->player->yIdx][maze->player->xIdx] = 2;
|
|
||||||
} else {
|
|
||||||
maze->pntr[maze->player->yIdx][maze->player->xIdx] = 0;
|
|
||||||
}
|
|
||||||
--maze->player->yIdx;
|
|
||||||
maze->pntr[maze->player->yIdx][maze->player->xIdx] = 4;
|
|
||||||
}
|
|
||||||
if (move == Entity::Move::Right && maze->player->xIdx != maze->rowSize - 1 &&
|
|
||||||
maze->pntr[maze->player->yIdx][maze->player->xIdx + 1] != 1) {
|
|
||||||
if (maze->player->xIdx == maze->start->xIdx && maze->player->yIdx == maze->start->yIdx) {
|
|
||||||
maze->pntr[maze->player->yIdx][maze->player->xIdx] = 2;
|
|
||||||
} else {
|
|
||||||
maze->pntr[maze->player->yIdx][maze->player->xIdx] = 0;
|
|
||||||
}
|
|
||||||
++maze->player->xIdx;
|
|
||||||
maze->pntr[maze->player->yIdx][maze->player->xIdx] = 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void freeMap() {
|
|
||||||
// Free memory for maze and entities
|
|
||||||
maze.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void allocMazeBuffer(const char *path) {
|
|
||||||
maze = std::make_unique<Maze>(0, 0);
|
maze = std::make_unique<Maze>(0, 0);
|
||||||
maze->player = std::make_unique<Entity>(0, 0);
|
maze->player = std::make_unique<Entity>(0, 0);
|
||||||
maze->start = std::make_unique<Entity>(0, 0);
|
maze->start = std::make_unique<Entity>(0, 0);
|
||||||
@@ -152,38 +146,33 @@ class Game {
|
|||||||
maze->pntr.resize(maze->colSize, std::vector<uint32_t>(maze->rowSize, 0));
|
maze->pntr.resize(maze->colSize, std::vector<uint32_t>(maze->rowSize, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadMazeToBuffer(const char *path) {
|
void loadMazeToBuffer(const std::string &path) {
|
||||||
FILE *file;
|
std::ifstream file(path);
|
||||||
if (!(file = fopen(path, "r"))) {
|
if (!file) {
|
||||||
std::cout << "Couldn't find map file. Check file path.\n";
|
throw std::runtime_error("Couldn't find map file. Check file path.");
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string line;
|
||||||
size_t y_cord = 0;
|
size_t y_cord = 0;
|
||||||
size_t x_cord = 0;
|
while (std::getline(file, line)) {
|
||||||
|
for (size_t x_cord = 0; x_cord < line.length(); ++x_cord) {
|
||||||
char cur;
|
char cur = line[x_cord];
|
||||||
while (EOF != (cur = getc(file))) {
|
if (cur == '4') {
|
||||||
if ('\n' == cur) {
|
|
||||||
x_cord = 0;
|
|
||||||
++y_cord;
|
|
||||||
continue;
|
|
||||||
} else if ('4' == cur) {
|
|
||||||
maze->player->xIdx = x_cord;
|
maze->player->xIdx = x_cord;
|
||||||
maze->player->yIdx = y_cord;
|
maze->player->yIdx = y_cord;
|
||||||
maze->start->xIdx = x_cord;
|
maze->start->xIdx = x_cord;
|
||||||
maze->start->yIdx = y_cord;
|
maze->start->yIdx = y_cord;
|
||||||
} else if ('3' == cur) {
|
} else if (cur == '3') {
|
||||||
maze->end->xIdx = x_cord;
|
maze->end->xIdx = x_cord;
|
||||||
maze->end->yIdx = y_cord;
|
maze->end->yIdx = y_cord;
|
||||||
}
|
}
|
||||||
maze->pntr[y_cord][x_cord] = (uint32_t)(cur - '0');
|
maze->pntr[y_cord][x_cord] = static_cast<uint32_t>(cur - '0');
|
||||||
++x_cord;
|
}
|
||||||
|
++y_cord;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void print() {
|
void print() const {
|
||||||
const char sprites[] = " H*XO";
|
|
||||||
saveCursor();
|
saveCursor();
|
||||||
for (size_t y_cord = 0; y_cord < maze->colSize; ++y_cord) {
|
for (size_t y_cord = 0; y_cord < maze->colSize; ++y_cord) {
|
||||||
for (size_t x_cord = 0; x_cord < maze->rowSize; ++x_cord) {
|
for (size_t x_cord = 0; x_cord < maze->rowSize; ++x_cord) {
|
||||||
@@ -205,7 +194,6 @@ class Game {
|
|||||||
struct termios raw;
|
struct termios raw;
|
||||||
tcgetattr(STDIN_FILENO, &raw);
|
tcgetattr(STDIN_FILENO, &raw);
|
||||||
tcgetattr(STDIN_FILENO, &term_state);
|
tcgetattr(STDIN_FILENO, &term_state);
|
||||||
//// atexit(&Game::setTermDef);
|
|
||||||
|
|
||||||
raw.c_lflag &= ~(ECHO | ICANON);
|
raw.c_lflag &= ~(ECHO | ICANON);
|
||||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
|
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
|
||||||
@@ -249,23 +237,42 @@ class Game {
|
|||||||
case 'd':
|
case 'd':
|
||||||
move(Entity::Move::Right);
|
move(Entity::Move::Right);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkWin();
|
checkWin();
|
||||||
if (winGame) {
|
if (winGame) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mtx);
|
||||||
|
updateNeeded = true;
|
||||||
|
}
|
||||||
|
cv.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
quitGame = true;
|
quitGame = true;
|
||||||
|
cv.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
void update() {
|
void update() {
|
||||||
zeroCursor();
|
zeroCursor();
|
||||||
clear();
|
clear();
|
||||||
while (!quitGame) {
|
|
||||||
print();
|
print();
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(41667));
|
|
||||||
|
while (!quitGame) {
|
||||||
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
|
cv.wait(lock, [this] { return updateNeeded || quitGame; });
|
||||||
|
|
||||||
|
if (quitGame)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (updateNeeded) {
|
||||||
|
print();
|
||||||
|
updateNeeded = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (winGame) {
|
if (winGame) {
|
||||||
@@ -280,7 +287,11 @@ class Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void startGame(int argc, char *argv[]) {
|
void startGame(int argc, char *argv[]) {
|
||||||
if (argc > 1 && argc < 3) {
|
if (argc != 2) {
|
||||||
|
throw std::runtime_error("Usage: " + std::string(argv[0]) + " <maze_file>");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
getTermSize();
|
getTermSize();
|
||||||
allocMazeBuffer(argv[1]);
|
allocMazeBuffer(argv[1]);
|
||||||
termTooSmallHandler();
|
termTooSmallHandler();
|
||||||
@@ -291,6 +302,9 @@ class Game {
|
|||||||
|
|
||||||
updateThread.join();
|
updateThread.join();
|
||||||
captureThread.join();
|
captureThread.join();
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "Error: " << e.what() << std::endl;
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user