Консольная игра "Змейка"
Автор - не я, изначально взято с itblogs. Я немного причесал код, убрал переключения цвета фона и добавил код для Linux/OSX.
В игре, ясное дело, ничего особенного, просто понравилась она мне. К тому же попробовал ascii.io для записи asciicast’a, так что геймплей можно заценить - http://ascii.io/a/1715
Исходник есть на GitHub’е, но можно просто скопировать снизу.
// Originally taken from http://itblogs.org/c-konsolnaya-zmejka/.
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#ifdef WINDOWS
// Библиотека, нужна для использования функции Sleep().
#include <windows.h>
// Библиотека, нужна для использования функций kbhit() и getch().
#include <conio.h>
#else
#include <unistd.h>
#include <termios.h>
#include <sys/select.h>
#define STDIN_FILENO 0
#define NB_DISABLE 0
#define NB_ENABLE 1
#define Sleep(x) usleep(x*1000)
int kbhit() {
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}
void nonblock(int state) {
struct termios ttystate;
// Get the terminal state.
tcgetattr(STDIN_FILENO, &ttystate);
if (state == NB_ENABLE) {
// Turn off canonical mode.
ttystate.c_lflag &= ~ICANON;
// Minimum of number input read.
ttystate.c_cc[VMIN] = 1;
} else if (state == NB_DISABLE) {
// Turn on canonical mode.
ttystate.c_lflag |= ICANON;
}
// Set the terminal attributes.
tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
}
int getch() {
return fgetc(stdin);
}
#endif
// snake_size - размер змейки.
// change_x, change_y - в какую сторону движется змейка.
// coordinates_x[1000], coordinates_y[1000] - массивы, хранящие координаты
// частей тела змейки.
// Kоординаты головы змейки хранятся в coordinates_x[1], coordinates_y[1].
// food_x, food_y - координаты еды.
int snake_size, change_x, change_y, coordinates_x[1000], coordinates_y[1000];
int food_x = -1, food_y = -1;
// symbol - хранит в себе ASCII код нажатой клавиши.
// a[1000][1000] - наша таблица, в которой происходит вся игра.
char symbol, a[1000][1000];
// Константы:
// N - размер таблицы, а именно высота.
// M - ширина таблицы.
// INTERVAL - интервал в миллисекундах, через каждый этот промежуток
// времени змейка будет передвигаться.
const int N = 13, M = 17, INTERVAL = 200;
// функция, считывающая нажатую клавишу.
void change_direction() {
// Cчитываем нажатую клавишу с помощью функции getch().
symbol = getch();
switch (symbol) {
// Управление змейкой у нас через wasd.
case 'w': if (change_x != 1 || change_y != 0) {
change_x = -1; change_y = 0;
}
break;
case 'a': if (change_x != 0 || change_y != 1) {
change_x = 0; change_y = -1;
}
break;
case 's': if (change_x != -1 || change_y != 0) {
change_x = 1; change_y = 0;
}
break;
case 'd': if (change_x != 0 || change_y != -1) {
change_x = 0; change_y = 1;
}
break;
#ifndef WINDOWS
case 'q': nonblock(NB_DISABLE); std::exit(0);
#endif
default: break;
}
}
// функция для вывода таблицы
void show_table() {
// Очищаем консоль.
#ifdef WINDOWS
system("cls");
#else
system("clear");
#endif
// Выводим таблицу и края.
for (int i = 0; i <= N + 1; ++i)
for (int j = 0; j <= M + 1; ++j)
std::cout
<< (i == 0 || j == 0 || i == N + 1 || j == M + 1 ?
'#' : a[i][j])
<< (j <= M ? "" : "\n");
}
// Очищаем координаты, в которых располагалась змейка.
void clear_snake_on_table() {
for (int i = 1; i <= snake_size; ++i)
a[coordinates_x[i]][coordinates_y[i]] = ' ';
}
// Красим координаты змейки.
void show_snake_on_table() {
// Изменяем тип головы.
if (change_x == 1 && change_y == 0)
a[coordinates_x[1]][coordinates_y[1]] = 'v';
if (change_x == -1 && change_y == 0)
a[coordinates_x[1]][coordinates_y[1]] = '^';
if (change_x == 0 && change_y == 1)
a[coordinates_x[1]][coordinates_y[1]] = '>';
if (change_x == 0 && change_y == -1)
a[coordinates_x[1]][coordinates_y[1]] = '<';
// Красим змейку.
for (int i = 2; i <= snake_size; ++i)
a[coordinates_x[i]][coordinates_y[i]] = '@';
}
// Проверяем, съела ли змейка саму себя.
bool game_over() {
for (int i = 2; i <= snake_size; ++i)
// Eсли координаты головы змейки равны координате какой-либо части тела
// змейки, то змейка съела саму себя.
if (coordinates_x[1] == coordinates_x[i] &&
coordinates_y[1] == coordinates_y[i])
return true;
// Если все координаты различны, то все в порядке - играем дальше.
return false;
}
// Проверяем, не вышла ли змейка за поле, если да то возвращаем ее обратно.
void check_coordinates() {
if (coordinates_x[1] > N) coordinates_x[1] = 1;
if (coordinates_x[1] < 1) coordinates_x[1] = N;
if (coordinates_y[1] > M) coordinates_y[1] = 1;
if (coordinates_y[1] < 1) coordinates_y[1] = M;
}
// функция следующего хода, в которой наша змейка сдвигается в сторону
// на 1 ячейку.
void next_step() {
// Чистим таблицу от змейки.
clear_snake_on_table();
// Передвигаем тело змейки.
for (int i = snake_size; i >= 2; --i) {
coordinates_x[i] = coordinates_x[i - 1];
coordinates_y[i] = coordinates_y[i - 1];
}
// Передвигаем голову змейки.
coordinates_x[1] += change_x;
coordinates_y[1] += change_y;
// Проверяем в порядке ли координаты.
check_coordinates();
// Если голова змейки там же где и еда, то увеличиваем размер змейки
// и очищаем координаты змейки.
if (coordinates_x[1] == food_x && coordinates_y[1] == food_y) {
snake_size++;
food_x = -1;
food_y = -1;
}
// Рисуем змейку.
show_snake_on_table();
// Если змея укусила себя.
if (game_over()) {
// Cообщаем всю правду о игроке.
std::cout << "You're looser! \n";
// Приостанавливаем игру.
#ifdef WINDOWS
system("pause");
#endif
// Завершаем программу.
std::exit(0);
}
}
// функция проверки на наличие еды на карте.
bool food_check() {
// Если координаты еды неопределенны, то возвращаем true.
if (food_x == -1 && food_y == -1) return false;
// В остальных случаях false.
return true;
}
// функция добавления еды на карту.
void place_food() {
std::srand(std::time(NULL));
// Cтавим в рандомное место еду.
for (int i = 1; i <= 9; ++i) {
int x = std::rand(), y = std::rand();
if (x < 0) x *= -1;
if (y < 0) y *= -1;
x %= (N + 1);
y %= (M + 1);
if (x == 0) ++x;
if (y == 0) ++y;
if (a[x][y] != '@' && a[x][y] != 'v' && a[x][y] != '^' &&
a[x][y] != '<' && a[x][y] != '>') {
food_x = x;
food_y = y;
a[x][y] = '+';
return;
}
}
}
// Начальные установки.
void standart_settings() {
// Размер змеи - 2.
snake_size = 2;
// Змейка занимает две клетки вправо от координаты {1,1}.
coordinates_x[1] = 1;
coordinates_y[1] = 2;
coordinates_x[2] = 1;
coordinates_y[2] = 1;
// Змейка движется вправо.
change_x = 0;
change_y = 1;
}
int main() {
// Задаем стандартные настройки.
standart_settings();
#ifndef WINDOWS
std::memset(a, ' ', sizeof(a));
nonblock(NB_ENABLE);
#endif
// Бесконечный цикл.
while (true) {
// Если нажата клавиша, обрабатываем нажатую клавишу.
if (kbhit() != 0)
change_direction();
// Двигаем змейку.
next_step();
// Если нет еды, то ставим ее.
if (!food_check())
place_food();
// Рисуем змейку.
show_table();
// "Усыпляем" программу на заданный интервал.
Sleep(INTERVAL);
}
}
P.S.
Если у кого есть всякие консольные примочки типа этой - делитесь, не стесняйтесь.
Disclaimer