342 lines
9.7 KiB
C
342 lines
9.7 KiB
C
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <ncurses.h>
|
||
|
||
// Пока оно 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;
|
||
};
|
||
|
||
struct 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 = (struct coord*)malloc(maxPythonSize * sizeof(struct coord));
|
||
if (python == NULL) {
|
||
// Обработка ошибки выделения памяти
|
||
printf("Memory allocation failed\n");
|
||
exit(1);
|
||
}
|
||
|
||
// Задаем начальные координаты
|
||
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);
|
||
char str1[] = "GAME OVER!";
|
||
char str2[50];
|
||
sprintf(str2, "Score: %d", score);
|
||
mvwprintw(gameOverWin, 2, (winWidth - (int)strlen(str1)) / 2, str1);
|
||
mvwprintw(gameOverWin, 4, (winWidth - (int)strlen(str2)) / 2, str2);
|
||
wrefresh(gameOverWin);
|
||
notimeout(gameOverWin, TRUE);
|
||
wgetch(gameOverWin);
|
||
}
|
||
|
||
// Здесь мы крутимся, пока игра идет
|
||
void GameLoop()
|
||
{
|
||
while (run)
|
||
{
|
||
// Обрабатываем ввод
|
||
Input();
|
||
|
||
if (!pause)
|
||
{
|
||
// Обрабатываем логику
|
||
Logic();
|
||
}
|
||
}
|
||
|
||
// Сюда мы выйдем, если игра так или иначе кончится
|
||
// Если Game over - рисуем плашку с очками
|
||
if (gameOver)
|
||
DrawGameOver();
|
||
}
|
||
|
||
// Выбрасываем новую еду
|
||
void DropFruit()
|
||
{
|
||
// Делаем флаг про то, что координаты не пересекаются с питоном
|
||
bool goodCoords = true;
|
||
|
||
// Крутимся до тех пор, пока флаг не будет true
|
||
// (Он сразу true, но если координаты попадут на змею, он станет false
|
||
// и все уйдет на второй круг. А если не попадут - не уйдет.)
|
||
do
|
||
{
|
||
// Новые координаты в переменные
|
||
fruitX = (rand() % (termWidth - 2)) + 1;
|
||
fruitY = (rand() % (termHeight - 2)) + 1;
|
||
|
||
// Тут мы проходим по куску массива с телом между началом и концом
|
||
// чтобы выяснить, не попадает ли новая еда поперек змеи.
|
||
for (int i = 0; i < pythonEnd; i++)
|
||
{
|
||
if (fruitX == python[i].x && fruitY == python[i].y)
|
||
{
|
||
// Если попали - сбрасываем флаг и выходим из цикла.
|
||
goodCoords = false;
|
||
break;
|
||
}
|
||
}
|
||
} while (!goodCoords);
|
||
|
||
// и нарисовать
|
||
mvprintw(fruitY, fruitX, "#");
|
||
}
|
||
|
||
// Главная функция - просто вызывает все предыдущие последовательно
|
||
int main(int argc, const char * argv[]) {
|
||
InitGame();
|
||
|
||
getch();
|
||
|
||
PrintScore();
|
||
|
||
GameLoop();
|
||
|
||
EndGame();
|
||
return 0;
|
||
}
|