Files
NMPython/main.c

342 lines
9.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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;
}