initial commit

This commit is contained in:
2021-04-25 23:00:33 +04:00
commit cb7cb42447
5 changed files with 655 additions and 0 deletions

316
main.cpp Normal file
View File

@@ -0,0 +1,316 @@
#include <ncurses.h>
#include <string>
// Пока оно true - будем крутиться в игре,
// если станет false - выходим.
static bool run = false;
// Если true - значит проиграли и надо нарисовать плашку Game Over
static bool gameOver;
// Если true - у нас пауза, ничего не происходит, ждем нажатия кнопок
static bool pause;
//Ширина и высота терминального окна
static int termWidth;
static int termHeight;
// Координаты головы
static int playerX;
static int playerY;
// Координаты еды
static int fruitX;
static int fruitY;
// Смещения по осям - куда бежать
// 1 или -1 - бежим вдоль оси. 0 - не бежим
static int dx;
static int dy;
// Очки
static int score;
// Начало и конец питона в массиве
static int pythonStart;
static int pythonEnd;
// Максимальный размер массива, чтобы можно было теоретически покрыть весь экран
static int maxPythonSize;
// Предварительное описание, реализация позже будет.
void DropFruit();
// Структурка для хранения координат в массиве.
struct coord
{
int x;
int y;
};
// Массив для координат тела - определение
static coord * python;
// Тут мы инициализируем все что надо
void InitGame()
{
// Инициализируем ncurses и выставляем ей разное
initscr();
raw();
cbreak();
noecho();
keypad(stdscr, true);
// Таймаут ввода, он же скорость игры
timeout(200);
// Получаем размер терминального окна
getmaxyx(stdscr, termHeight, termWidth);
// Убираем курсор
curs_set(0);
//Рисуем рамку
box(stdscr, 0, 0);
//Инициализируем разные переменные
run = true;
gameOver = false;
pause = false;
// Голову в центр экрана
playerX = termWidth / 2;
playerY = termHeight / 2;
// Едем вправо
dx = 1;
dy = 0;
// Очков пока ноль
score = 0;
// Высчитываем максимальный размер питона, исходя из размера экрана
// (ну вдруг кто-то умудрится)
maxPythonSize = (termWidth - 2) * (termHeight - 2);
// Инициализируем массив под координаты частей тела
python = new coord[maxPythonSize]();
// Задаем начальные координаты
python[0].x = playerX;
python[0].y = playerY;
// и указатели на хвост и голову
pythonStart = 0;
pythonEnd = 0;
// Бросаем первую еду
DropFruit();
}
// Тут завершается ncurses
void EndGame()
{
endwin();
}
// Процедура, ловящая ввод и обрабатывающая его
void Input()
{
// Получаем нажатую клавишу
int c = getch();
// в зависимости от нажатой клавиши правим смещения
switch (c) {
// Если нажали q - ставим run в false, потому как надо выходить
case 'q':
run = false;
break;
case ' ':
pause = !pause;
break;
case KEY_UP:
// На каждый чих надо проверить, не пытается ли игрок поехать в противоположную сторону.
// Если так - нафиг. Ничего не делаем.
if (dy == 1)
break;
dx = 0;
dy = -1;
break;
case KEY_DOWN:
if (dy == -1)
break;
dx = 0;
dy = 1;
break;
case KEY_LEFT:
if (dx == 1)
break;
dx = -1;
dy = 0;
break;
case KEY_RIGHT:
if (dx == -1)
break;
dx = 1;
dy = 0;
}
}
// Микроскопическая процедурка, печатающая очки в углу
void PrintScore()
{
mvprintw(0, termWidth - 20, "Score: %5d", score);
}
// Тут мы двигаем голову и проверяем все что можно
void Logic()
{
// Двигаем по X, просто прибавляя к координатам смещение
playerX += dx;
// Проверяем на выход за рамку
if (playerX > termWidth - 2 || playerX < 1)
{
// Если вылезли - ставим флаг GameOver и run в false, чтобы выйти
gameOver = true;
run = false;
return;
}
playerY += dy;
if (playerY > termHeight - 2 || playerY < 1)
{
gameOver = true;
run = false;
return;
}
// Тут мы проходим по куску массива с телом между началом и концом
// чтобы выяснить, не куснули ли сами себя.
int i = pythonStart;
while (i <= pythonEnd)
{
// Если координаты головы совпадают с координатами из массива - Game over
if (playerX == python[i].x && playerY == python[i].y)
{
gameOver = true;
run = false;
return;
}
i++;
}
// Проверяем, не куснули ли мы еду
if (playerX == fruitX && playerY == fruitY)
{
// Если куснули - выкидываем новую
DropFruit();
// Добавляем очков
score++;
// Сдвигаем хвост
pythonEnd++;
// Если хвост дошел до конца массива - переставляем его на начало
if (pythonEnd == maxPythonSize)
pythonEnd = 0;
// Выводим новые очки в угол
PrintScore();
}
// Двигаем питона
// Стираем хвост
mvprintw(python[pythonEnd].y, python[pythonEnd].x, " ");
// Двигаем указатель головы назад по массиву
pythonStart--;
// Не забывая его переставить, если он дошел до нуля
if (pythonStart < 0)
pythonStart = maxPythonSize - 1;
// Тоже и с указателем хвоста
pythonEnd--;
if (pythonEnd < 0)
pythonEnd = maxPythonSize - 1;
// Записываем новое положение головы по указателю
python[pythonStart].x = playerX;
python[pythonStart].y = playerY;
// Рисуем по новым координатам голову
mvprintw(playerY, playerX, "@");
}
// Жуткая процедура рисования окошка с Game Over
void DrawGameOver()
{
int winWidth = termWidth / 2 + 10;
int winHeight = 7;
int winX = (termWidth - winWidth) / 2;
int winY = (termHeight - winHeight) / 2;
WINDOW * gameOverWin = newwin(winHeight, winWidth, winY, winX);
box(gameOverWin, 0, 0);
std::string str1 = "GAME OVER!";
std::string str2 = "Score: " + std::to_string(score);
mvwprintw(gameOverWin, 2, (winWidth - (int)str1.length()) / 2, str1.c_str());
mvwprintw(gameOverWin, 4, (winWidth - (int)str2.length()) / 2, str2.c_str());
wrefresh(gameOverWin);
notimeout(gameOverWin, TRUE);
wgetch(gameOverWin);
}
// Здесь мы крутимся, пока игра идет
void GameLoop()
{
while (run)
{
// Обрабатываем ввод
Input();
if (!pause)
{
// Обрабатываем логику
Logic();
}
}
// Сюда мы выйдем, если игра так или иначе кончится
// Если Game over - рисуем плашку с очками
if (gameOver)
DrawGameOver();
}
// Выбрасываем новую еду
void DropFruit()
{
// Новые координаты в переменные
fruitX = (rand() % (termWidth - 2)) + 1;
fruitY = (rand() % (termHeight - 2)) + 1;
// и нарисовать
mvprintw(fruitY, fruitX, "#");
}
// Главная функция - просто вызывает все предыдущие последовательно
int main(int argc, const char * argv[]) {
InitGame();
getch();
PrintScore();
GameLoop();
EndGame();
return 0;
}